Added the functionality of the thumb track.
[wine] / dlls / comctl32 / datetime.c
1 /*
2  * Date and time picker control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1999 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 DEFAULT_DEBUG_CHANNEL(datetime)
21
22
23
24 #define DATETIME_GetInfoPtr(hwnd) ((DATETIME_INFO *)GetWindowLongA (hwnd, 0))
25 static BOOL
26
27 DATETIME_SendSimpleNotify (HWND hwnd, UINT code);
28
29 extern char *days[];  /* from ole/parsedt.c */
30
31
32 static LRESULT
33 DATETIME_GetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
34 {
35   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
36   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
37   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
38
39   if (!lParam) return GDT_NONE;
40
41   if ((dwStyle & DTS_SHOWNONE) && 
42        (SendMessageA (infoPtr->hwndCheckbut, BM_GETCHECK, 0, 0)))
43         return GDT_NONE;
44
45   MONTHCAL_CopyTime (&infoPtr->date, lprgSysTimeArray);
46
47   return GDT_VALID;
48 }
49
50
51 static LRESULT
52 DATETIME_SetSystemTime (HWND hwnd, WPARAM wParam, LPARAM lParam )
53 {
54   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
55   SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
56
57   if (!lParam) return 0;
58
59   if (lParam==GDT_VALID) 
60         MONTHCAL_CopyTime (lprgSysTimeArray, &infoPtr->date);
61   if (lParam==GDT_NONE) {
62         infoPtr->dateValid=FALSE;
63     SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, 0, 0);
64         }
65   return 1;
66 }
67
68
69 static LRESULT
70 DATETIME_GetMonthCalColor (HWND hwnd, WPARAM wParam)
71 {
72   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
73
74   return SendMessageA (infoPtr->hMonthCal, MCM_GETCOLOR, wParam, 0);
75 }
76
77 static LRESULT
78 DATETIME_SetMonthCalColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
79 {
80   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
81
82   return SendMessageA (infoPtr->hMonthCal, MCM_SETCOLOR, wParam, lParam);
83 }
84
85
86 /* FIXME: need to get way to force font into monthcal structure */
87
88 static LRESULT
89 DATETIME_GetMonthCal (HWND hwnd)
90 {
91   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
92
93   return infoPtr->hMonthCal;
94 }
95
96
97
98 /* FIXME: need to get way to force font into monthcal structure */
99
100 static LRESULT
101 DATETIME_GetMonthCalFont (HWND hwnd)
102 {
103
104   return 0;
105 }
106
107 static LRESULT
108 DATETIME_SetMonthCalFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
109 {
110
111   return 0;
112 }
113
114
115 static void DATETIME_Refresh (HWND hwnd, HDC hdc)
116
117 {
118   DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
119   RECT *daytxt   = &infoPtr->daytxt;
120   RECT *daynumtxt= &infoPtr->daynumtxt;
121   RECT *rmonthtxt= &infoPtr->rmonthtxt;
122   RECT *yeartxt  = &infoPtr->yeartxt;
123   RECT *calbutton= &infoPtr->calbutton;
124   RECT *checkbox = &infoPtr->checkbox;
125   RECT *rect     = &infoPtr->rect;
126   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
127   SYSTEMTIME date = infoPtr->date;
128   SIZE size;
129   BOOL prssed=FALSE;
130   COLORREF oldBk;
131   
132
133         
134   if (infoPtr->dateValid) {
135     char txt[80];
136         HFONT oldFont;
137         oldFont = SelectObject (hdc, infoPtr->hFont);
138
139         GetClientRect (hwnd, rect);
140
141         sprintf (txt,"%s,",days[date.wDayOfWeek]);
142         GetTextExtentPoint32A (hdc, txt, strlen (txt), &size);
143         rect->bottom=size.cy+2;
144
145         checkbox->left  = 0;
146         checkbox->right = 0;
147         checkbox->top   = rect->top;
148         checkbox->bottom= rect->bottom;
149     if (dwStyle & DTS_SHOWNONE) {    /* FIXME: draw checkbox */
150                 checkbox->right=18;
151                 }
152
153
154         if (infoPtr->select==(DTHT_DAY|DTHT_GOTFOCUS))
155                 oldBk=SetBkColor (hdc, COLOR_GRAYTEXT);
156        daytxt->left  = checkbox->right;
157        daytxt->right = checkbox->right+size.cx;
158        daytxt->top   = rect->top;
159        daytxt->bottom= rect->bottom;
160        DrawTextA ( hdc, txt, strlen(txt), daytxt,
161                          DT_LEFT | DT_VCENTER | DT_SINGLELINE );
162         if (infoPtr->select==(DTHT_DAY|DTHT_GOTFOCUS)) 
163                 SetBkColor (hdc, oldBk);
164
165         if (infoPtr->select==(DTHT_MONTH|DTHT_GOTFOCUS)) 
166                 oldBk=SetBkColor (hdc, COLOR_GRAYTEXT);
167         strcpy (txt, monthtxt[date.wMonth]);
168         GetTextExtentPoint32A (hdc, "September", 9, &size);
169        rmonthtxt->left  = daytxt->right;
170        rmonthtxt->right = daytxt->right+size.cx;
171         rmonthtxt->top   = rect->top;
172         rmonthtxt->bottom= rect->bottom;
173         DrawTextA ( hdc, txt, strlen(txt), rmonthtxt,
174                          DT_CENTER | DT_VCENTER | DT_SINGLELINE );
175         if (infoPtr->select==(DTHT_MONTH|DTHT_GOTFOCUS)) 
176                 SetBkColor (hdc, oldBk);
177
178         if (infoPtr->select==(DTHT_DAYNUM|DTHT_GOTFOCUS)) 
179                 oldBk=SetBkColor (hdc, COLOR_GRAYTEXT);
180         sprintf (txt,"%d,",date.wDay);
181         GetTextExtentPoint32A (hdc, "31,", 3, &size);
182        daynumtxt->left  = rmonthtxt->right;
183        daynumtxt->right = rmonthtxt->right+size.cx;
184         daynumtxt->top   = rect->top;
185         daynumtxt->bottom= rect->bottom;
186         DrawTextA ( hdc, txt, strlen(txt), daynumtxt,
187                          DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
188         if (infoPtr->select==(DTHT_DAYNUM|DTHT_GOTFOCUS)) 
189                 SetBkColor (hdc, oldBk);
190
191         if (infoPtr->select==(DTHT_YEAR|DTHT_GOTFOCUS)) 
192                 oldBk=SetBkColor (hdc, COLOR_GRAYTEXT);
193         sprintf (txt,"%d",date.wYear);
194         GetTextExtentPoint32A (hdc, "2000", 5, &size);
195        yeartxt->left  = daynumtxt->right;
196        yeartxt->right = daynumtxt->right+size.cx;
197         yeartxt->top   = rect->top;
198         yeartxt->bottom= rect->bottom;
199         DrawTextA ( hdc, txt, strlen(txt), yeartxt,
200                          DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
201         if (infoPtr->select==(DTHT_YEAR|DTHT_GOTFOCUS)) 
202                 SetBkColor (hdc, oldBk);
203         
204         SelectObject (hdc, oldFont);
205         }
206
207         if (!(dwStyle & DTS_UPDOWN)) {
208
209                 calbutton->right = rect->right;
210                 calbutton->left  = rect->right-15;
211                 calbutton->top   = rect->top;
212                 calbutton->bottom= rect->bottom;
213
214         DrawFrameControl(hdc, calbutton, DFC_SCROLL,
215                 DFCS_SCROLLDOWN | (prssed ? DFCS_PUSHED : 0) |
216                 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
217         }
218
219 }
220
221 static LRESULT
222 DATETIME_HitTest (HWND hwnd, DATETIME_INFO *infoPtr, POINT pt)
223 {
224   TRACE ("%ld, %ld\n",pt.x,pt.y);
225
226   if (PtInRect (&infoPtr->calbutton, pt)) return DTHT_MCPOPUP;
227   if (PtInRect (&infoPtr->yeartxt, pt))   return DTHT_YEAR;
228   if (PtInRect (&infoPtr->daynumtxt, pt)) return DTHT_DAYNUM;
229   if (PtInRect (&infoPtr->rmonthtxt, pt)) return DTHT_MONTH;
230   if (PtInRect (&infoPtr->daytxt, pt))    return DTHT_DAY;
231   if (PtInRect (&infoPtr->checkbox, pt))  return DTHT_CHECKBOX;
232
233   return 0;
234 }
235
236 static LRESULT
237 DATETIME_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
238 {
239     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
240         POINT pt;
241         int old;
242
243     TRACE ("\n");
244
245         old=infoPtr->select;
246     pt.x=(INT)LOWORD(lParam);
247     pt.y=(INT)HIWORD(lParam);
248     infoPtr->select=DATETIME_HitTest (hwnd, infoPtr, pt);
249                            
250         if (infoPtr->select!=old) {
251                 HDC hdc;
252
253                 SetFocus (hwnd);
254                 hdc=GetDC (hwnd);
255                 DATETIME_Refresh (hwnd,hdc);
256         ReleaseDC (hwnd, hdc);
257     }
258         if (infoPtr->select==DTHT_MCPOPUP) {
259                         POINT pt;
260
261                         pt.x=8;
262                         pt.y=infoPtr->rect.bottom+5;
263                         ClientToScreen (hwnd, &pt);
264                         infoPtr->hMonthCal=CreateWindowExA (0,"SysMonthCal32", 0, 
265                                 WS_POPUP | WS_BORDER,
266                                 pt.x,pt.y,145,150,
267                                 GetParent (hwnd), 
268                                 0,0,0);
269
270                         TRACE ("dt:%x mc:%x mc parent:%x, desktop:%x, mcpp:%x\n",
271                       hwnd,infoPtr->hMonthCal, 
272                       GetParent (infoPtr->hMonthCal),
273                       GetDesktopWindow (),
274                       GetParent (GetParent (infoPtr->hMonthCal)));
275
276                         SetFocus (hwnd);
277                         DATETIME_SendSimpleNotify (hwnd, DTN_DROPDOWN);
278         }
279     return 0;
280 }
281
282
283 static LRESULT
284 DATETIME_Paint (HWND hwnd, WPARAM wParam)
285 {
286     HDC hdc;
287     PAINTSTRUCT ps;
288
289     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
290     DATETIME_Refresh (hwnd, hdc);
291     if(!wParam)
292     EndPaint (hwnd, &ps);
293     return 0;
294 }
295
296 static LRESULT
297 DATETIME_ParentNotify (HWND hwnd, WPARAM wParam, LPARAM lParam)
298 {
299     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
300         LPNMHDR lpnmh=(LPNMHDR) lParam;
301
302         TRACE ("%x,%lx\n",wParam, lParam);
303         TRACE ("Got notification %x from %x\n", lpnmh->code, lpnmh->hwndFrom);
304         TRACE ("info: %x %x %x\n",hwnd,infoPtr->hMonthCal,infoPtr->hUpdown);
305         return 0;
306 }
307
308 static LRESULT
309 DATETIME_Notify (HWND hwnd, WPARAM wParam, LPARAM lParam)
310
311 {
312     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
313         LPNMHDR lpnmh=(LPNMHDR) lParam;
314
315         TRACE ("%x,%lx\n",wParam, lParam);
316         TRACE ("Got notification %x from %x\n", lpnmh->code, lpnmh->hwndFrom);
317         TRACE ("info: %x %x %x\n",hwnd,infoPtr->hMonthCal,infoPtr->hUpdown);
318         return 0;
319 }
320
321 static LRESULT
322 DATETIME_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
323 {
324     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
325     HDC hdc;
326
327     TRACE ("\n");
328
329         if (infoPtr->select) {
330                         DATETIME_SendSimpleNotify (hwnd, NM_KILLFOCUS);
331                         infoPtr->select&= ~DTHT_GOTFOCUS;
332         }
333     hdc = GetDC (hwnd);
334     DATETIME_Refresh (hwnd, hdc);
335     ReleaseDC (hwnd, hdc);
336     InvalidateRect (hwnd, NULL, TRUE);
337
338     return 0;
339 }
340
341
342 static LRESULT
343 DATETIME_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
344 {
345     HDC hdc;
346     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);        
347
348     TRACE ("\n");
349
350         if (infoPtr->select) {
351                         DATETIME_SendSimpleNotify (hwnd, NM_SETFOCUS);  
352                         infoPtr->select|=DTHT_GOTFOCUS;
353         }
354     hdc = GetDC (hwnd);
355     DATETIME_Refresh (hwnd, hdc);
356     ReleaseDC (hwnd, hdc);
357
358     return 0;
359 }
360
361
362 static BOOL
363 DATETIME_SendSimpleNotify (HWND hwnd, UINT code)
364 {
365     NMHDR nmhdr;
366
367     TRACE("%x\n",code);
368     nmhdr.hwndFrom = hwnd;
369     nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
370     nmhdr.code     = code;
371
372     return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
373                                    (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
374 }
375
376
377
378
379
380
381 static LRESULT
382 DATETIME_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
383 {
384   DATETIME_INFO *infoPtr;
385   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
386
387     /* allocate memory for info structure */
388     infoPtr = (DATETIME_INFO *)COMCTL32_Alloc (sizeof(DATETIME_INFO));
389     if (infoPtr == NULL) {
390         ERR("could not allocate info memory!\n");
391         return 0;
392     }
393
394     SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
395
396         if (dwStyle & DTS_SHOWNONE) {
397                 infoPtr->hwndCheckbut=CreateWindowExA (0,"button", 0, 
398                                 WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 
399                                 2,2,13,13,
400                                 hwnd, 
401                 0, GetWindowLongA  (hwnd, GWL_HINSTANCE), 0);
402                 SendMessageA (infoPtr->hwndCheckbut, BM_SETCHECK, 1, 0);
403         }
404
405         if (dwStyle & DTS_UPDOWN) {
406
407                         infoPtr->hUpdown=CreateUpDownControl (
408                                 WS_CHILD | WS_BORDER | WS_VISIBLE,
409                                 120,1,20,20,
410                                 hwnd,1,0,0,
411                                 UD_MAXVAL, UD_MINVAL, 0);
412         }
413
414     /* initialize info structure */
415         infoPtr->hMonthCal=0;
416         GetSystemTime (&infoPtr->date);
417         infoPtr->dateValid = TRUE;
418         infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
419     return 0;
420 }
421
422
423 static LRESULT
424 DATETIME_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
425 {
426     DATETIME_INFO *infoPtr = DATETIME_GetInfoPtr (hwnd);
427         
428     COMCTL32_Free (infoPtr);
429     return 0;
430 }
431
432
433
434
435
436 static LRESULT WINAPI
437 DATETIME_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
438 {
439
440     switch (uMsg)
441     {
442
443     case DTM_GETSYSTEMTIME:
444                 DATETIME_GetSystemTime (hwnd, wParam, lParam);
445
446     case DTM_SETSYSTEMTIME:
447                 DATETIME_SetSystemTime (hwnd, wParam, lParam);
448
449     case DTM_GETRANGE:
450         FIXME("Unimplemented msg DTM_GETRANGE\n");
451         return 0;
452
453     case DTM_SETRANGE:
454         FIXME("Unimplemented msg DTM_SETRANGE\n");
455         return 1;
456
457     case DTM_SETFORMATA:
458         FIXME("Unimplemented msg DTM_SETFORMAT32A\n");
459         return 1;
460
461     case DTM_SETFORMATW:
462         FIXME("Unimplemented msg DTM_SETFORMAT32W\n");
463         return 1;
464
465     case DTM_SETMCCOLOR:
466         return DATETIME_SetMonthCalColor (hwnd, wParam, lParam);
467
468     case DTM_GETMCCOLOR:
469         return DATETIME_GetMonthCalColor (hwnd, wParam);
470
471     case DTM_GETMONTHCAL:
472         return DATETIME_GetMonthCal (hwnd);
473
474     case DTM_SETMCFONT:
475         return DATETIME_SetMonthCalFont (hwnd, wParam, lParam);
476
477     case DTM_GETMCFONT:
478         return DATETIME_GetMonthCalFont (hwnd);
479
480         case WM_PARENTNOTIFY:
481                 return DATETIME_ParentNotify (hwnd, wParam, lParam);
482
483         case WM_NOTIFY:
484                 return DATETIME_Notify (hwnd, wParam, lParam);
485
486     case WM_PAINT:
487         return DATETIME_Paint (hwnd, wParam);
488
489     case WM_KILLFOCUS:
490         return DATETIME_KillFocus (hwnd, wParam, lParam);
491
492     case WM_SETFOCUS:
493         return DATETIME_SetFocus (hwnd, wParam, lParam);
494
495     case WM_LBUTTONDOWN:
496         return DATETIME_LButtonDown (hwnd, wParam, lParam);
497
498         case WM_CREATE:
499             return DATETIME_Create (hwnd, wParam, lParam);
500
501         case WM_DESTROY:
502             return DATETIME_Destroy (hwnd, wParam, lParam);
503
504         default:
505             if (uMsg >= WM_USER)
506                 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
507                      uMsg, wParam, lParam);
508             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
509     }
510     return 0;
511 }
512
513
514 VOID
515 DATETIME_Register (void)
516 {
517     WNDCLASSA wndClass;
518
519     if (GlobalFindAtomA (DATETIMEPICK_CLASSA)) return;
520
521     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
522     wndClass.style         = CS_GLOBALCLASS;
523     wndClass.lpfnWndProc   = (WNDPROC)DATETIME_WindowProc;
524     wndClass.cbClsExtra    = 0;
525     wndClass.cbWndExtra    = sizeof(DATETIME_INFO *);
526     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
527     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
528     wndClass.lpszClassName = DATETIMEPICK_CLASSA;
529  
530     RegisterClassA (&wndClass);
531 }
532
533
534 VOID
535 DATETIME_Unregister (void)
536 {
537     if (GlobalFindAtomA (DATETIMEPICK_CLASSA))
538         UnregisterClassA (DATETIMEPICK_CLASSA, (HINSTANCE)NULL);
539 }
540