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