10 #include <WINNT/dialog.h>
11 #include <WINNT/resize.h> // for GetRectInParent()
12 #include <WINNT/subclass.h>
13 #include <WINNT/ctl_spinner.h>
14 #include <WINNT/ctl_date.h>
15 #include <WINNT/TaLocale.h>
18 #define cchRESOURCE 256
23 * MISCELLANEOUS ______________________________________________________________
28 #define REALLOC(_a,_c,_r,_i) DateReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
29 BOOL DateReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
34 if (cReq <= *pcTarget)
37 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
40 if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
42 memset (pNew, 0x00, cbElement * cNew);
46 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
58 * DATE _______________________________________________________________________
65 HWND hFirst; // one of hYear, hMonth, hDay
78 SYSTEMTIME dateNow; // only wYear,wMonth,wDay fields are relevant
80 DWORD dwFormat; // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D
84 static CRITICAL_SECTION csDate;
85 static DateInfo *aDate = NULL;
86 static size_t cDate = 0;
88 #define cszDATECLASS TEXT("Date")
90 BOOL CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp);
91 BOOL CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp);
92 BOOL CALLBACK DateDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
94 void Date_SendCallback (DateInfo *pdi, WORD eln, LPARAM lp);
96 void Date_OnCreate (DateInfo *pdi);
97 void Date_OnDestroy (DateInfo *pdi);
98 void Date_OnButtonDown (DateInfo *pdi, UINT msg, WPARAM wp, LPARAM lp);
99 BOOL Date_OnGetDate (DateInfo *pdi, WPARAM wp, LPARAM lp);
100 BOOL Date_OnSetDate (DateInfo *pdi, WPARAM wp, LPARAM lp);
102 void Date_Edit_OnSetFocus (DateInfo *pdi, HWND hEdit);
103 void Date_Edit_OnUpdate (DateInfo *pdi, HWND hEdit);
104 void Date_Edit_SetText (DateInfo *pdi, HWND hEdit);
107 BOOL RegisterDateClass (void)
109 static BOOL fRegistered = FALSE;
113 InitializeCriticalSection (&csDate);
116 memset (&wc, 0x00, sizeof(wc));
117 wc.style = CS_GLOBALCLASS;
118 wc.lpfnWndProc = (WNDPROC)DateProc;
119 wc.hInstance = THIS_HINST;
120 wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
121 wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
122 wc.lpszClassName = cszDATECLASS;
124 if (RegisterClass (&wc))
132 void Date_SendCallback (DateInfo *pdi, WORD eln, LPARAM lp)
134 if (!pdi->fCallingBack)
136 pdi->fCallingBack = TRUE;
138 SendMessage (GetParent (pdi->hDate),
140 MAKELONG ((WORD)GetWindowLong (pdi->hDate, GWL_ID), eln),
143 pdi->fCallingBack = FALSE;
148 BOOL CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp)
150 DateInfo *pdi = NULL;
152 EnterCriticalSection (&csDate);
154 if (msg == WM_CREATE)
156 for (size_t iDate = 0; iDate < cDate; ++iDate)
158 if (aDate[ iDate ].hDate == NULL)
163 if (!REALLOC (aDate, cDate, 1+iDate, 4))
167 memset (&aDate[ iDate ], 0x00, sizeof(DateInfo));
168 aDate[ iDate ].hDate = hDate;
170 pdi = &aDate[ iDate ];
174 for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
176 if (aDate[ iDate ].hDate == hDate)
177 pdi = &aDate[ iDate ];
181 LeaveCriticalSection (&csDate);
192 Date_OnDestroy (pdi);
197 Date_OnButtonDown (pdi, msg, wp, lp);
201 PostMessage (GetParent(hDate), WM_NEXTDLGCTL, (WPARAM)pdi->hFirst, TRUE);
205 EnableWindow (pdi->hYear, IsWindowEnabled (hDate));
206 EnableWindow (pdi->hSep1, IsWindowEnabled (hDate));
207 EnableWindow (pdi->hMonth, IsWindowEnabled (hDate));
208 EnableWindow (pdi->hSep2, IsWindowEnabled (hDate));
209 EnableWindow (pdi->hDay, IsWindowEnabled (hDate));
212 GetRectInParent (hDate, &rDate);
213 InvalidateRect (GetParent(hDate), &rDate, TRUE);
214 UpdateWindow (GetParent(hDate));
222 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)pdi->hSpinner);
226 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)pdi->hSpinner);
230 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)pdi->hSpinner);
234 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)pdi->hSpinner);
238 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)pdi->hSpinner);
242 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)pdi->hSpinner);
248 return Date_OnGetDate (pdi, wp, lp);
251 return Date_OnSetDate (pdi, wp, lp);
255 return DefWindowProc (hDate, msg, wp, lp);
259 void Date_OnCreate (DateInfo *pdi)
261 Subclass_AddHook (GetParent(pdi->hDate), DateDlgProc);
263 TCHAR szDateSep[ cchRESOURCE ];
264 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SDATE, szDateSep, cchRESOURCE))
265 lstrcpy (szDateSep, TEXT("/"));
267 TCHAR szDateFormat[ cchRESOURCE ];
268 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_IDATE, szDateFormat, cchRESOURCE))
269 pdi->dwFormat = 0; // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D
271 pdi->dwFormat = atoi(szDateFormat);
274 GetClientRect (pdi->hDate, &rDate);
276 POINT ptDate = {0,0};
277 ClientToScreen (pdi->hDate, &ptDate);
278 ScreenToClient (GetParent (pdi->hDate), &ptDate);
280 SIZE s88; // size of widest likely double-digit number
281 SIZE sDateSep; // size of ":"
283 HDC hdc = GetDC (GetParent (pdi->hDate));
284 GetTextExtentPoint (hdc, TEXT("88"), lstrlen(TEXT("88")), &s88);
285 GetTextExtentPoint (hdc, szDateSep, lstrlen(szDateSep), &sDateSep);
286 sDateSep.cx ++; // we have to disable this; that draws slightly wider
287 ReleaseDC (GetParent (pdi->hDate), hdc);
289 LONG cxNumbers = cxRECT(rDate) - GetSystemMetrics(SM_CXVSCROLL) - (sDateSep.cx)*2;
290 LONG cxYear = max( cxNumbers/2, s88.cx*2 );
291 LONG cxMonth = max( cxNumbers/4, s88.cx );
292 LONG cxDay = max( cxNumbers/4, s88.cx );
294 cxYear = min (cxYear, (LONG)( 1.2 * s88.cx * 2 ));
295 cxMonth = min (cxMonth, (LONG)( 1.2 * s88.cx ));
296 cxDay = min (cxDay, (LONG)( 1.2 * s88.cx )); // don't be TOO wide.
298 pdi->idYear = 100+NextControlID (GetParent (pdi->hDate));
299 pdi->idMonth = pdi->idYear +1;
300 pdi->idDay = pdi->idYear +2;
304 LONG cy = cyRECT(rDate);
311 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=M,1=D,2=Y
313 switch (pdi->dwFormat)
315 case 0: cx = cxMonth; id = pdi->idMonth; break;
316 case 1: cx = cxDay; id = pdi->idDay; break;
317 case 2: cx = cxYear; id = pdi->idYear; break;
320 hWnd = CreateWindow (
323 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
325 GetParent(pdi->hDate),
331 switch (pdi->dwFormat)
333 case 0: pdi->hMonth = hWnd; iiFirst = pdi->dateNow.wMonth; break;
334 case 1: pdi->hDay = hWnd; iiFirst = pdi->dateNow.wDay; break;
335 case 2: pdi->hYear = hWnd; iiFirst = pdi->dateNow.wYear; break;
339 // Create a separator
341 pdi->hSep1 = CreateWindow (
344 WS_CHILD | SS_CENTER,
345 xx, yy, sDateSep.cx, cy,
346 GetParent(pdi->hDate),
352 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=D,1=M,2=M
354 switch (pdi->dwFormat)
356 case 0: cx = cxDay; id = pdi->idDay; break;
357 case 1: cx = cxMonth; id = pdi->idMonth; break;
358 case 2: cx = cxMonth; id = pdi->idMonth; break;
361 hWnd = CreateWindow (
364 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
366 GetParent(pdi->hDate),
372 switch (pdi->dwFormat)
374 case 0: pdi->hDay = hWnd; break;
375 case 1: pdi->hMonth = hWnd; break;
376 case 2: pdi->hMonth = hWnd; break;
379 // Create a separator
381 pdi->hSep2 = CreateWindow (
384 WS_CHILD | SS_CENTER,
385 xx, yy, sDateSep.cx, cy,
386 GetParent(pdi->hDate),
392 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=Y,1=Y,2=D
394 switch (pdi->dwFormat)
396 case 0: cx = cxYear; id = pdi->idYear; break;
397 case 1: cx = cxYear; id = pdi->idYear; break;
398 case 2: cx = cxDay; id = pdi->idDay; break;
401 hWnd = CreateWindow (
404 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
406 GetParent(pdi->hDate),
412 switch (pdi->dwFormat)
414 case 0: pdi->hYear = hWnd; break;
415 case 1: pdi->hYear = hWnd; break;
416 case 2: pdi->hDay = hWnd; break;
419 // Subclass the edit controls
421 Subclass_AddHook (pdi->hYear, DateEditProc);
422 Subclass_AddHook (pdi->hMonth, DateEditProc);
423 Subclass_AddHook (pdi->hDay, DateEditProc);
425 HFONT hf = (HFONT)SendMessage (GetParent (pdi->hDate), WM_GETFONT, 0, 0);
426 SendMessage (pdi->hYear, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
427 SendMessage (pdi->hSep1, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
428 SendMessage (pdi->hMonth, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
429 SendMessage (pdi->hSep2, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
430 SendMessage (pdi->hDay, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
432 EnableWindow (pdi->hYear, IsWindowEnabled (pdi->hDate));
433 EnableWindow (pdi->hSep1, IsWindowEnabled (pdi->hDate));
434 EnableWindow (pdi->hMonth, IsWindowEnabled (pdi->hDate));
435 EnableWindow (pdi->hSep2, IsWindowEnabled (pdi->hDate));
436 EnableWindow (pdi->hDay, IsWindowEnabled (pdi->hDate));
438 ShowWindow (pdi->hYear, TRUE);
439 ShowWindow (pdi->hSep1, TRUE);
440 ShowWindow (pdi->hMonth, TRUE);
441 ShowWindow (pdi->hSep2, TRUE);
442 ShowWindow (pdi->hDay, TRUE);
445 GetWindowRect (pdi->hDate, &rWindow);
446 rWindow.right += (cxYear + cxMonth + cxDay + sDateSep.cx*2) - cxRECT(rDate);
448 SetWindowPos (pdi->hDate, NULL, 0, 0, cxRECT(rWindow), cyRECT(rWindow),
449 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
452 GetRectInParent (pdi->hDate, &rSpinner);
453 rSpinner.left = rSpinner.right;
454 rSpinner.right = rSpinner.left + GetSystemMetrics (SM_CXVSCROLL);
455 rSpinner.bottom -= 2; // just like Win95 does
456 CreateSpinner (pdi->hFirst, 10, FALSE, 0, iiFirst, 3000, &rSpinner);
457 pdi->hSpinner = SP_GetSpinner (pdi->hFirst);
458 pdi->hSpinnerBuddy = pdi->hFirst;
460 Date_Edit_SetText (pdi, pdi->hYear);
461 Date_Edit_SetText (pdi, pdi->hMonth);
462 Date_Edit_SetText (pdi, pdi->hDay);
463 Date_Edit_OnSetFocus (pdi, pdi->hFirst);
467 void Date_OnDestroy (DateInfo *pdi)
469 Subclass_RemoveHook (GetParent (pdi->hDate), DateDlgProc);
474 void Date_OnButtonDown (DateInfo *pdi, UINT msg, WPARAM wp, LPARAM lp)
476 DWORD dw = GetMessagePos();
479 pt.y = HIWORD(dw); // in screen coordinates
484 GetWindowRect (pdi->hYear, &rTarget);
485 if (PtInRect (&rTarget, pt))
486 hTarget = pdi->hYear;
488 GetWindowRect (pdi->hMonth, &rTarget);
489 if (PtInRect (&rTarget, pt))
490 hTarget = pdi->hMonth;
492 GetWindowRect (pdi->hDay, &rTarget);
493 if (PtInRect (&rTarget, pt))
498 PostMessage (hTarget, msg, wp, lp);
503 BOOL Date_OnGetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
505 SYSTEMTIME *pdate = (SYSTEMTIME*)lp;
506 pdate->wYear = pdi->dateNow.wYear;
507 pdate->wMonth = pdi->dateNow.wMonth;
508 pdate->wDayOfWeek = pdi->dateNow.wDayOfWeek;
509 pdate->wDay = pdi->dateNow.wDay;
514 BOOL Date_OnSetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
516 SYSTEMTIME *pdate = (SYSTEMTIME*)lp;
517 pdi->dateNow = *pdate;
518 Date_Edit_SetText (pdi, pdi->hYear);
519 Date_Edit_SetText (pdi, pdi->hMonth);
520 Date_Edit_SetText (pdi, pdi->hDay);
525 BOOL CALLBACK DateDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
527 PVOID oldProc = Subclass_FindNextHook (hDlg, DateDlgProc);
532 case WM_CTLCOLOREDIT:
533 case WM_CTLCOLORSTATIC:
534 for (iDate = 0; iDate < cDate; ++iDate)
536 if (aDate[ iDate ].hDate == NULL)
538 if ( (aDate[ iDate ].hYear == (HWND)lp) ||
539 (aDate[ iDate ].hSep1 == (HWND)lp) ||
540 (aDate[ iDate ].hMonth == (HWND)lp) ||
541 (aDate[ iDate ].hSep2 == (HWND)lp) ||
542 (aDate[ iDate ].hDay == (HWND)lp) )
545 if (IsWindowEnabled (aDate[ iDate ].hDate))
546 clr = GetSysColor (COLOR_WINDOW);
548 clr = GetSysColor (COLOR_BTNFACE);
549 SetBkColor ((HDC)wp, clr);
550 return (BOOL)CreateSolidBrush (clr);
556 for (iDate = 0; iDate < cDate; ++iDate)
558 if (aDate[ iDate ].hDate == NULL)
560 if ( (aDate[ iDate ].idYear == LOWORD(wp)) ||
561 (aDate[ iDate ].idMonth == LOWORD(wp)) ||
562 (aDate[ iDate ].idDay == LOWORD(wp)) )
564 if (HIWORD(wp) == SPN_UPDATE)
566 Date_Edit_OnUpdate (&aDate[ iDate ], GetDlgItem(hDlg,LOWORD(wp)));
575 return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
577 return DefWindowProc (hDlg, msg, wp, lp);
581 BOOL CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
583 DateInfo *pdi = NULL;
585 EnterCriticalSection (&csDate);
587 for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
589 if ( (aDate[ iDate ].hYear == hEdit) ||
590 (aDate[ iDate ].hMonth == hEdit) ||
591 (aDate[ iDate ].hDay == hEdit) )
593 pdi = &aDate[ iDate ];
597 LeaveCriticalSection (&csDate);
604 Date_Edit_OnSetFocus (pdi, hEdit);
609 PVOID oldProc = Subclass_FindNextHook (hEdit, DateEditProc);
611 return CallWindowProc ((WNDPROC)oldProc, hEdit, msg, wp, lp);
613 return DefWindowProc (hEdit, msg, wp, lp);
617 void Date_Edit_OnSetFocus (DateInfo *pdi, HWND hEdit)
623 if (hEdit == pdi->hYear)
626 dwNow = pdi->dateNow.wYear;
629 else if (hEdit == pdi->hMonth)
632 dwNow = pdi->dateNow.wMonth;
635 else // (hEdit == pdi->hDay)
638 dwNow = pdi->dateNow.wDay;
642 if (pdi->hSpinnerBuddy != hEdit)
644 SP_SetBuddy (pdi->hSpinnerBuddy, hEdit, FALSE);
645 pdi->hSpinnerBuddy = hEdit;
648 SP_SetRange (hEdit, dwMin, dwMax);
649 SP_SetPos (hEdit, dwNow);
651 SendMessage (hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1); // select all
655 void Date_Edit_OnUpdate (DateInfo *pdi, HWND hEdit)
657 TCHAR szText[ cchRESOURCE ];
659 if (hEdit == pdi->hYear)
661 GetWindowText (pdi->hYear, szText, 256);
662 pdi->dateNow.wYear = (WORD)atol (szText);
665 if (hEdit == pdi->hMonth)
667 GetWindowText (pdi->hMonth, szText, 256);
668 pdi->dateNow.wMonth = (WORD)atol (szText);
671 if (hEdit == pdi->hDay)
673 GetWindowText (pdi->hDay, szText, 256);
674 pdi->dateNow.wDay = (WORD)atol (szText);
677 SYSTEMTIME st = pdi->dateNow;
678 Date_SendCallback (pdi, DN_UPDATE, (LPARAM)&st);
682 void Date_Edit_SetText (DateInfo *pdi, HWND hEdit)
686 if (hEdit == pdi->hYear)
687 dwNow = pdi->dateNow.wYear;
688 else if (hEdit == pdi->hMonth)
689 dwNow = pdi->dateNow.wMonth;
690 else if (hEdit == pdi->hDay)
691 dwNow = pdi->dateNow.wDay;
693 if (pdi->hSpinnerBuddy == hEdit)
695 SP_SetPos (hEdit, dwNow);
699 TCHAR szText[ cchRESOURCE ];
700 wsprintf (szText, TEXT("%lu"), dwNow);
701 SetWindowText (hEdit, szText);