- define additional shell paths for CSIDL_... constants
[wine] / dlls / comctl32 / datetime.c
1 /*
2  * Date and time picker control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1999, 2000 Alex Priem <alexp@sci.kun.nl>
6  * Copyright 2000 Chris Morgan <cmorgan@wpi.edu>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * TODO:
23  *   - All messages.
24  *   - All notifications.
25  *
26  */
27
28 #include <math.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winuser.h"
37 #include "winnls.h"
38 #include "commctrl.h"
39 #include "comctl32.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(datetime);
43
44 typedef struct
45 {
46         HWND hMonthCal;
47         HWND hwndNotify;
48         HWND hUpdown;
49         SYSTEMTIME date;
50         BOOL dateValid;
51         HWND hwndCheckbut;
52         RECT rcClient; /* rect around the edge of the window */
53         RECT rcDraw; /* rect inside of the border */
54         RECT checkbox;  /* checkbox allowing the control to be enabled/disabled */
55         RECT calbutton; /* button that toggles the dropdown of the monthcal control */
56         BOOL bCalDepressed; /* TRUE = cal button is depressed */
57         int  select;
58         HFONT hFont;
59         int nrFieldsAllocated;
60         int nrFields;
61         int haveFocus;
62         int *fieldspec;
63         RECT *fieldRect;
64         int  *buflen;
65         char textbuf[256];
66         POINT monthcal_pos;
67 } DATETIME_INFO, *LPDATETIME_INFO;
68
69 /* in monthcal.c */
70 extern int MONTHCAL_MonthLength(int month, int year);
71
72 /* this list of defines is closely related to `allowedformatchars' defined
73  * in datetime.c; the high nibble indicates the `base type' of the format
74  * specifier.
75  * Do not change without first reading DATETIME_UseFormat.
76  *
77  */
78
79 #define DT_END_FORMAT      0
80 #define ONEDIGITDAY     0x01
81 #define TWODIGITDAY     0x02
82 #define THREECHARDAY    0x03
83 #define FULLDAY         0x04
84 #define ONEDIGIT12HOUR  0x11
85 #define TWODIGIT12HOUR  0x12
86 #define ONEDIGIT24HOUR  0x21
87 #define TWODIGIT24HOUR  0x22
88 #define ONEDIGITMINUTE  0x31
89 #define TWODIGITMINUTE  0x32
90 #define ONEDIGITMONTH   0x41
91 #define TWODIGITMONTH   0x42
92 #define THREECHARMONTH  0x43
93 #define FULLMONTH       0x44
94 #define ONEDIGITSECOND  0x51
95 #define TWODIGITSECOND  0x52
96 #define ONELETTERAMPM   0x61
97 #define TWOLETTERAMPM   0x62
98 #define ONEDIGITYEAR    0x71
99 #define TWODIGITYEAR    0x72
100 #define INVALIDFULLYEAR 0x73      /* FIXME - yyy is not valid - we'll treat it as yyyy */
101 #define FULLYEAR        0x74
102 #define FORMATCALLBACK  0x81      /* -> maximum of 0x80 callbacks possible */
103 #define FORMATCALLMASK  0x80
104 #define DT_STRING       0x0100
105
106 #define DTHT_DATEFIELD  0xff      /* for hit-testing */
107
108 #define DTHT_NONE     0
109 #define DTHT_CHECKBOX 0x200     /* these should end at '00' , to make */
110 #define DTHT_MCPOPUP  0x300     /* & DTHT_DATEFIELD 0 when DATETIME_KeyDown */
111 #define DTHT_GOTFOCUS 0x400     /* tests for date-fields */
112
113 #define DATETIME_GetInfoPtr(hwnd) ((DATETIME_INFO *)GetWindowLongA (hwnd, 0))
114
115 static BOOL DATETIME_SendSimpleNotify (HWND hwnd, UINT code);
116 static BOOL DATETIME_SendDateTimeChangeNotify (HWND hwnd);
117 extern void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to);
118 static const char *allowedformatchars = {"dhHmMstyX'"};
119 static const int maxrepetition [] = {4,2,2,2,4,2,2,4,-1,-1};
120
121
122 static LRESULT
123 DATETIME_GetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
124 {
125   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
126   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
127   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
128
129   TRACE("%04x %08lx\n",wParam,lParam);
130   if (!lParam) return GDT_NONE;
131
132   if ((dwStyle & DTS_SHOWNONE) &&
133        (SendMessageA (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0) == BST_UNCHECKED))
134         return GDT_NONE;
135
136   MONTHCAL_CopyTime (&infoPtr->date, lprgSysTimeArray);
137
138   return GDT_VALID;
139 }
140
141
142 static LRESULT
143 DATETIME_SetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
144 {
145   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
146   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
147
148   TRACE("%p %04x %08lx\n",hwnd, wParam, lParam);
149   if (!lParam) return 0;
150
151   TRACE("%04d/%02d/%02d %02d:%02d:%02d)\n",
152         lprgSysTimeArray->wYear, lprgSysTimeArray->wMonth, lprgSysTimeArray->wDay,
153         lprgSysTimeArray->wHour, lprgSysTimeArray->wMinute, lprgSysTimeArray->wSecond);
154
155   if (wParam==GDT_VALID) {
156       infoPtr->dateValid = TRUE;
157       MONTHCAL_CopyTime (lprgSysTimeArray, &infoPtr->date);
158       SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, BST_CHECKED, 0);
159   } else if (wParam==GDT_NONE) {
160       infoPtr->dateValid = FALSE;
161       SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, BST_UNCHECKED, 0);
162   }
163   InvalidateRect(hwnd, NULL, TRUE);
164   return 1;
165 }
166
167
168 static LRESULT
169 DATETIME_GetRange (HWND hwnd, LPARAM lParam )
170 {
171   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
172   LRESULT ret;
173
174   TRACE("%08lx\n", lParam);
175   ret =  SendMessageA (infoPtr->hMonthCal, MCM_GETRANGE, 0, lParam);
176   if (!ret) ret = 1; /* bug emulation... */
177   return ret;
178 }
179
180 static LRESULT
181 DATETIME_SetRange (HWND hwnd, WPARAM wParam, LPARAM lParam )
182 {
183   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
184
185   TRACE("%04x %08lx\n",wParam,lParam);
186
187   return SendMessageA (infoPtr->hMonthCal, MCM_SETRANGE, wParam, lParam);
188 }
189
190 static LRESULT
191 DATETIME_GetMonthCalColor (HWND hwnd, WPARAM wParam)
192 {
193   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
194
195   TRACE("%04x\n",wParam);
196   return SendMessageA (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
197 }
198
199
200 static LRESULT
201 DATETIME_SetMonthCalColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
202 {
203   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
204
205   TRACE("%04x %08lx\n",wParam,lParam);
206   return SendMessageA (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
207 }
208
209
210 /* FIXME: need to get way to force font into monthcal structure */
211 static LRESULT
212 DATETIME_GetMonthCal (HWND hwnd)
213 {
214   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
215
216   TRACE("\n");
217   return (LRESULT)infoPtr->hMonthCal;
218 }
219
220
221
222 /* FIXME: need to get way to force font into monthcal structure */
223
224 static LRESULT
225 DATETIME_GetMonthCalFont (HWND hwnd)
226 {
227
228   TRACE("\n");
229   return 0;
230 }
231
232
233 static LRESULT
234 DATETIME_SetMonthCalFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
235 {
236
237   TRACE("%04x %08lx\n",wParam,lParam);
238   return 0;
239 }
240
241
242 /*
243    Split up a formattxt in actions.
244    See ms documentation for the meaning of the letter codes/'specifiers'.
245
246    Notes:
247    *'dddddd' is handled as 'dddd' plus 'dd'.
248    *unrecognized formats are strings (here given the type DT_STRING;
249    start of the string is encoded in lower bits of DT_STRING.
250    Therefore, 'string' ends finally up as '<show seconds>tring'.
251
252  */
253
254
255 static void
256 DATETIME_UseFormat (DATETIME_INFO *infoPtr, const char *formattxt)
257 {
258  int i,j,k,len;
259  int *nrFields=& infoPtr->nrFields;
260
261  TRACE ("%s\n",formattxt);
262
263
264  *nrFields=0;
265  infoPtr->fieldspec[*nrFields]=0;
266  len=strlen(allowedformatchars);
267  k=0;
268
269  for (i=0; i<strlen (formattxt); i++)  {
270         TRACE ("\n%d %c:",i, formattxt[i]);
271         for (j=0; j<len; j++) {
272                 if (allowedformatchars[j]==formattxt[i]) {
273                         TRACE ("%c[%d,%x]",allowedformatchars[j], *nrFields,
274                                                          infoPtr->fieldspec[*nrFields]);
275                         if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
276                                 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
277                                 break;
278                         }
279                         if (infoPtr->fieldspec[*nrFields]>>4!=j) {
280                                 (*nrFields)++;
281                                 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
282                                 break;
283                         }
284                         if ((infoPtr->fieldspec[*nrFields] & 0x0f)==maxrepetition[j]) {
285                                 (*nrFields)++;
286                                 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
287                                 break;
288                         }
289                         infoPtr->fieldspec[*nrFields]++;
290                         break;
291                 }   /* if allowedformatchar */
292          } /* for j */
293
294
295                         /* char is not a specifier: handle char like a string */
296         if (j==len) {
297                 if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
298                         infoPtr->fieldspec[*nrFields]=DT_STRING+k;
299                         infoPtr->buflen[*nrFields]=0;
300         } else
301                 if ((infoPtr->fieldspec[*nrFields] & DT_STRING)!=DT_STRING)  {
302                         (*nrFields)++;
303                         infoPtr->fieldspec[*nrFields]=DT_STRING+k;
304                         infoPtr->buflen[*nrFields]=0;
305                 }
306                 infoPtr->textbuf[k]=formattxt[i];
307                 k++;
308                 infoPtr->buflen[*nrFields]++;
309         }   /* if j=len */
310
311         if (*nrFields==infoPtr->nrFieldsAllocated) {
312                 FIXME ("out of memory; should reallocate. crash ahead.\n");
313         }
314
315   } /* for i */
316
317   TRACE("\n");
318
319   if (infoPtr->fieldspec[*nrFields]!=0) (*nrFields)++;
320 }
321
322
323 static LRESULT
324 DATETIME_SetFormat (HWND hwnd, WPARAM wParam, LPARAM lParam)
325 {
326  DATETIME_INFO *infoPtr= DATETIME_GetInfoPtr (hwnd);
327  char format_buf[80];
328  DWORD format_item;
329
330  TRACE("%04x %08lx\n",wParam,lParam);
331  if (!lParam) {
332         DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
333
334         if (dwStyle & DTS_LONGDATEFORMAT)
335                 format_item=LOCALE_SLONGDATE;
336         else if (dwStyle & DTS_TIMEFORMAT)
337                 format_item=LOCALE_STIMEFORMAT;
338         else /* DTS_SHORTDATEFORMAT */
339                 format_item=LOCALE_SSHORTDATE;
340         GetLocaleInfoA( GetSystemDefaultLCID(), format_item,format_buf,sizeof(format_buf));
341         DATETIME_UseFormat (infoPtr, format_buf);
342  }
343  else
344         DATETIME_UseFormat (infoPtr, (char *) lParam);
345
346  return infoPtr->nrFields;
347 }
348
349
350 static LRESULT
351 DATETIME_SetFormatW (HWND hwnd, WPARAM wParam, LPARAM lParam)
352
353 {
354  TRACE("%04x %08lx\n",wParam,lParam);
355  if (lParam) {
356         LPSTR buf;
357         int retval;
358         int len = WideCharToMultiByte( CP_ACP, 0, (LPWSTR)lParam, -1, NULL, 0, NULL, NULL );
359
360         buf = (LPSTR) Alloc (len);
361         WideCharToMultiByte( CP_ACP, 0, (LPWSTR)lParam, -1, buf, len, NULL, NULL );
362         retval=DATETIME_SetFormat (hwnd, 0, (LPARAM) buf);
363         Free (buf);
364         return retval;
365  }
366  else
367         return DATETIME_SetFormat (hwnd, 0, 0);
368
369 }
370
371
372 static void
373 DATETIME_ReturnTxt (DATETIME_INFO *infoPtr, int count, char *result, int resultSize)
374 {
375  SYSTEMTIME date = infoPtr->date;
376  int spec;
377  char buffer[80];
378
379  *result=0;
380  TRACE ("%d,%d\n", infoPtr->nrFields, count);
381  if ((count>infoPtr->nrFields) || (count<0)) {
382         WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count);
383         return;
384  }
385
386  if (!infoPtr->fieldspec) return;
387
388  spec=infoPtr->fieldspec[count];
389  if (spec & DT_STRING) {
390         int txtlen=infoPtr->buflen[count];
391
392         if (txtlen > resultSize)
393             txtlen = resultSize - 1;
394         memcpy (result, infoPtr->textbuf + (spec &~ DT_STRING), txtlen);
395         result[txtlen]=0;
396         TRACE ("arg%d=%x->[%s]\n",count,infoPtr->fieldspec[count],result);
397         return;
398  }
399
400
401  switch (spec) {
402         case DT_END_FORMAT:
403                 *result=0;
404                 break;
405         case ONEDIGITDAY:
406                 sprintf (result,"%d",date.wDay);
407                 break;
408         case TWODIGITDAY:
409                 sprintf (result,"%.2d",date.wDay);
410                 break;
411         case THREECHARDAY:
412                 GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1+(date.wDayOfWeek+6)%7,
413                                 result,4);
414                 /*sprintf (result,"%.3s",days[date.wDayOfWeek]);*/
415                 break;
416         case FULLDAY:
417                 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SDAYNAME1+ (date.wDayOfWeek+6)%7,
418                                result, resultSize);
419                 break;
420         case ONEDIGIT12HOUR:
421                 if (date.wHour>12)
422                         sprintf (result,"%d",date.wHour-12);
423                 else
424                         sprintf (result,"%d",date.wHour);
425                 break;
426         case TWODIGIT12HOUR:
427                 if (date.wHour>12)
428                         sprintf (result,"%.2d",date.wHour-12);
429                 else
430                         sprintf (result,"%.2d",date.wHour);
431                 break;
432         case ONEDIGIT24HOUR:
433                 sprintf (result,"%d",date.wHour);
434                 break;
435         case TWODIGIT24HOUR:
436                 sprintf (result,"%.2d",date.wHour);
437                 break;
438         case ONEDIGITSECOND:
439                 sprintf (result,"%d",date.wSecond);
440                 break;
441         case TWODIGITSECOND:
442                 sprintf (result,"%.2d",date.wSecond);
443                 break;
444         case ONEDIGITMINUTE:
445                 sprintf (result,"%d",date.wMinute);
446                 break;
447         case TWODIGITMINUTE:
448                 sprintf (result,"%.2d",date.wMinute);
449                 break;
450         case ONEDIGITMONTH:
451                 sprintf (result,"%d",date.wMonth);
452                 break;
453         case TWODIGITMONTH:
454                 sprintf (result,"%.2d",date.wMonth);
455                 break;
456         case THREECHARMONTH:
457                 GetLocaleInfoA( GetSystemDefaultLCID(),LOCALE_SMONTHNAME1+date.wMonth -1,
458                                buffer,sizeof(buffer));
459                 sprintf (result,"%.3s",buffer);
460                 break;
461         case FULLMONTH:
462                 GetLocaleInfoA( GetSystemDefaultLCID(),LOCALE_SMONTHNAME1+date.wMonth -1,
463                                result, resultSize);
464                 break;
465         case ONELETTERAMPM:
466                 if (date.wHour<12)
467                         strcpy (result,"A");
468                 else
469                         strcpy (result,"P");
470                 break;
471         case TWOLETTERAMPM:
472                 if (date.wHour<12)
473                         strcpy (result,"AM");
474                 else
475                         strcpy (result,"PM");
476                 break;
477         case FORMATCALLBACK:
478                 FIXME ("Not implemented\n");
479                 strcpy (result,"xxx");
480                 break;
481         case ONEDIGITYEAR:
482                 sprintf (result,"%d",date.wYear-10* (int) floor(date.wYear/10));
483                 break;
484         case TWODIGITYEAR:
485                 sprintf (result,"%.2d",date.wYear-100* (int) floor(date.wYear/100));
486                 break;
487         case INVALIDFULLYEAR:
488         case FULLYEAR:
489                 sprintf (result,"%d",date.wYear);
490                 break;
491     }
492
493         TRACE ("arg%d=%x->[%s]\n",count,infoPtr->fieldspec[count],result);
494 }
495
496
497 static void
498 DATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number)
499 {
500  SYSTEMTIME *date = &infoPtr->date;
501  int spec;
502
503  TRACE ("%d\n",number);
504  if ((number>infoPtr->nrFields) || (number<0)) return;
505
506  spec=infoPtr->fieldspec[number];
507  if ((spec & DTHT_DATEFIELD)==0) return;
508
509  switch (spec) {
510         case ONEDIGITDAY:
511         case TWODIGITDAY:
512         case THREECHARDAY:
513         case FULLDAY:
514                 date->wDay++;
515                 if (date->wDay>MONTHCAL_MonthLength(date->wMonth,date->wYear))
516                   date->wDay=1;
517                 break;
518         case ONEDIGIT12HOUR:
519         case TWODIGIT12HOUR:
520         case ONEDIGIT24HOUR:
521         case TWODIGIT24HOUR:
522                 date->wHour++;
523                 if (date->wHour>23) date->wHour=0;
524                 break;
525         case ONEDIGITSECOND:
526         case TWODIGITSECOND:
527                 date->wSecond++;
528                 if (date->wSecond>59) date->wSecond=0;
529                 break;
530         case ONEDIGITMINUTE:
531         case TWODIGITMINUTE:
532                 date->wMinute++;
533                 if (date->wMinute>59) date->wMinute=0;
534                 break;
535         case ONEDIGITMONTH:
536         case TWODIGITMONTH:
537         case THREECHARMONTH:
538         case FULLMONTH:
539                 date->wMonth++;
540                 if (date->wMonth>12) date->wMonth=1;
541                 if (date->wDay>MONTHCAL_MonthLength(date->wMonth,date->wYear))
542                         date->wDay=MONTHCAL_MonthLength(date->wMonth,date->wYear);
543                 break;
544         case ONELETTERAMPM:
545         case TWOLETTERAMPM:
546                 date->wHour+=12;
547                 if (date->wHour>23) date->wHour-=24;
548                 break;
549         case FORMATCALLBACK:
550                 FIXME ("Not implemented\n");
551                 break;
552         case ONEDIGITYEAR:
553         case TWODIGITYEAR:
554         case FULLYEAR:
555                 date->wYear++;
556                 break;
557         }
558
559 }
560
561
562 static void
563 DATETIME_DecreaseField (DATETIME_INFO *infoPtr, int number)
564 {
565  SYSTEMTIME *date = & infoPtr->date;
566  int spec;
567
568  TRACE ("%d\n",number);
569  if ((number>infoPtr->nrFields) || (number<0)) return;
570
571  spec = infoPtr->fieldspec[number];
572  if ((spec & DTHT_DATEFIELD)==0) return;
573
574  TRACE ("%x\n",spec);
575
576  switch (spec) {
577         case ONEDIGITDAY:
578         case TWODIGITDAY:
579         case THREECHARDAY:
580         case FULLDAY:
581                 date->wDay--;
582                 if (date->wDay<1)
583                   date->wDay=MONTHCAL_MonthLength(date->wMonth,date->wYear);
584                 break;
585         case ONEDIGIT12HOUR:
586         case TWODIGIT12HOUR:
587         case ONEDIGIT24HOUR:
588         case TWODIGIT24HOUR:
589                 if (date->wHour)
590                         date->wHour--;
591                 else
592                         date->wHour=23;
593                 break;
594         case ONEDIGITSECOND:
595         case TWODIGITSECOND:
596                 if (date->wHour)
597                         date->wSecond--;
598                 else
599                         date->wHour=59;
600                 break;
601         case ONEDIGITMINUTE:
602         case TWODIGITMINUTE:
603                 if (date->wMinute)
604                         date->wMinute--;
605                 else
606                         date->wMinute=59;
607                 break;
608         case ONEDIGITMONTH:
609         case TWODIGITMONTH:
610         case THREECHARMONTH:
611         case FULLMONTH:
612                 if (date->wMonth>1)
613                         date->wMonth--;
614                 else
615                         date->wMonth=12;
616                 if (date->wDay>MONTHCAL_MonthLength(date->wMonth,date->wYear))
617                         date->wDay=MONTHCAL_MonthLength(date->wMonth,date->wYear);
618                 break;
619         case ONELETTERAMPM:
620         case TWOLETTERAMPM:
621                 if (date->wHour<12)
622                         date->wHour+=12;
623                 else
624                         date->wHour-=12;
625                 break;
626         case FORMATCALLBACK:
627                 FIXME ("Not implemented\n");
628                 break;
629         case ONEDIGITYEAR:
630         case TWODIGITYEAR:
631         case FULLYEAR:
632                 date->wYear--;
633                 break;
634         }
635
636 }
637
638
639 static void
640 DATETIME_ResetFieldDown (DATETIME_INFO *infoPtr, int number)
641 {
642  SYSTEMTIME *date = &infoPtr->date;
643  int spec;
644
645  TRACE ("%d\n",number);
646  if ((number>infoPtr->nrFields) || (number<0)) return;
647
648  spec = infoPtr->fieldspec[number];
649  if ((spec & DTHT_DATEFIELD)==0) return;
650
651
652  switch (spec) {
653         case ONEDIGITDAY:
654         case TWODIGITDAY:
655         case THREECHARDAY:
656         case FULLDAY:
657                 date->wDay = 1;
658                 break;
659         case ONEDIGIT12HOUR:
660         case TWODIGIT12HOUR:
661         case ONEDIGIT24HOUR:
662         case TWODIGIT24HOUR:
663         case ONELETTERAMPM:
664         case TWOLETTERAMPM:
665                 date->wHour = 0;
666                 break;
667         case ONEDIGITSECOND:
668         case TWODIGITSECOND:
669                 date->wSecond = 0;
670                 break;
671         case ONEDIGITMINUTE:
672         case TWODIGITMINUTE:
673                 date->wMinute = 0;
674                 break;
675         case ONEDIGITMONTH:
676         case TWODIGITMONTH:
677         case THREECHARMONTH:
678         case FULLMONTH:
679                 date->wMonth = 1;
680         case FORMATCALLBACK:
681                 FIXME ("Not implemented\n");
682                 break;
683         case ONEDIGITYEAR:
684         case TWODIGITYEAR:
685         /* FYI: On 1752/9/14 the calendar changed and England and the
686          * American colonies changed to the Gregorian calendar. This change
687          * involved having September 14th follow September 2nd. So no date
688          * algorithm works before that date.
689          */
690         case FULLYEAR:
691                 date->wSecond = 0;
692                 date->wMinute = 0;
693                 date->wHour = 0;
694                 date->wDay = 14;                /* overactive ms-programmers..*/
695                 date->wMonth = 9;
696                 date->wYear = 1752;
697                 break;
698         }
699
700 }
701
702
703 static void
704 DATETIME_ResetFieldUp (DATETIME_INFO *infoPtr, int number)
705 {
706  SYSTEMTIME *date = & infoPtr->date;
707  int spec;
708
709  TRACE("%d \n",number);
710  if ((number>infoPtr->nrFields) || (number<0)) return;
711
712  spec=infoPtr->fieldspec[number];
713  if ((spec & DTHT_DATEFIELD)==0) return;
714
715  switch (spec) {
716         case ONEDIGITDAY:
717         case TWODIGITDAY:
718         case THREECHARDAY:
719         case FULLDAY:
720                 date->wDay=MONTHCAL_MonthLength(date->wMonth,date->wYear);
721                 break;
722         case ONEDIGIT12HOUR:
723         case TWODIGIT12HOUR:
724         case ONEDIGIT24HOUR:
725         case TWODIGIT24HOUR:
726         case ONELETTERAMPM:
727         case TWOLETTERAMPM:
728                 date->wHour=23;
729                 break;
730         case ONEDIGITSECOND:
731         case TWODIGITSECOND:
732                 date->wSecond=59;
733                 break;
734         case ONEDIGITMINUTE:
735         case TWODIGITMINUTE:
736                 date->wMinute=59;
737                 break;
738         case ONEDIGITMONTH:
739         case TWODIGITMONTH:
740         case THREECHARMONTH:
741         case FULLMONTH:
742                 date->wMonth=12;
743         case FORMATCALLBACK:
744                 FIXME ("Not implemented\n");
745                 break;
746         case ONEDIGITYEAR:
747         case TWODIGITYEAR:
748         case FULLYEAR:
749                 date->wYear=9999;    /* Y10K problem? naaah. */
750                 break;
751         }
752
753 }
754
755
756 static void DATETIME_Refresh (HWND hwnd, HDC hdc)
757
758 {
759   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
760   int i,prevright;
761   RECT *field;
762   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
763   RECT *rcDraw = &infoPtr->rcDraw;
764   RECT *rcClient = &infoPtr->rcClient;
765   RECT *calbutton = &infoPtr->calbutton;
766   RECT *checkbox = &infoPtr->checkbox;
767   HBRUSH hbr;
768   SIZE size;
769   COLORREF oldBk, oldTextColor;
770
771   /* draw control edge */
772   TRACE("\n");
773   hbr = CreateSolidBrush(RGB(255, 255, 255));
774   FillRect(hdc, rcClient, hbr);
775   DrawEdge(hdc, rcClient, EDGE_SUNKEN, BF_RECT);
776   DeleteObject(hbr);
777
778   if (infoPtr->dateValid) {
779     char txt[80];
780     HFONT oldFont;
781     oldFont = SelectObject (hdc, infoPtr->hFont);
782
783     DATETIME_ReturnTxt (infoPtr, 0, txt, sizeof(txt));
784     GetTextExtentPoint32A (hdc, txt, strlen (txt), &size);
785     rcDraw->bottom = size.cy+2;
786
787     if (dwStyle & DTS_SHOWNONE) checkbox->right = 18;
788
789     prevright = checkbox->right;
790
791     for (i=0; i<infoPtr->nrFields; i++) {
792       DATETIME_ReturnTxt (infoPtr, i, txt, sizeof(txt));
793       GetTextExtentPoint32A (hdc, txt, strlen (txt), &size);
794       field = & infoPtr->fieldRect[i];
795       field->left  = prevright;
796       field->right = prevright+size.cx;
797       field->top   = rcDraw->top;
798       field->bottom = rcDraw->bottom;
799       prevright = field->right;
800
801       if ((infoPtr->haveFocus) && (i==infoPtr->select)) {
802         hbr = CreateSolidBrush (GetSysColor (COLOR_ACTIVECAPTION));
803         FillRect(hdc, field, hbr);
804         oldBk = SetBkColor (hdc, GetSysColor(COLOR_ACTIVECAPTION));
805         oldTextColor = SetTextColor (hdc, GetSysColor(COLOR_WINDOW));
806         DeleteObject (hbr);
807         DrawTextA ( hdc, txt, strlen(txt), field,
808               DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
809         SetBkColor (hdc, oldBk);
810                 SetTextColor (hdc, oldTextColor);
811       }
812       else
813         DrawTextA ( hdc, txt, strlen(txt), field,
814                          DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
815     }
816
817     SelectObject (hdc, oldFont);
818   }
819
820   if (!(dwStyle & DTS_UPDOWN)) {
821     DrawFrameControl(hdc, calbutton, DFC_SCROLL,
822         DFCS_SCROLLDOWN | (infoPtr->bCalDepressed ? DFCS_PUSHED : 0) |
823         (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
824   }
825 }
826
827
828 static LRESULT
829 DATETIME_HitTest (HWND hwnd, DATETIME_INFO *infoPtr, POINT pt)
830 {
831   int i, retval;
832
833   TRACE ("%ld, %ld\n",pt.x,pt.y);
834
835   retval = DTHT_NONE;
836   if (PtInRect (&infoPtr->calbutton, pt))
837     {retval = DTHT_MCPOPUP; TRACE("Hit in calbutton(DTHT_MCPOPUP)\n"); goto done; }
838   if (PtInRect (&infoPtr->checkbox, pt))
839     {retval = DTHT_CHECKBOX; TRACE("Hit in checkbox(DTHT_CHECKBOX)\n"); goto done; }
840
841   for (i=0; i<infoPtr->nrFields; i++) {
842     if (PtInRect (&infoPtr->fieldRect[i], pt)) {
843       retval = i;
844       TRACE("Hit in date text in field %d\n", i);
845       break;
846     }
847  }
848
849 done:
850   return retval;
851 }
852
853
854 static LRESULT
855 DATETIME_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
856 {
857   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
858   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
859   int old, new;
860   POINT pt;
861
862   TRACE ("\n");
863
864   old = infoPtr->select;
865   pt.x = (INT)LOWORD(lParam);
866   pt.y = (INT)HIWORD(lParam);
867
868   new = DATETIME_HitTest (hwnd, infoPtr, pt);
869
870   /* FIXME: might be conditions where we don't want to update infoPtr->select */
871   infoPtr->select = new;
872
873   if (infoPtr->select != old) {
874     infoPtr->haveFocus = DTHT_GOTFOCUS;
875    }
876
877   if (infoPtr->select == DTHT_MCPOPUP) {
878     /* FIXME: button actually is only depressed during dropdown of the */
879     /* calendar control and when the mouse is over the button window */
880     infoPtr->bCalDepressed = TRUE;
881
882     /* recalculate the position of the monthcal popup */
883     if(dwStyle & DTS_RIGHTALIGN)
884       infoPtr->monthcal_pos.x = infoPtr->rcClient.right - ((infoPtr->calbutton.right -
885                                 infoPtr->calbutton.left) + 145);
886     else
887       infoPtr->monthcal_pos.x = 8;
888
889     infoPtr->monthcal_pos.y = infoPtr->rcClient.bottom;
890     ClientToScreen (hwnd, &(infoPtr->monthcal_pos));
891     SetWindowPos(infoPtr->hMonthCal, 0, infoPtr->monthcal_pos.x,
892         infoPtr->monthcal_pos.y, 145, 150, 0);
893
894     if(IsWindowVisible(infoPtr->hMonthCal))
895         ShowWindow(infoPtr->hMonthCal, SW_HIDE);
896     else
897         ShowWindow(infoPtr->hMonthCal, SW_SHOW);
898
899     TRACE ("dt:%p mc:%p mc parent:%p, desktop:%p\n",
900            hwnd, infoPtr->hMonthCal, infoPtr->hwndNotify, GetDesktopWindow ());
901     DATETIME_SendSimpleNotify (hwnd, DTN_DROPDOWN);
902   }
903
904   InvalidateRect(hwnd, NULL, FALSE);
905
906   return 0;
907 }
908
909
910 static LRESULT
911 DATETIME_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
912 {
913   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
914
915   TRACE("\n");
916
917   if(infoPtr->bCalDepressed == TRUE) {
918     infoPtr->bCalDepressed = FALSE;
919     InvalidateRect(hwnd, &(infoPtr->calbutton), TRUE);
920   }
921
922   return 0;
923 }
924
925
926 static LRESULT
927 DATETIME_Paint (HWND hwnd, WPARAM wParam)
928 {
929     HDC hdc;
930     PAINTSTRUCT ps;
931
932     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
933     DATETIME_Refresh (hwnd, hdc);
934     if(!wParam)
935     EndPaint (hwnd, &ps);
936     return 0;
937 }
938
939
940 static LRESULT
941 DATETIME_Button_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
942 {
943     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr(hwnd);
944
945     switch(HIWORD(wParam)) {
946     case BN_CLICKED:
947     {
948         DWORD state = SendMessageA((HWND)lParam, BM_GETCHECK, 0, 0);
949         if(state == BST_CHECKED)
950             infoPtr->dateValid = TRUE;
951         else
952             infoPtr->dateValid = FALSE;
953         InvalidateRect(hwnd, NULL, TRUE);
954         return 0;
955     }
956     default:
957         return 0;
958     }
959 }
960           
961         
962         
963 static LRESULT
964 DATETIME_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
965 {
966     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr(hwnd);
967
968     TRACE("%08x %08lx\n", wParam, lParam);
969     TRACE("hwndbutton = %p\n", infoPtr->hwndCheckbut);
970     if(infoPtr->hwndCheckbut == (HWND)lParam)
971         return DATETIME_Button_Command(hwnd, wParam, lParam);
972     return 0;
973 }
974
975 static LRESULT
976 DATETIME_ParentNotify (HWND hwnd, WPARAM wParam, LPARAM lParam)
977 {
978  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
979  LPNMHDR lpnmh = (LPNMHDR) lParam;
980
981  TRACE ("%x,%lx\n",wParam, lParam);
982  TRACE ("Got notification %x from %p\n", lpnmh->code, lpnmh->hwndFrom);
983  TRACE ("info: %p %p %p\n",hwnd,infoPtr->hMonthCal,infoPtr->hUpdown);
984  return 0;
985 }
986
987
988 static LRESULT
989 DATETIME_Notify (HWND hwnd, WPARAM wParam, LPARAM lParam)
990
991 {
992  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
993  LPNMHDR lpnmh = (LPNMHDR) lParam;
994
995  TRACE ("%x,%lx\n",wParam, lParam);
996  TRACE ("Got notification %x from %p\n", lpnmh->code, lpnmh->hwndFrom);
997  TRACE ("info: %p %p %p\n",hwnd,infoPtr->hMonthCal,infoPtr->hUpdown);
998  return 0;
999 }
1000
1001
1002 static LRESULT
1003 DATETIME_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1004 {
1005  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1006  int FieldNum,wrap=0;
1007
1008  TRACE("%x %lx %x\n",wParam, lParam, infoPtr->select);
1009
1010  FieldNum = infoPtr->select & DTHT_DATEFIELD;
1011
1012  if (!(infoPtr->haveFocus)) return 0;
1013  if ((FieldNum==0) && (infoPtr->select)) return 0;
1014
1015  if (infoPtr->select & FORMATCALLMASK) {
1016         FIXME ("Callbacks not implemented yet\n");
1017  }
1018
1019  switch (wParam) {
1020         case VK_ADD:
1021         case VK_UP:
1022                 DATETIME_IncreaseField (infoPtr,FieldNum);
1023                 DATETIME_SendDateTimeChangeNotify (hwnd);
1024                 break;
1025         case VK_SUBTRACT:
1026         case VK_DOWN:
1027                 DATETIME_DecreaseField (infoPtr,FieldNum);
1028                 DATETIME_SendDateTimeChangeNotify (hwnd);
1029                 break;
1030         case VK_HOME:
1031                 DATETIME_ResetFieldDown (infoPtr,FieldNum);
1032                 DATETIME_SendDateTimeChangeNotify (hwnd);
1033                 break;
1034         case VK_END:
1035                 DATETIME_ResetFieldUp(infoPtr,FieldNum);
1036                 DATETIME_SendDateTimeChangeNotify (hwnd);
1037                 break;
1038         case VK_LEFT:
1039                 do {
1040                         if (infoPtr->select==0) {
1041                                 infoPtr->select = infoPtr->nrFields - 1;
1042                                 wrap++;
1043                         } else
1044                         infoPtr->select--;
1045                 }
1046                 while ((infoPtr->fieldspec[infoPtr->select] & DT_STRING) && (wrap<2));
1047                 break;
1048         case VK_RIGHT:
1049                 do {
1050                         infoPtr->select++;
1051                         if (infoPtr->select==infoPtr->nrFields) {
1052                                 infoPtr->select = 0;
1053                                 wrap++;
1054                         }
1055                         }
1056                 while ((infoPtr->fieldspec[infoPtr->select] & DT_STRING) && (wrap<2));
1057                 break;
1058         }
1059
1060   InvalidateRect(hwnd, NULL, FALSE);
1061
1062   return 0;
1063 }
1064
1065
1066 static LRESULT
1067 DATETIME_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
1068 {
1069     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1070
1071     TRACE ("\n");
1072
1073     if (infoPtr->haveFocus) {
1074         DATETIME_SendSimpleNotify (hwnd, NM_KILLFOCUS);
1075         infoPtr->haveFocus = 0;
1076     }
1077
1078     InvalidateRect (hwnd, NULL, TRUE);
1079
1080     return 0;
1081 }
1082
1083
1084 static LRESULT
1085 DATETIME_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
1086 {
1087     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1088
1089     TRACE ("\n");
1090
1091     if (infoPtr->haveFocus==0) {
1092         DATETIME_SendSimpleNotify (hwnd, NM_SETFOCUS);
1093         infoPtr->haveFocus = DTHT_GOTFOCUS;
1094     }
1095
1096     InvalidateRect(hwnd, NULL, FALSE);
1097
1098     return 0;
1099 }
1100
1101
1102 static BOOL
1103 DATETIME_SendDateTimeChangeNotify (HWND hwnd)
1104
1105 {
1106  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1107  NMDATETIMECHANGE dtdtc;
1108
1109  TRACE ("\n");
1110  dtdtc.nmhdr.hwndFrom = hwnd;
1111  dtdtc.nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
1112  dtdtc.nmhdr.code     = DTN_DATETIMECHANGE;
1113
1114  if ((GetWindowLongA (hwnd, GWL_STYLE) & DTS_SHOWNONE))
1115    dtdtc.dwFlags = GDT_NONE;
1116  else
1117    dtdtc.dwFlags = GDT_VALID;
1118
1119  MONTHCAL_CopyTime (&infoPtr->date, &dtdtc.st);
1120  return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
1121                               (WPARAM)dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);
1122 }
1123
1124
1125 static BOOL
1126 DATETIME_SendSimpleNotify (HWND hwnd, UINT code)
1127 {
1128     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1129     NMHDR nmhdr;
1130
1131     TRACE("%x\n",code);
1132     nmhdr.hwndFrom = hwnd;
1133     nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
1134     nmhdr.code     = code;
1135
1136     return (BOOL) SendMessageA (infoPtr->hwndNotify, WM_NOTIFY,
1137                                 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1138 }
1139
1140 static LRESULT
1141 DATETIME_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1142 {
1143   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr(hwnd);
1144   DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1145
1146   /* set size */
1147   infoPtr->rcClient.bottom = HIWORD(lParam);
1148   infoPtr->rcClient.right = LOWORD(lParam);
1149
1150   TRACE("Height=%ld, Width=%ld\n", infoPtr->rcClient.bottom, infoPtr->rcClient.right);
1151
1152   /* use DrawEdge to adjust the size of rcEdge to get rcDraw */
1153   memcpy((&infoPtr->rcDraw), (&infoPtr->rcClient), sizeof(infoPtr->rcDraw));
1154
1155   DrawEdge(NULL, &(infoPtr->rcDraw), EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1156
1157   /* set the size of the button that drops the calendar down */
1158   /* FIXME: account for style that allows button on left side */
1159   infoPtr->calbutton.top   = infoPtr->rcDraw.top;
1160   infoPtr->calbutton.bottom= infoPtr->rcDraw.bottom;
1161   infoPtr->calbutton.left  = infoPtr->rcDraw.right-15;
1162   infoPtr->calbutton.right = infoPtr->rcDraw.right;
1163
1164   /* set enable/disable button size for show none style being enabled */
1165   /* FIXME: these dimensions are completely incorrect */
1166   infoPtr->checkbox.top = infoPtr->rcDraw.top;
1167   infoPtr->checkbox.bottom = infoPtr->rcDraw.bottom;
1168   infoPtr->checkbox.left = infoPtr->rcDraw.left;
1169   infoPtr->checkbox.right = infoPtr->rcDraw.left + 10;
1170
1171   /* update the position of the monthcal control */
1172   if(dwStyle & DTS_RIGHTALIGN)
1173     infoPtr->monthcal_pos.x = infoPtr->rcClient.right - ((infoPtr->calbutton.right -
1174                                 infoPtr->calbutton.left) + 145);
1175   else
1176     infoPtr->monthcal_pos.x = 8;
1177
1178   infoPtr->monthcal_pos.y = infoPtr->rcClient.bottom;
1179   ClientToScreen (hwnd, &(infoPtr->monthcal_pos));
1180   SetWindowPos(infoPtr->hMonthCal, 0, infoPtr->monthcal_pos.x,
1181     infoPtr->monthcal_pos.y,
1182     145, 150, 0);
1183
1184   InvalidateRect(hwnd, NULL, FALSE);
1185
1186   return 0;
1187 }
1188
1189
1190 static LRESULT
1191 DATETIME_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1192 {
1193   DATETIME_INFO *infoPtr;
1194   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1195
1196   /* allocate memory for info structure */
1197   TRACE("%04x %08lx\n",wParam,lParam);
1198   infoPtr = (DATETIME_INFO *)Alloc (sizeof(DATETIME_INFO));
1199   if (infoPtr == NULL) {
1200     ERR("could not allocate info memory!\n");
1201     return 0;
1202   }
1203
1204   SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
1205
1206   if (dwStyle & DTS_SHOWNONE) {
1207     infoPtr->hwndCheckbut=CreateWindowExA (0,"button", 0,
1208          WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
1209          2,2,13,13,
1210          hwnd,
1211          0, (HINSTANCE)GetWindowLongA  (hwnd, GWL_HINSTANCE), 0);
1212          SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, 1, 0);
1213   }
1214
1215   if (dwStyle & DTS_UPDOWN) {
1216     infoPtr->hUpdown=CreateUpDownControl (
1217         WS_CHILD | WS_BORDER | WS_VISIBLE,
1218         120,1,20,20,
1219         hwnd,1,0,0,
1220         UD_MAXVAL, UD_MINVAL, 0);
1221   }
1222
1223   infoPtr->fieldspec = (int *) Alloc (32*sizeof(int));
1224   infoPtr->fieldRect = (RECT *) Alloc (32*sizeof(RECT));
1225   infoPtr->buflen = (int *) Alloc (32*sizeof(int));
1226   infoPtr->nrFieldsAllocated = 32;
1227   infoPtr->hwndNotify = ((LPCREATESTRUCTA)lParam)->hwndParent;
1228
1229   DATETIME_SetFormat (hwnd, 0, 0);
1230
1231   /* create the monthcal control */
1232     infoPtr->hMonthCal = CreateWindowExA (0,"SysMonthCal32", 0,
1233         WS_BORDER | WS_POPUP | WS_CLIPSIBLINGS,
1234         0, 0, 0, 0,
1235         infoPtr->hwndNotify,
1236         0, 0, 0);
1237
1238   /* initialize info structure */
1239   GetSystemTime (&infoPtr->date);
1240   infoPtr->dateValid = TRUE;
1241   infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1242   return 0;
1243 }
1244
1245
1246 static LRESULT
1247 DATETIME_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1248 {
1249     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1250
1251     TRACE("\n");
1252     Free (infoPtr);
1253     SetWindowLongA( hwnd, 0, 0 );
1254     return 0;
1255 }
1256
1257
1258 static LRESULT WINAPI
1259 DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1260 {
1261     if (!DATETIME_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
1262         return DefWindowProcA( hwnd, uMsg, wParam, lParam );
1263
1264     switch (uMsg)
1265     {
1266
1267     case DTM_GETSYSTEMTIME:
1268         return DATETIME_GetSystemTime (hwnd, wParam, lParam);
1269
1270     case DTM_SETSYSTEMTIME:
1271         return DATETIME_SetSystemTime (hwnd, wParam, lParam);
1272
1273     case DTM_GETRANGE:
1274         return DATETIME_GetRange(hwnd, lParam);
1275
1276     case DTM_SETRANGE:
1277         return DATETIME_SetRange(hwnd, wParam, lParam);
1278
1279     case DTM_SETFORMATA:
1280         return DATETIME_SetFormat (hwnd, wParam, lParam);
1281
1282     case DTM_SETFORMATW:
1283         return DATETIME_SetFormatW (hwnd, wParam, lParam);
1284
1285     case DTM_SETMCCOLOR:
1286         return DATETIME_SetMonthCalColor (hwnd, wParam, lParam);
1287
1288     case DTM_GETMCCOLOR:
1289         return DATETIME_GetMonthCalColor (hwnd, wParam);
1290
1291     case DTM_GETMONTHCAL:
1292         return DATETIME_GetMonthCal (hwnd);
1293
1294     case DTM_SETMCFONT:
1295         return DATETIME_SetMonthCalFont (hwnd, wParam, lParam);
1296
1297     case DTM_GETMCFONT:
1298         return DATETIME_GetMonthCalFont (hwnd);
1299
1300     case WM_PARENTNOTIFY:
1301         return DATETIME_ParentNotify (hwnd, wParam, lParam);
1302
1303     case WM_NOTIFY:
1304         return DATETIME_Notify (hwnd, wParam, lParam);
1305
1306     case WM_GETDLGCODE:
1307         return DLGC_WANTARROWS | DLGC_WANTCHARS;
1308
1309     case WM_PAINT:
1310         return DATETIME_Paint (hwnd, wParam);
1311
1312     case WM_KEYDOWN:
1313         return DATETIME_KeyDown (hwnd, wParam, lParam);
1314
1315     case WM_KILLFOCUS:
1316         return DATETIME_KillFocus (hwnd, wParam, lParam);
1317
1318     case WM_SETFOCUS:
1319         return DATETIME_SetFocus (hwnd, wParam, lParam);
1320
1321     case WM_SIZE:
1322         return DATETIME_Size (hwnd, wParam, lParam);
1323
1324     case WM_LBUTTONDOWN:
1325         return DATETIME_LButtonDown (hwnd, wParam, lParam);
1326
1327     case WM_LBUTTONUP:
1328         return DATETIME_LButtonUp (hwnd, wParam, lParam);
1329
1330     case WM_CREATE:
1331         return DATETIME_Create (hwnd, wParam, lParam);
1332
1333     case WM_DESTROY:
1334         return DATETIME_Destroy (hwnd, wParam, lParam);
1335
1336     case WM_COMMAND:
1337         return DATETIME_Command (hwnd, wParam, lParam);
1338
1339     default:
1340         if ((uMsg >= WM_USER) && (uMsg < WM_APP))
1341                 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
1342                      uMsg, wParam, lParam);
1343         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1344     }
1345     return 0;
1346 }
1347
1348
1349 VOID
1350 DATETIME_Register (void)
1351 {
1352     WNDCLASSA wndClass;
1353
1354     TRACE("\n");
1355     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1356     wndClass.style         = CS_GLOBALCLASS;
1357     wndClass.lpfnWndProc   = (WNDPROC)DATETIME_WindowProc;
1358     wndClass.cbClsExtra    = 0;
1359     wndClass.cbWndExtra    = sizeof(DATETIME_INFO *);
1360     wndClass.hCursor       = LoadCursorA (0, (LPSTR)IDC_ARROW);
1361     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1362     wndClass.lpszClassName = DATETIMEPICK_CLASSA;
1363
1364     RegisterClassA (&wndClass);
1365 }
1366
1367
1368 VOID
1369 DATETIME_Unregister (void)
1370 {
1371     TRACE("\n");
1372     UnregisterClassA (DATETIMEPICK_CLASSA, NULL);
1373 }