- Implementation of WM_SETFORMAT
[wine/wine64.git] / dlls / comctl32 / datetime.c
blob583598fc62a4bd9a1ec51631445b5698d8c02c23
1 /*
2 * Date and time picker control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999, 2000 Alex Priem <alexp@sci.kun.nl>
8 * TODO:
9 * - All messages.
10 * - All notifications.
14 #include "winbase.h"
15 #include "commctrl.h"
16 #include "datetime.h"
17 #include "monthcal.h"
18 #include "debugtools.h"
20 #include <math.h>
21 #include <string.h>
23 DEFAULT_DEBUG_CHANNEL(datetime)
26 #define DATETIME_GetInfoPtr(hwnd) ((DATETIME_INFO *)GetWindowLongA (hwnd, 0))
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};
37 static LRESULT
38 DATETIME_GetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
40 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
41 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
42 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
44 if (!lParam) return GDT_NONE;
46 if ((dwStyle & DTS_SHOWNONE) &&
47 (SendMessageA (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0)))
48 return GDT_NONE;
50 MONTHCAL_CopyTime (&infoPtr->date, lprgSysTimeArray);
52 return GDT_VALID;
56 static LRESULT
57 DATETIME_SetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
59 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
60 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
62 if (!lParam) return 0;
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);
70 return 1;
74 static LRESULT
75 DATETIME_GetMonthCalColor (HWND hwnd, WPARAM wParam)
77 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
79 return SendMessageA (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
82 static LRESULT
83 DATETIME_SetMonthCalColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
85 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
87 return SendMessageA (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
91 /* FIXME: need to get way to force font into monthcal structure */
93 static LRESULT
94 DATETIME_GetMonthCal (HWND hwnd)
96 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
98 return infoPtr->hMonthCal;
103 /* FIXME: need to get way to force font into monthcal structure */
105 static LRESULT
106 DATETIME_GetMonthCalFont (HWND hwnd)
109 return 0;
112 static LRESULT
113 DATETIME_SetMonthCalFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
116 return 0;
122 Split up a formattxt in actions.
123 See ms documentation for the meaning of the letter codes/'specifiers'.
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'.
135 static void
136 DATETIME_UseFormat (DATETIME_INFO *infoPtr, const char *formattxt)
138 int i,j,k,len;
139 int *nrFields=& infoPtr->nrFields;
141 TRACE ("%s\n",formattxt);
144 *nrFields=0;
145 infoPtr->fieldspec[*nrFields]=0;
146 len=strlen(allowedformatchars);
147 k=0;
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;
159 if (infoPtr->fieldspec[*nrFields]>>4!=j) {
160 (*nrFields)++;
161 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
162 break;
164 if ((infoPtr->fieldspec[*nrFields] & 0x0f)==maxrepetition[j]) {
165 (*nrFields)++;
166 infoPtr->fieldspec[*nrFields]=(j<<4) +1;
167 break;
169 infoPtr->fieldspec[*nrFields]++;
170 break;
171 } /* if allowedformatchar */
172 } /* for j */
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;
186 infoPtr->textbuf[k]=formattxt[i];
187 k++;
188 infoPtr->buflen[*nrFields]++;
189 } /* if j=len */
191 if (*nrFields==infoPtr->nrFieldsAllocated) {
192 FIXME ("out of memory; should reallocate. crash ahead.\n");
195 } /* for i */
197 if (infoPtr->fieldspec[*nrFields]!=0) (*nrFields)++;
200 static LRESULT
201 DATETIME_SetFormat (HWND hwnd, WPARAM wParam, LPARAM lParam)
204 DATETIME_INFO *infoPtr= DATETIME_GetInfoPtr (hwnd);
206 if (!lParam) {
207 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
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");
216 else
217 DATETIME_UseFormat (infoPtr, (char *) lParam);
219 return infoPtr->nrFields;
222 static LRESULT
223 DATETIME_SetFormatW (HWND hwnd, WPARAM wParam, LPARAM lParam)
226 if (lParam) {
227 LPSTR buf;
228 int retval;
229 int len = lstrlenW ((LPWSTR) lParam)+1;
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;
237 else
238 return DATETIME_SetFormat (hwnd, 0, 0);
242 static void
243 DATETIME_ReturnTxt (DATETIME_INFO *infoPtr, int count, char *result)
245 SYSTEMTIME date = infoPtr->date;
246 int spec;
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;
255 if (!infoPtr->fieldspec) return;
257 spec=infoPtr->fieldspec[count];
258 if (spec & DT_STRING) {
259 int txtlen=infoPtr->buflen[count];
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;
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;
353 TRACE ("arg%d=%x->[%s]\n",count,infoPtr->fieldspec[count],result);
356 static void
357 DATETIME_IncreaseField (DATETIME_INFO *infoPtr, int number)
359 SYSTEMTIME *date = &infoPtr->date;
360 int spec;
362 TRACE ("%d\n",number);
363 if ((number>infoPtr->nrFields) || (number<0)) return;
365 spec=infoPtr->fieldspec[number];
366 if ((spec & DTHT_DATEFIELD)==0) return;
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;
420 static void
421 DATETIME_DecreaseField (DATETIME_INFO *infoPtr, int number)
423 SYSTEMTIME *date = & infoPtr->date;
424 int spec;
426 TRACE ("%d\n",number);
427 if ((number>infoPtr->nrFields) || (number<0)) return;
429 spec=infoPtr->fieldspec[number];
430 if ((spec & DTHT_DATEFIELD)==0) return;
432 TRACE ("%x\n",spec);
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;
496 static void
497 DATETIME_ResetFieldDown (DATETIME_INFO *infoPtr, int number)
499 SYSTEMTIME *date = &infoPtr->date;
500 int spec;
502 TRACE ("%d\n",number);
503 if ((number>infoPtr->nrFields) || (number<0)) return;
505 spec=infoPtr->fieldspec[number];
506 if ((spec & DTHT_DATEFIELD)==0) return;
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;
554 static void
555 DATETIME_ResetFieldUp (DATETIME_INFO *infoPtr, int number)
557 SYSTEMTIME *date = & infoPtr->date;
558 int spec;
560 if ((number>infoPtr->nrFields) || (number<0)) return;
562 spec=infoPtr->fieldspec[number];
563 if ((spec & DTHT_DATEFIELD)==0) return;
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;
606 static void DATETIME_Refresh (HWND hwnd, HDC hdc)
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;
623 if (infoPtr->dateValid) {
624 char txt[80];
625 HFONT oldFont;
626 oldFont = SelectObject (hdc, infoPtr->hFont);
628 GetClientRect (hwnd, rect);
629 hbr = CreateSolidBrush (GetSysColor (COLOR_WINDOW));
630 FillRect(hdc, rect, hbr);
631 DeleteObject(hbr);
634 DATETIME_ReturnTxt (infoPtr, 0, txt);
635 GetTextExtentPoint32A (hdc, txt, strlen (txt), &size);
636 rect->bottom=size.cy+2;
638 checkbox->left = 0;
639 checkbox->right = 0;
640 checkbox->top = rect->top;
641 checkbox->bottom= rect->bottom;
643 if (dwStyle & DTS_SHOWNONE) checkbox->right=18;
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;
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);
667 else
668 DrawTextA ( hdc, txt, strlen(txt), field,
669 DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
672 SelectObject (hdc, oldFont);
675 if (!(dwStyle & DTS_UPDOWN)) {
677 calbutton->right = rect->right;
678 calbutton->left = rect->right-15;
679 calbutton->top = rect->top;
680 calbutton->bottom= rect->bottom;
682 DrawFrameControl(hdc, calbutton, DFC_SCROLL,
683 DFCS_SCROLLDOWN | (prssed ? DFCS_PUSHED : 0) |
684 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
689 static LRESULT
690 DATETIME_HitTest (HWND hwnd, DATETIME_INFO *infoPtr, POINT pt)
692 int i,retval;
694 TRACE ("%ld, %ld\n",pt.x,pt.y);
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; }
700 for (i=0; i<infoPtr->nrFields; i++) {
701 if (PtInRect (&infoPtr->fieldRect[i], pt)) {
702 retval=i;
703 break;
707 done:
708 TRACE ("%x\n",retval);
709 return retval;
712 static LRESULT
713 DATETIME_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
715 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
716 POINT pt;
717 int old,new;
719 TRACE ("\n");
721 old=infoPtr->select;
722 pt.x=(INT)LOWORD(lParam);
723 pt.y=(INT)HIWORD(lParam);
725 new=DATETIME_HitTest (hwnd, infoPtr, pt);
727 if ((new & DT_STRING)==0) infoPtr->select=new;
729 if (infoPtr->select!=old) {
730 HDC hdc;
732 SetFocus (hwnd);
733 hdc=GetDC (hwnd);
734 DATETIME_Refresh (hwnd,hdc);
735 infoPtr->haveFocus=DTHT_GOTFOCUS;
737 ReleaseDC (hwnd, hdc);
740 if (infoPtr->select==DTHT_MCPOPUP) {
741 POINT pt;
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);
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)));
758 SetFocus (hwnd);
759 DATETIME_SendSimpleNotify (hwnd, DTN_DROPDOWN);
760 return 0;
763 return 0;
767 static LRESULT
768 DATETIME_Paint (HWND hwnd, WPARAM wParam)
770 HDC hdc;
771 PAINTSTRUCT ps;
773 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
774 DATETIME_Refresh (hwnd, hdc);
775 if(!wParam)
776 EndPaint (hwnd, &ps);
777 return 0;
780 static LRESULT
781 DATETIME_ParentNotify (HWND hwnd, WPARAM wParam, LPARAM lParam)
783 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
784 LPNMHDR lpnmh=(LPNMHDR) lParam;
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;
792 static LRESULT
793 DATETIME_Notify (HWND hwnd, WPARAM wParam, LPARAM lParam)
796 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
797 LPNMHDR lpnmh=(LPNMHDR) lParam;
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;
807 static LRESULT
808 DATETIME_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
810 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
811 HDC hdc;
812 int FieldNum,wrap=0;
814 TRACE("%x %lx %x\n",wParam, lParam, infoPtr->select);
816 FieldNum=infoPtr->select & DTHT_DATEFIELD;
818 if (!(infoPtr->haveFocus)) return 0;
819 if ((FieldNum==0) && (infoPtr->select)) return 0;
821 if (infoPtr->select & FORMATCALLMASK) {
822 FIXME ("Callbacks not implemented yet\n");
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--;
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++;
862 while ((infoPtr->fieldspec[infoPtr->select] & DT_STRING) && (wrap<2));
863 break;
866 hdc = GetDC (hwnd);
867 DATETIME_Refresh (hwnd, hdc);
868 ReleaseDC (hwnd, hdc);
870 return 0;
874 static LRESULT
875 DATETIME_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
877 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
878 HDC hdc;
880 TRACE ("\n");
882 if (infoPtr->haveFocus) {
883 DATETIME_SendSimpleNotify (hwnd, NM_KILLFOCUS);
884 infoPtr->haveFocus = 0;
886 hdc = GetDC (hwnd);
887 DATETIME_Refresh (hwnd, hdc);
888 ReleaseDC (hwnd, hdc);
889 InvalidateRect (hwnd, NULL, TRUE);
891 return 0;
895 static LRESULT
896 DATETIME_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
898 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
899 HDC hdc;
901 TRACE ("\n");
903 if (infoPtr->haveFocus==0) {
904 DATETIME_SendSimpleNotify (hwnd, NM_SETFOCUS);
905 infoPtr->haveFocus=DTHT_GOTFOCUS;
907 hdc = GetDC (hwnd);
908 DATETIME_Refresh (hwnd, hdc);
909 ReleaseDC (hwnd, hdc);
911 return 0;
915 static BOOL
916 DATETIME_SendDateTimeChangeNotify (HWND hwnd)
919 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
920 NMDATETIMECHANGE dtdtc;
922 TRACE ("\n");
923 dtdtc.nmhdr.hwndFrom = hwnd;
924 dtdtc.nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
925 dtdtc.nmhdr.code = DTN_DATETIMECHANGE;
927 if ((GetWindowLongA (hwnd, GWL_STYLE) & DTS_SHOWNONE))
928 dtdtc.dwFlags = GDT_NONE;
929 else
930 dtdtc.dwFlags = GDT_VALID;
932 MONTHCAL_CopyTime (&infoPtr->date, &dtdtc.st);
933 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
934 (WPARAM)dtdtc.nmhdr.idFrom, (LPARAM)&dtdtc);
938 static BOOL
939 DATETIME_SendSimpleNotify (HWND hwnd, UINT code)
941 NMHDR nmhdr;
943 TRACE("%x\n",code);
944 nmhdr.hwndFrom = hwnd;
945 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
946 nmhdr.code = code;
948 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
949 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
957 static LRESULT
958 DATETIME_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
960 DATETIME_INFO *infoPtr;
961 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
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;
970 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
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);
981 if (dwStyle & DTS_UPDOWN) {
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);
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;
995 DATETIME_SetFormat (hwnd, 0, 0);
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;
1006 static LRESULT
1007 DATETIME_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1009 DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
1011 COMCTL32_Free (infoPtr);
1012 return 0;
1019 static LRESULT WINAPI
1020 DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1023 switch (uMsg)
1026 case DTM_GETSYSTEMTIME:
1027 DATETIME_GetSystemTime (hwnd, wParam, lParam);
1029 case DTM_SETSYSTEMTIME:
1030 DATETIME_SetSystemTime (hwnd, wParam, lParam);
1032 case DTM_GETRANGE:
1033 FIXME("Unimplemented msg DTM_GETRANGE\n");
1034 return 0;
1036 case DTM_SETRANGE:
1037 FIXME("Unimplemented msg DTM_SETRANGE\n");
1038 return 1;
1040 case DTM_SETFORMATA:
1041 return DATETIME_SetFormat (hwnd, wParam, lParam);
1043 case DTM_SETFORMATW:
1044 return DATETIME_SetFormatW (hwnd, wParam, lParam);
1046 case DTM_SETMCCOLOR:
1047 return DATETIME_SetMonthCalColor (hwnd, wParam, lParam);
1049 case DTM_GETMCCOLOR:
1050 return DATETIME_GetMonthCalColor (hwnd, wParam);
1052 case DTM_GETMONTHCAL:
1053 return DATETIME_GetMonthCal (hwnd);
1055 case DTM_SETMCFONT:
1056 return DATETIME_SetMonthCalFont (hwnd, wParam, lParam);
1058 case DTM_GETMCFONT:
1059 return DATETIME_GetMonthCalFont (hwnd);
1061 case WM_PARENTNOTIFY:
1062 return DATETIME_ParentNotify (hwnd, wParam, lParam);
1064 case WM_NOTIFY:
1065 return DATETIME_Notify (hwnd, wParam, lParam);
1067 case WM_GETDLGCODE:
1068 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1070 case WM_PAINT:
1071 return DATETIME_Paint (hwnd, wParam);
1073 case WM_KEYDOWN:
1074 return DATETIME_KeyDown (hwnd, wParam, lParam);
1076 case WM_KILLFOCUS:
1077 return DATETIME_KillFocus (hwnd, wParam, lParam);
1079 case WM_SETFOCUS:
1080 return DATETIME_SetFocus (hwnd, wParam, lParam);
1082 case WM_LBUTTONDOWN:
1083 return DATETIME_LButtonDown (hwnd, wParam, lParam);
1085 case WM_CREATE:
1086 return DATETIME_Create (hwnd, wParam, lParam);
1088 case WM_DESTROY:
1089 return DATETIME_Destroy (hwnd, wParam, lParam);
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);
1097 return 0;
1101 VOID
1102 DATETIME_Register (void)
1104 WNDCLASSA wndClass;
1106 if (GlobalFindAtomA (DATETIMEPICK_CLASSA)) return;
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;
1117 RegisterClassA (&wndClass);
1121 VOID
1122 DATETIME_Unregister (void)
1124 if (GlobalFindAtomA (DATETIMEPICK_CLASSA))
1125 UnregisterClassA (DATETIMEPICK_CLASSA, (HINSTANCE)NULL);