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