Extended the winmm joystick API calls to support the new Linux
[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  *
7  *
8  * TODO:
9  *   - All messages.
10  *   - All notifications.
11  *
12  */
13
14 #include "winbase.h"
15 #include "commctrl.h"
16 #include "datetime.h"
17 #include "monthcal.h"
18 #include "debugtools.h"
19
20 #include <math.h>
21 #include <string.h>
22
23 DEFAULT_DEBUG_CHANNEL(datetime)
24
25
26 #define DATETIME_GetInfoPtr(hwnd) ((DATETIME_INFO *)GetWindowLongA (hwnd, 0))
27
28 static BOOL DATETIME_SendSimpleNotify (HWND hwnd, UINT code);
29 static BOOL DATETIME_SendDateTimeChangeNotify (HWND hwnd);
30 extern void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to);
31 static const char * const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
32 "Thursday", "Friday", "Saturday", NULL};
33 static const char *allowedformatchars = {"dhHmMstyX'"};
34 static const int maxrepetition [] = {4,2,2,2,4,2,2,3,-1,-1};
35
36
37 static LRESULT
38 DATETIME_GetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
39 {
40   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
41   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
42   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
43
44   if (!lParam) return GDT_NONE;
45
46   if ((dwStyle & DTS_SHOWNONE) && 
47        (SendMessageA (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0)))
48         return GDT_NONE;
49
50   MONTHCAL_CopyTime (&infoPtr->date, lprgSysTimeArray);
51
52   return GDT_VALID;
53 }
54
55
56 static LRESULT
57 DATETIME_SetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
58 {
59   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
60   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
61
62   if (!lParam) return 0;
63
64   if (lParam==GDT_VALID) 
65         MONTHCAL_CopyTime (lprgSysTimeArray, &infoPtr->date);
66   if (lParam==GDT_NONE) {
67         infoPtr->dateValid=FALSE;
68     SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, 0, 0);
69         }
70   return 1;
71 }
72
73
74 static LRESULT
75 DATETIME_GetMonthCalColor (HWND hwnd, WPARAM wParam)
76 {
77   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
78
79   return SendMessageA (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
80 }
81
82 static LRESULT
83 DATETIME_SetMonthCalColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
84 {
85   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
86
87   return SendMessageA (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
88 }
89
90
91 /* FIXME: need to get way to force font into monthcal structure */
92
93 static LRESULT
94 DATETIME_GetMonthCal (HWND hwnd)
95 {
96   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
97
98   return infoPtr->hMonthCal;
99 }
100
101
102
103 /* FIXME: need to get way to force font into monthcal structure */
104
105 static LRESULT
106 DATETIME_GetMonthCalFont (HWND hwnd)
107 {
108
109   return 0;
110 }
111
112 static LRESULT
113 DATETIME_SetMonthCalFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
114 {
115
116   return 0;
117 }
118
119
120
121 /* 
122    Split up a formattxt in actions. 
123    See ms documentation for the meaning of the letter codes/'specifiers'.
124
125    Notes: 
126    *'dddddd' is handled as 'dddd' plus 'dd'.
127    *unrecognized formats are strings (here given the type DT_STRING; 
128    start of the string is encoded in lower bits of DT_STRING.
129    Therefore, 'string' ends finally up as '<show seconds>tring'.
130
131  */
132
133
134
135 static void 
136 DATETIME_UseFormat (DATETIME_INFO *infoPtr, const char *formattxt)
137 {
138  int i,j,k,len;
139  int *nrFields=& infoPtr->nrFields;
140
141  TRACE ("%s\n",formattxt);
142
143
144  *nrFields=0;
145  infoPtr->fieldspec[*nrFields]=0;
146  len=strlen(allowedformatchars);
147  k=0;
148
149  for (i=0; i<strlen (formattxt); i++)  {
150         TRACE ("\n%d %c:",i, formattxt[i]);
151         for (j=0; j<len; j++) {
152                 if (allowedformatchars[j]==formattxt[i]) {   
153                         TRACE ("%c[%d,%x]",allowedformatchars[j], *nrFields,
154                                                          infoPtr->fieldspec[*nrFields]);
155                         if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
156                                 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
157                                 break;
158                         }
159                         if (infoPtr->fieldspec[*nrFields]>>4!=j) {   
160                                 (*nrFields)++;  
161                                 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
162                                 break;
163                         }
164                         if ((infoPtr->fieldspec[*nrFields] & 0x0f)==maxrepetition[j]) {
165                                 (*nrFields)++;  
166                                 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
167                                 break;
168                         }
169                         infoPtr->fieldspec[*nrFields]++;
170                         break;
171                 }   /* if allowedformatchar */
172          } /* for j */
173
174
175                         /* char is not a specifier: handle char like a string */
176         if (j==len) {
177                 if ((*nrFields==0) && (infoPtr->fieldspec[*nrFields]==0)) {
178                         infoPtr->fieldspec[*nrFields]=DT_STRING+k;
179                         infoPtr->buflen[*nrFields]=0;
180         } else 
181                 if ((infoPtr->fieldspec[*nrFields] & DT_STRING)!=DT_STRING)  {
182                         (*nrFields)++;
183                         infoPtr->fieldspec[*nrFields]=DT_STRING+k;
184                         infoPtr->buflen[*nrFields]=0;
185                 } 
186                 infoPtr->textbuf[k]=formattxt[i];
187                 k++;
188                 infoPtr->buflen[*nrFields]++;
189         }   /* if j=len */
190
191         if (*nrFields==infoPtr->nrFieldsAllocated) {
192                 FIXME ("out of memory; should reallocate. crash ahead.\n");
193         }
194
195   } /* for i */
196
197  if (infoPtr->fieldspec[*nrFields]!=0) (*nrFields)++;
198 }
199
200 static LRESULT 
201 DATETIME_SetFormat (HWND hwnd, WPARAM wParam, LPARAM lParam)
202
203 {
204  DATETIME_INFO *infoPtr= DATETIME_GetInfoPtr (hwnd);
205
206  if (!lParam) {
207         DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
208
209         if (dwStyle & DTS_SHORTDATEFORMAT) 
210                         DATETIME_UseFormat (infoPtr, "M/d/yy");
211         if (dwStyle & DTS_LONGDATEFORMAT) 
212                         DATETIME_UseFormat (infoPtr, "dddd, MMMM dd, yyy");
213         if (dwStyle & DTS_TIMEFORMAT) 
214                         DATETIME_UseFormat (infoPtr, "h:mm:ss tt");
215  }      
216  else
217         DATETIME_UseFormat (infoPtr, (char *) lParam);
218
219  return infoPtr->nrFields;
220 }
221
222 static LRESULT 
223 DATETIME_SetFormatW (HWND hwnd, WPARAM wParam, LPARAM lParam)
224
225 {
226  if (lParam) {
227         LPSTR buf;
228         int retval;
229         int len = lstrlenW ((LPWSTR) lParam)+1;
230
231         buf = (LPSTR) COMCTL32_Alloc (len);
232         lstrcpyWtoA (buf, (LPWSTR) lParam);
233         retval=DATETIME_SetFormat (hwnd, 0, (LPARAM) buf);
234         COMCTL32_Free (buf);
235         return retval;
236  } 
237  else
238         return DATETIME_SetFormat (hwnd, 0, 0);
239
240 }
241
242 static void 
243 DATETIME_ReturnTxt (DATETIME_INFO *infoPtr, int count, char *result)
244 {
245  SYSTEMTIME date = infoPtr->date;
246  int spec;
247
248  *result=0;
249  TRACE ("%d,%d\n", infoPtr->nrFields, count);
250  if ((count>infoPtr->nrFields) || (count<0)) {
251         WARN ("buffer overrun, have %d want %d\n", infoPtr->nrFields, count);
252         return;
253  }
254
255  if (!infoPtr->fieldspec) return;
256  
257  spec=infoPtr->fieldspec[count];
258  if (spec & DT_STRING) {
259         int txtlen=infoPtr->buflen[count];
260
261         strncpy (result, infoPtr->textbuf + (spec &~ DT_STRING), txtlen);
262         result[txtlen]=0;
263         TRACE ("arg%d=%x->[%s]\n",count,infoPtr->fieldspec[count],result);
264         return;
265  }
266                 
267
268  switch (spec) {
269         case DT_END_FORMAT: 
270                 *result=0;
271                 break;
272         case ONEDIGITDAY: 
273                 sprintf (result,"%d",date.wDay);
274                 break;
275         case TWODIGITDAY: 
276                 sprintf (result,"%.2d",date.wDay);
277                 break;
278         case THREECHARDAY: 
279                 sprintf (result,"%.3s",days[date.wDayOfWeek]);
280                 break;
281         case FULLDAY:   
282                 strcpy  (result,days[date.wDayOfWeek]);
283                 break;
284         case ONEDIGIT12HOUR: 
285                 if (date.wHour>12) 
286                         sprintf (result,"%d",date.wHour-12);
287                 else 
288                         sprintf (result,"%d",date.wHour);
289                 break;
290         case TWODIGIT12HOUR: 
291                 if (date.wHour>12) 
292                         sprintf (result,"%.2d",date.wHour-12);
293                 else 
294                         sprintf (result,"%.2d",date.wHour);
295                 break;
296         case ONEDIGIT24HOUR: 
297                 sprintf (result,"%d",date.wHour);
298                 break;
299         case TWODIGIT24HOUR: 
300                 sprintf (result,"%.2d",date.wHour);
301                 break;
302         case ONEDIGITSECOND: 
303                 sprintf (result,"%d",date.wSecond);
304                 break;
305         case TWODIGITSECOND: 
306                 sprintf (result,"%.2d",date.wSecond);
307                 break;
308         case ONEDIGITMINUTE: 
309                 sprintf (result,"%d",date.wMinute);
310                 break;
311         case TWODIGITMINUTE: 
312                 sprintf (result,"%.2d",date.wMinute);
313                 break;
314         case ONEDIGITMONTH: 
315                 sprintf (result,"%d",date.wMonth);
316                 break;
317         case TWODIGITMONTH: 
318                 sprintf (result,"%.2d",date.wMonth);
319                 break;
320         case THREECHARMONTH: 
321                 sprintf (result,"%.3s",monthtxt[date.wMonth-1]);
322                 break;
323         case FULLMONTH:   
324                 strcpy (result,monthtxt[date.wMonth-1]);
325                 break;
326         case ONELETTERAMPM:   
327                 if (date.wHour<12) 
328                         strcpy (result,"A");
329                 else 
330                         strcpy (result,"P");
331                 break;
332         case TWOLETTERAMPM:   
333                 if (date.wHour<12) 
334                         strcpy (result,"AM");
335                 else 
336                         strcpy (result,"PM");
337                 break;
338         case FORMATCALLBACK:   
339                 FIXME ("Not implemented\n");
340                 strcpy (result,"xxx");
341                 break;
342         case ONEDIGITYEAR: 
343                 sprintf (result,"%d",date.wYear-10* (int) floor(date.wYear/10));
344                 break;
345         case TWODIGITYEAR: 
346                 sprintf (result,"%.2d",date.wYear-100* (int) floor(date.wYear/100));
347                 break;
348         case FULLYEAR:   
349                 sprintf (result,"%d",date.wYear);
350                 break;
351     }
352         
353         TRACE ("arg%d=%x->[%s]\n",count,infoPtr->fieldspec[count],result);
354 }
355
356 static void 
357 DATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number)
358 {
359  SYSTEMTIME *date = &infoPtr->date;
360  int spec;
361
362  TRACE ("%d\n",number);
363  if ((number>infoPtr->nrFields) || (number<0)) return;
364
365  spec=infoPtr->fieldspec[number];
366  if ((spec & DTHT_DATEFIELD)==0) return;
367                 
368
369  switch (spec) {
370         case ONEDIGITDAY: 
371         case TWODIGITDAY: 
372         case THREECHARDAY: 
373         case FULLDAY:
374                 date->wDay++;
375                 if (date->wDay>mdays[date->wMonth-1]) date->wDay=1;     
376                 break;
377         case ONEDIGIT12HOUR: 
378         case TWODIGIT12HOUR: 
379         case ONEDIGIT24HOUR: 
380         case TWODIGIT24HOUR: 
381                 date->wHour++;
382                 if (date->wHour>23) date->wHour=0;
383                 break;
384         case ONEDIGITSECOND: 
385         case TWODIGITSECOND: 
386                 date->wSecond++;
387                 if (date->wSecond>59) date->wSecond=0;
388                 break;
389         case ONEDIGITMINUTE: 
390         case TWODIGITMINUTE: 
391                 date->wMinute++;
392                 if (date->wMinute>59) date->wMinute=0;
393                 break;
394         case ONEDIGITMONTH: 
395         case TWODIGITMONTH: 
396         case THREECHARMONTH: 
397         case FULLMONTH:   
398                 date->wMonth++;
399                 if (date->wMonth>12) date->wMonth=1;
400                 if (date->wDay>mdays[date->wMonth-1]) 
401                         date->wDay=mdays[date->wMonth-1];
402                 break;
403         case ONELETTERAMPM:   
404         case TWOLETTERAMPM:   
405                 date->wHour+=12;
406                 if (date->wHour>23) date->wHour-=24;
407                 break;
408         case FORMATCALLBACK:   
409                 FIXME ("Not implemented\n");
410                 break;
411         case ONEDIGITYEAR: 
412         case TWODIGITYEAR: 
413         case FULLYEAR:   
414                 date->wYear++;
415                 break;
416         }
417
418 }
419
420 static void 
421 DATETIME_DecreaseField (DATETIME_INFO *infoPtr, int number)
422 {
423  SYSTEMTIME *date = & infoPtr->date;
424  int spec;
425
426  TRACE ("%d\n",number);
427  if ((number>infoPtr->nrFields) || (number<0)) return;
428
429  spec=infoPtr->fieldspec[number];
430  if ((spec & DTHT_DATEFIELD)==0) return;
431                 
432  TRACE ("%x\n",spec);
433
434  switch (spec) {
435         case ONEDIGITDAY: 
436         case TWODIGITDAY: 
437         case THREECHARDAY: 
438         case FULLDAY:
439                 date->wDay--;
440                 if (date->wDay<1) date->wDay=mdays[date->wMonth-1];
441                 break;
442         case ONEDIGIT12HOUR: 
443         case TWODIGIT12HOUR: 
444         case ONEDIGIT24HOUR: 
445         case TWODIGIT24HOUR: 
446                 if (date->wHour) 
447                         date->wHour--;
448                 else
449                         date->wHour=23;
450                 break;
451         case ONEDIGITSECOND: 
452         case TWODIGITSECOND: 
453                 if (date->wHour) 
454                         date->wSecond--;
455                 else
456                         date->wHour=59;
457                 break;
458         case ONEDIGITMINUTE: 
459         case TWODIGITMINUTE: 
460                 if (date->wMinute) 
461                         date->wMinute--;
462                 else
463                         date->wMinute=59;
464                 break;
465         case ONEDIGITMONTH: 
466         case TWODIGITMONTH: 
467         case THREECHARMONTH: 
468         case FULLMONTH:   
469                 if (date->wMonth>1) 
470                         date->wMonth--;
471                 else
472                         date->wMonth=12;
473                 if (date->wDay>mdays[date->wMonth-1]) 
474                         date->wDay=mdays[date->wMonth-1];
475                 break;
476         case ONELETTERAMPM:   
477         case TWOLETTERAMPM:   
478                 if (date->wHour<12) 
479                         date->wHour+=12;
480                 else
481                         date->wHour-=12;
482                 break;
483         case FORMATCALLBACK:   
484                 FIXME ("Not implemented\n");
485                 break;
486         case ONEDIGITYEAR: 
487         case TWODIGITYEAR: 
488         case FULLYEAR:   
489                 date->wYear--;
490                 break;
491         }
492
493 }
494
495
496 static void 
497 DATETIME_ResetFieldDown (DATETIME_INFO *infoPtr, int number)
498 {
499  SYSTEMTIME *date = &infoPtr->date;
500  int spec;
501
502  TRACE ("%d\n",number);
503  if ((number>infoPtr->nrFields) || (number<0)) return;
504
505  spec=infoPtr->fieldspec[number];
506  if ((spec & DTHT_DATEFIELD)==0) return;
507                 
508
509  switch (spec) {
510         case ONEDIGITDAY: 
511         case TWODIGITDAY: 
512         case THREECHARDAY: 
513         case FULLDAY:
514                 date->wDay=1;
515                 break;
516         case ONEDIGIT12HOUR: 
517         case TWODIGIT12HOUR: 
518         case ONEDIGIT24HOUR: 
519         case TWODIGIT24HOUR: 
520         case ONELETTERAMPM:   
521         case TWOLETTERAMPM:   
522                 date->wHour=0;
523                 break;
524         case ONEDIGITSECOND: 
525         case TWODIGITSECOND: 
526                 date->wSecond=0;
527                 break;
528         case ONEDIGITMINUTE: 
529         case TWODIGITMINUTE: 
530                 date->wMinute=0;
531                 break;
532         case ONEDIGITMONTH: 
533         case TWODIGITMONTH: 
534         case THREECHARMONTH: 
535         case FULLMONTH:   
536                 date->wMonth=1;
537         case FORMATCALLBACK:   
538                 FIXME ("Not implemented\n");
539                 break;
540         case ONEDIGITYEAR: 
541         case TWODIGITYEAR: 
542         case FULLYEAR:   
543                 date->wSecond=0;
544                 date->wMinute=0;
545                 date->wHour=0;
546                 date->wDay=14;          /* overactive ms-programmers..*/
547                 date->wMonth=9;
548                 date->wYear=1752;
549                 break;
550         }
551
552 }
553
554 static void 
555 DATETIME_ResetFieldUp (DATETIME_INFO *infoPtr, int number)
556 {
557  SYSTEMTIME *date = & infoPtr->date;
558  int spec;
559
560  if ((number>infoPtr->nrFields) || (number<0)) return;
561
562  spec=infoPtr->fieldspec[number];
563  if ((spec & DTHT_DATEFIELD)==0) return;
564                 
565  switch (spec) {
566         case ONEDIGITDAY: 
567         case TWODIGITDAY: 
568         case THREECHARDAY: 
569         case FULLDAY:
570                 date->wDay=mdays[date->wMonth-1];
571                 break;
572         case ONEDIGIT12HOUR: 
573         case TWODIGIT12HOUR: 
574         case ONEDIGIT24HOUR: 
575         case TWODIGIT24HOUR: 
576         case ONELETTERAMPM:   
577         case TWOLETTERAMPM:   
578                 date->wHour=23;
579                 break;
580         case ONEDIGITSECOND: 
581         case TWODIGITSECOND: 
582                 date->wSecond=59;
583                 break;
584         case ONEDIGITMINUTE: 
585         case TWODIGITMINUTE: 
586                 date->wMinute=59;
587                 break;
588         case ONEDIGITMONTH: 
589         case TWODIGITMONTH: 
590         case THREECHARMONTH: 
591         case FULLMONTH:   
592                 date->wMonth=12;
593         case FORMATCALLBACK:   
594                 FIXME ("Not implemented\n");
595                 break;
596         case ONEDIGITYEAR: 
597         case TWODIGITYEAR: 
598         case FULLYEAR:   
599                 date->wYear=9999;    /* Y10K problem? naaah. */
600                 break;
601         }
602
603 }
604
605
606 static void DATETIME_Refresh (HWND hwnd, HDC hdc)
607
608 {
609   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
610   int i,prevright;
611   RECT *field;
612   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
613   RECT *rect     = &infoPtr->rect;
614   RECT *calbutton= &infoPtr->calbutton;
615   RECT *checkbox= &infoPtr->checkbox;
616   HBRUSH hbr;
617   SIZE size;
618   BOOL prssed=FALSE;
619   COLORREF oldBk, oldTextColor;
620   
621
622         
623   if (infoPtr->dateValid) {
624     char txt[80];
625         HFONT oldFont;
626         oldFont = SelectObject (hdc, infoPtr->hFont);
627
628         GetClientRect (hwnd, rect);
629         hbr = CreateSolidBrush (GetSysColor (COLOR_WINDOW));
630     FillRect(hdc, rect, hbr);
631     DeleteObject(hbr);
632
633         
634         DATETIME_ReturnTxt (infoPtr, 0, txt);
635         GetTextExtentPoint32A (hdc, txt, strlen (txt), &size);
636         rect->bottom=size.cy+2;
637
638         checkbox->left  = 0;
639         checkbox->right = 0;
640         checkbox->top   = rect->top;
641         checkbox->bottom= rect->bottom;
642
643     if (dwStyle & DTS_SHOWNONE) checkbox->right=18;
644
645     prevright=checkbox->right;
646         for (i=0; i<infoPtr->nrFields; i++) {
647                 DATETIME_ReturnTxt (infoPtr, i, txt);
648                 GetTextExtentPoint32A (hdc, txt, strlen (txt), &size);
649                 field = & infoPtr->fieldRect[i];
650         field->left  = prevright;
651         field->right = prevright+size.cx;
652         field->top   = rect->top;
653         field->bottom= rect->bottom;
654         prevright=field->right;
655
656         if ((infoPtr->haveFocus) && (i==infoPtr->select)) {
657                 hbr=CreateSolidBrush (GetSysColor (COLOR_ACTIVECAPTION));
658                 FillRect(hdc, field, hbr);
659                 oldBk=SetBkColor (hdc, GetSysColor(COLOR_ACTIVECAPTION));
660                 oldTextColor=SetTextColor (hdc, GetSysColor(COLOR_WINDOW));
661                 DeleteObject (hbr);
662                 DrawTextA ( hdc, txt, strlen(txt), field,
663                         DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
664                 SetBkColor (hdc, oldBk);
665                 SetTextColor (hdc, oldTextColor);
666         }
667         else 
668             DrawTextA ( hdc, txt, strlen(txt), field,
669                          DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
670         }
671
672         SelectObject (hdc, oldFont);
673         }
674
675         if (!(dwStyle & DTS_UPDOWN)) {
676
677                 calbutton->right = rect->right;
678                 calbutton->left  = rect->right-15;
679                 calbutton->top   = rect->top;
680                 calbutton->bottom= rect->bottom;
681
682                 DrawFrameControl(hdc, calbutton, DFC_SCROLL,
683                 DFCS_SCROLLDOWN | (prssed ? DFCS_PUSHED : 0) |
684                 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
685         }
686
687 }
688
689 static LRESULT
690 DATETIME_HitTest (HWND hwnd, DATETIME_INFO *infoPtr, POINT pt)
691 {
692   int i,retval;
693
694   TRACE ("%ld, %ld\n",pt.x,pt.y);
695
696   retval=DTHT_NONE;
697   if (PtInRect (&infoPtr->calbutton, pt)) {retval=DTHT_MCPOPUP; goto done; }
698   if (PtInRect (&infoPtr->checkbox, pt))  {retval=DTHT_CHECKBOX; goto done; }
699
700   for (i=0; i<infoPtr->nrFields; i++) {
701         if (PtInRect (&infoPtr->fieldRect[i], pt)) {
702                 retval=i;
703                 break;
704         }
705  }
706
707 done:
708   TRACE ("%x\n",retval);
709   return retval;
710 }
711
712 static LRESULT
713 DATETIME_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
714 {
715   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);  
716   POINT pt;
717   int old,new;
718
719   TRACE ("\n");
720
721   old=infoPtr->select;
722   pt.x=(INT)LOWORD(lParam);
723   pt.y=(INT)HIWORD(lParam);
724
725   new=DATETIME_HitTest (hwnd, infoPtr, pt);
726
727   if ((new & DT_STRING)==0) infoPtr->select=new;
728
729   if (infoPtr->select!=old) {
730                 HDC hdc;
731
732                 SetFocus (hwnd);
733                 hdc=GetDC (hwnd);
734                 DATETIME_Refresh (hwnd,hdc);
735                 infoPtr->haveFocus=DTHT_GOTFOCUS;
736                 
737         ReleaseDC (hwnd, hdc);
738    }
739
740   if (infoPtr->select==DTHT_MCPOPUP) {
741         POINT pt;
742
743         pt.x=8;
744         pt.y=infoPtr->rect.bottom+5;
745         ClientToScreen (hwnd, &pt);
746         infoPtr->hMonthCal=CreateWindowExA (0,"SysMonthCal32", 0, 
747                         WS_POPUP | WS_BORDER,
748                         pt.x,pt.y,145,150,
749                         GetParent (hwnd), 
750                         0,0,0);
751
752         TRACE ("dt:%x mc:%x mc parent:%x, desktop:%x, mcpp:%x\n",
753                 hwnd,infoPtr->hMonthCal, 
754                 GetParent (infoPtr->hMonthCal),
755                 GetDesktopWindow (),
756                 GetParent (GetParent (infoPtr->hMonthCal)));
757
758         SetFocus (hwnd);
759         DATETIME_SendSimpleNotify (hwnd, DTN_DROPDOWN);
760         return 0;
761     }
762
763     return 0;
764 }
765
766
767 static LRESULT
768 DATETIME_Paint (HWND hwnd, WPARAM wParam)
769 {
770     HDC hdc;
771     PAINTSTRUCT ps;
772
773     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
774     DATETIME_Refresh (hwnd, hdc);
775     if(!wParam)
776     EndPaint (hwnd, &ps);
777     return 0;
778 }
779
780 static LRESULT
781 DATETIME_ParentNotify (HWND hwnd, WPARAM wParam, LPARAM lParam)
782 {
783  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);   
784  LPNMHDR lpnmh=(LPNMHDR) lParam;
785
786  TRACE ("%x,%lx\n",wParam, lParam);
787  TRACE ("Got notification %x from %x\n", lpnmh->code, lpnmh->hwndFrom);
788  TRACE ("info: %x %x %x\n",hwnd,infoPtr->hMonthCal,infoPtr->hUpdown);
789  return 0;
790 }
791
792 static LRESULT
793 DATETIME_Notify (HWND hwnd, WPARAM wParam, LPARAM lParam)
794
795 {
796  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);   
797  LPNMHDR lpnmh=(LPNMHDR) lParam;
798
799  TRACE ("%x,%lx\n",wParam, lParam);
800  TRACE ("Got notification %x from %x\n", lpnmh->code, lpnmh->hwndFrom);
801  TRACE ("info: %x %x %x\n",hwnd,infoPtr->hMonthCal,infoPtr->hUpdown);
802  return 0;
803 }
804
805
806
807 static LRESULT 
808 DATETIME_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
809 {
810  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
811  HDC hdc;
812  int FieldNum,wrap=0;
813
814  TRACE("%x %lx %x\n",wParam, lParam, infoPtr->select);
815
816  FieldNum=infoPtr->select & DTHT_DATEFIELD;
817
818  if (!(infoPtr->haveFocus)) return 0;
819  if ((FieldNum==0) && (infoPtr->select)) return 0;
820
821  if (infoPtr->select & FORMATCALLMASK) {
822         FIXME ("Callbacks not implemented yet\n");
823  }
824
825  switch (wParam) {
826         case VK_ADD:
827         case VK_UP: 
828                 DATETIME_IncreaseField (infoPtr,FieldNum);
829                 DATETIME_SendDateTimeChangeNotify (hwnd);
830                 break;
831         case VK_SUBTRACT:
832         case VK_DOWN:
833                 DATETIME_DecreaseField (infoPtr,FieldNum);
834                 DATETIME_SendDateTimeChangeNotify (hwnd);
835                 break;
836         case VK_HOME:
837                 DATETIME_ResetFieldDown (infoPtr,FieldNum);
838                 DATETIME_SendDateTimeChangeNotify (hwnd);
839                 break;
840         case VK_END:
841                 DATETIME_ResetFieldUp(infoPtr,FieldNum);
842                 DATETIME_SendDateTimeChangeNotify (hwnd);
843                 break;
844         case VK_LEFT: 
845                 do {
846                         if (infoPtr->select==0) {
847                                 infoPtr->select=infoPtr->nrFields-1;
848                                 wrap++;
849                         } else 
850                         infoPtr->select--;
851                 }
852                 while ((infoPtr->fieldspec[infoPtr->select] & DT_STRING) && (wrap<2));
853                 break;
854         case VK_RIGHT:  
855                 do {
856                         infoPtr->select++;
857                         if (infoPtr->select==infoPtr->nrFields) {
858                                 infoPtr->select=0;
859                                 wrap++;
860                         }
861                         }
862                 while ((infoPtr->fieldspec[infoPtr->select] & DT_STRING) && (wrap<2));
863                 break;
864         }
865
866   hdc = GetDC (hwnd);
867   DATETIME_Refresh (hwnd, hdc);
868   ReleaseDC (hwnd, hdc);
869
870   return 0;
871 }
872
873
874 static LRESULT
875 DATETIME_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
876 {
877     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
878     HDC hdc;
879
880     TRACE ("\n");
881
882     if (infoPtr->haveFocus) {
883         DATETIME_SendSimpleNotify (hwnd, NM_KILLFOCUS);
884         infoPtr->haveFocus = 0;
885     }
886     hdc = GetDC (hwnd);
887     DATETIME_Refresh (hwnd, hdc);
888     ReleaseDC (hwnd, hdc);
889     InvalidateRect (hwnd, NULL, TRUE);
890
891     return 0;
892 }
893
894
895 static LRESULT
896 DATETIME_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
897 {
898     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
899     HDC hdc;
900
901     TRACE ("\n");
902
903     if (infoPtr->haveFocus==0) {
904         DATETIME_SendSimpleNotify (hwnd, NM_SETFOCUS);  
905         infoPtr->haveFocus=DTHT_GOTFOCUS;
906     }
907     hdc = GetDC (hwnd);
908     DATETIME_Refresh (hwnd, hdc);
909     ReleaseDC (hwnd, hdc);
910
911     return 0;
912 }
913
914
915 static BOOL
916 DATETIME_SendDateTimeChangeNotify (HWND hwnd)
917
918 {
919  DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);   
920  NMDATETIMECHANGE dtdtc;
921
922  TRACE ("\n");
923  dtdtc.nmhdr.hwndFrom = hwnd;
924  dtdtc.nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
925  dtdtc.nmhdr.code     = DTN_DATETIMECHANGE;
926
927  if ((GetWindowLongA (hwnd, GWL_STYLE) & DTS_SHOWNONE))
928         dtdtc.dwFlags = GDT_NONE;
929  else
930         dtdtc.dwFlags = GDT_VALID;
931
932  MONTHCAL_CopyTime (&infoPtr->date, &dtdtc.st);
933  return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
934                               (WPARAM)dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);
935 }
936
937
938 static BOOL
939 DATETIME_SendSimpleNotify (HWND hwnd, UINT code)
940 {
941     NMHDR nmhdr;
942
943     TRACE("%x\n",code);
944     nmhdr.hwndFrom = hwnd;
945     nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
946     nmhdr.code     = code;
947
948     return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
949                                 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
950 }
951
952
953
954
955
956
957 static LRESULT
958 DATETIME_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
959 {
960   DATETIME_INFO *infoPtr;
961   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
962
963     /* allocate memory for info structure */
964     infoPtr = (DATETIME_INFO *)COMCTL32_Alloc (sizeof(DATETIME_INFO));
965     if (infoPtr == NULL) {
966         ERR("could not allocate info memory!\n");
967         return 0;
968     }
969
970     SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
971
972         if (dwStyle & DTS_SHOWNONE) {
973                 infoPtr->hwndCheckbut=CreateWindowExA (0,"button", 0, 
974                                 WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 
975                                 2,2,13,13,
976                                 hwnd, 
977                 0, GetWindowLongA  (hwnd, GWL_HINSTANCE), 0);
978                 SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, 1, 0);
979         }
980
981         if (dwStyle & DTS_UPDOWN) {
982
983                 infoPtr->hUpdown=CreateUpDownControl (
984                                 WS_CHILD | WS_BORDER | WS_VISIBLE,
985                                 120,1,20,20,
986                                 hwnd,1,0,0,
987                                 UD_MAXVAL, UD_MINVAL, 0);
988         }
989
990         infoPtr->fieldspec=(int *) COMCTL32_Alloc (32*sizeof(int));
991         infoPtr->fieldRect=(RECT *) COMCTL32_Alloc (32*sizeof(RECT));
992         infoPtr->buflen=(int *) COMCTL32_Alloc (32*sizeof(int));
993         infoPtr->nrFieldsAllocated=32;
994
995         DATETIME_SetFormat (hwnd, 0, 0);
996
997     /* initialize info structure */
998         infoPtr->hMonthCal=0;
999         GetSystemTime (&infoPtr->date);
1000         infoPtr->dateValid = TRUE;
1001         infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1002     return 0;
1003 }
1004
1005
1006 static LRESULT
1007 DATETIME_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1008 {
1009     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1010         
1011     COMCTL32_Free (infoPtr);
1012     return 0;
1013 }
1014
1015
1016
1017
1018
1019 static LRESULT WINAPI
1020 DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1021 {
1022
1023     switch (uMsg)
1024     {
1025
1026     case DTM_GETSYSTEMTIME:
1027                 DATETIME_GetSystemTime (hwnd, wParam, lParam);
1028
1029     case DTM_SETSYSTEMTIME:
1030                 DATETIME_SetSystemTime (hwnd, wParam, lParam);
1031
1032     case DTM_GETRANGE:
1033         FIXME("Unimplemented msg DTM_GETRANGE\n");
1034         return 0;
1035
1036     case DTM_SETRANGE:
1037         FIXME("Unimplemented msg DTM_SETRANGE\n");
1038         return 1;
1039
1040     case DTM_SETFORMATA:
1041         return DATETIME_SetFormat (hwnd, wParam, lParam);
1042
1043     case DTM_SETFORMATW:
1044         return DATETIME_SetFormatW (hwnd, wParam, lParam);
1045
1046     case DTM_SETMCCOLOR:
1047         return DATETIME_SetMonthCalColor (hwnd, wParam, lParam);
1048
1049     case DTM_GETMCCOLOR:
1050         return DATETIME_GetMonthCalColor (hwnd, wParam);
1051
1052     case DTM_GETMONTHCAL:
1053         return DATETIME_GetMonthCal (hwnd);
1054
1055     case DTM_SETMCFONT:
1056         return DATETIME_SetMonthCalFont (hwnd, wParam, lParam);
1057
1058     case DTM_GETMCFONT:
1059         return DATETIME_GetMonthCalFont (hwnd);
1060
1061     case WM_PARENTNOTIFY:
1062         return DATETIME_ParentNotify (hwnd, wParam, lParam);
1063
1064     case WM_NOTIFY:
1065         return DATETIME_Notify (hwnd, wParam, lParam);
1066
1067     case WM_GETDLGCODE:
1068         return DLGC_WANTARROWS | DLGC_WANTCHARS;
1069
1070     case WM_PAINT:
1071         return DATETIME_Paint (hwnd, wParam);
1072
1073     case WM_KEYDOWN:
1074         return DATETIME_KeyDown (hwnd, wParam, lParam);
1075
1076     case WM_KILLFOCUS:
1077         return DATETIME_KillFocus (hwnd, wParam, lParam);
1078
1079     case WM_SETFOCUS:
1080         return DATETIME_SetFocus (hwnd, wParam, lParam);
1081
1082     case WM_LBUTTONDOWN:
1083         return DATETIME_LButtonDown (hwnd, wParam, lParam);
1084
1085     case WM_CREATE:
1086         return DATETIME_Create (hwnd, wParam, lParam);
1087
1088     case WM_DESTROY:
1089         return DATETIME_Destroy (hwnd, wParam, lParam);
1090
1091     default:
1092         if (uMsg >= WM_USER)
1093                 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
1094                      uMsg, wParam, lParam);
1095         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1096     }
1097     return 0;
1098 }
1099
1100
1101 VOID
1102 DATETIME_Register (void)
1103 {
1104     WNDCLASSA wndClass;
1105
1106     if (GlobalFindAtomA (DATETIMEPICK_CLASSA)) return;
1107
1108     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1109     wndClass.style         = CS_GLOBALCLASS;
1110     wndClass.lpfnWndProc   = (WNDPROC)DATETIME_WindowProc;
1111     wndClass.cbClsExtra    = 0;
1112     wndClass.cbWndExtra    = sizeof(DATETIME_INFO *);
1113     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
1114     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1115     wndClass.lpszClassName = DATETIMEPICK_CLASSA;
1116  
1117     RegisterClassA (&wndClass);
1118 }
1119
1120
1121 VOID
1122 DATETIME_Unregister (void)
1123 {
1124     if (GlobalFindAtomA (DATETIMEPICK_CLASSA))
1125         UnregisterClassA (DATETIMEPICK_CLASSA, (HINSTANCE)NULL);
1126 }
1127