2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 #include <afs/param.h>
19 #include <WINNT/dialog.h>
20 #include <WINNT/resize.h> // for GetRectInParent()
21 #include <WINNT/subclass.h>
22 #include <WINNT/ctl_spinner.h>
23 #include <WINNT/ctl_date.h>
24 #include <WINNT/TaLocale.h>
27 #define cchRESOURCE 256
32 * MISCELLANEOUS ______________________________________________________________
37 #define REALLOC(_a,_c,_r,_i) DateReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
38 BOOL DateReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
43 if (cReq <= *pcTarget)
46 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
49 if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
51 memset (pNew, 0x00, cbElement * cNew);
55 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
67 * DATE _______________________________________________________________________
74 HWND hFirst; // one of hYear, hMonth, hDay
87 SYSTEMTIME dateNow; // only wYear,wMonth,wDay fields are relevant
89 DWORD dwFormat; // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D
93 static CRITICAL_SECTION csDate;
94 static DateInfo *aDate = NULL;
95 static size_t cDate = 0;
97 #define cszDATECLASS TEXT("Date")
99 BOOL CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp);
100 BOOL CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp);
101 BOOL CALLBACK DateDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
103 void Date_SendCallback (DateInfo *pdi, WORD eln, LPARAM lp);
105 void Date_OnCreate (DateInfo *pdi);
106 void Date_OnDestroy (DateInfo *pdi);
107 void Date_OnButtonDown (DateInfo *pdi, UINT msg, WPARAM wp, LPARAM lp);
108 BOOL Date_OnGetDate (DateInfo *pdi, WPARAM wp, LPARAM lp);
109 BOOL Date_OnSetDate (DateInfo *pdi, WPARAM wp, LPARAM lp);
111 void Date_Edit_OnSetFocus (DateInfo *pdi, HWND hEdit);
112 void Date_Edit_OnUpdate (DateInfo *pdi, HWND hEdit);
113 void Date_Edit_SetText (DateInfo *pdi, HWND hEdit);
116 BOOL RegisterDateClass (void)
118 static BOOL fRegistered = FALSE;
122 InitializeCriticalSection (&csDate);
125 memset (&wc, 0x00, sizeof(wc));
126 wc.style = CS_GLOBALCLASS;
127 wc.lpfnWndProc = (WNDPROC)DateProc;
128 wc.hInstance = THIS_HINST;
129 wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
130 wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
131 wc.lpszClassName = cszDATECLASS;
133 if (RegisterClass (&wc))
141 void Date_SendCallback (DateInfo *pdi, WORD eln, LPARAM lp)
143 if (!pdi->fCallingBack)
145 pdi->fCallingBack = TRUE;
147 SendMessage (GetParent (pdi->hDate),
149 MAKELONG ((WORD)GetWindowLong (pdi->hDate, GWL_ID), eln),
152 pdi->fCallingBack = FALSE;
157 BOOL CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp)
159 DateInfo *pdi = NULL;
161 EnterCriticalSection (&csDate);
163 if (msg == WM_CREATE)
165 for (size_t iDate = 0; iDate < cDate; ++iDate)
167 if (aDate[ iDate ].hDate == NULL)
172 if (!REALLOC (aDate, cDate, 1+iDate, 4))
176 memset (&aDate[ iDate ], 0x00, sizeof(DateInfo));
177 aDate[ iDate ].hDate = hDate;
179 pdi = &aDate[ iDate ];
183 for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
185 if (aDate[ iDate ].hDate == hDate)
186 pdi = &aDate[ iDate ];
190 LeaveCriticalSection (&csDate);
201 Date_OnDestroy (pdi);
206 Date_OnButtonDown (pdi, msg, wp, lp);
210 PostMessage (GetParent(hDate), WM_NEXTDLGCTL, (WPARAM)pdi->hFirst, TRUE);
214 EnableWindow (pdi->hYear, IsWindowEnabled (hDate));
215 EnableWindow (pdi->hSep1, IsWindowEnabled (hDate));
216 EnableWindow (pdi->hMonth, IsWindowEnabled (hDate));
217 EnableWindow (pdi->hSep2, IsWindowEnabled (hDate));
218 EnableWindow (pdi->hDay, IsWindowEnabled (hDate));
221 GetRectInParent (hDate, &rDate);
222 InvalidateRect (GetParent(hDate), &rDate, TRUE);
223 UpdateWindow (GetParent(hDate));
231 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)pdi->hSpinner);
235 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)pdi->hSpinner);
239 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)pdi->hSpinner);
243 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)pdi->hSpinner);
247 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)pdi->hSpinner);
251 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)pdi->hSpinner);
257 return Date_OnGetDate (pdi, wp, lp);
260 return Date_OnSetDate (pdi, wp, lp);
264 return DefWindowProc (hDate, msg, wp, lp);
268 void Date_OnCreate (DateInfo *pdi)
270 Subclass_AddHook (GetParent(pdi->hDate), DateDlgProc);
272 TCHAR szDateSep[ cchRESOURCE ];
273 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SDATE, szDateSep, cchRESOURCE))
274 lstrcpy (szDateSep, TEXT("/"));
276 TCHAR szDateFormat[ cchRESOURCE ];
277 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_IDATE, szDateFormat, cchRESOURCE))
278 pdi->dwFormat = 0; // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D
280 pdi->dwFormat = atoi(szDateFormat);
283 GetClientRect (pdi->hDate, &rDate);
285 POINT ptDate = {0,0};
286 ClientToScreen (pdi->hDate, &ptDate);
287 ScreenToClient (GetParent (pdi->hDate), &ptDate);
289 SIZE s88; // size of widest likely double-digit number
290 SIZE sDateSep; // size of ":"
292 HDC hdc = GetDC (GetParent (pdi->hDate));
293 GetTextExtentPoint (hdc, TEXT("88"), lstrlen(TEXT("88")), &s88);
294 GetTextExtentPoint (hdc, szDateSep, lstrlen(szDateSep), &sDateSep);
295 sDateSep.cx ++; // we have to disable this; that draws slightly wider
296 ReleaseDC (GetParent (pdi->hDate), hdc);
298 LONG cxNumbers = cxRECT(rDate) - GetSystemMetrics(SM_CXVSCROLL) - (sDateSep.cx)*2;
299 LONG cxYear = max( cxNumbers/2, s88.cx*2 );
300 LONG cxMonth = max( cxNumbers/4, s88.cx );
301 LONG cxDay = max( cxNumbers/4, s88.cx );
303 cxYear = min (cxYear, (LONG)( 1.2 * s88.cx * 2 ));
304 cxMonth = min (cxMonth, (LONG)( 1.2 * s88.cx ));
305 cxDay = min (cxDay, (LONG)( 1.2 * s88.cx )); // don't be TOO wide.
307 pdi->idYear = 100+NextControlID (GetParent (pdi->hDate));
308 pdi->idMonth = pdi->idYear +1;
309 pdi->idDay = pdi->idYear +2;
313 LONG cy = cyRECT(rDate);
320 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=M,1=D,2=Y
322 switch (pdi->dwFormat)
324 case 0: cx = cxMonth; id = pdi->idMonth; break;
325 case 1: cx = cxDay; id = pdi->idDay; break;
326 case 2: cx = cxYear; id = pdi->idYear; break;
329 hWnd = CreateWindow (
332 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
334 GetParent(pdi->hDate),
340 switch (pdi->dwFormat)
342 case 0: pdi->hMonth = hWnd; iiFirst = pdi->dateNow.wMonth; break;
343 case 1: pdi->hDay = hWnd; iiFirst = pdi->dateNow.wDay; break;
344 case 2: pdi->hYear = hWnd; iiFirst = pdi->dateNow.wYear; break;
348 // Create a separator
350 pdi->hSep1 = CreateWindow (
353 WS_CHILD | SS_CENTER,
354 xx, yy, sDateSep.cx, cy,
355 GetParent(pdi->hDate),
361 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=D,1=M,2=M
363 switch (pdi->dwFormat)
365 case 0: cx = cxDay; id = pdi->idDay; break;
366 case 1: cx = cxMonth; id = pdi->idMonth; break;
367 case 2: cx = cxMonth; id = pdi->idMonth; break;
370 hWnd = CreateWindow (
373 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
375 GetParent(pdi->hDate),
381 switch (pdi->dwFormat)
383 case 0: pdi->hDay = hWnd; break;
384 case 1: pdi->hMonth = hWnd; break;
385 case 2: pdi->hMonth = hWnd; break;
388 // Create a separator
390 pdi->hSep2 = CreateWindow (
393 WS_CHILD | SS_CENTER,
394 xx, yy, sDateSep.cx, cy,
395 GetParent(pdi->hDate),
401 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=Y,1=Y,2=D
403 switch (pdi->dwFormat)
405 case 0: cx = cxYear; id = pdi->idYear; break;
406 case 1: cx = cxYear; id = pdi->idYear; break;
407 case 2: cx = cxDay; id = pdi->idDay; break;
410 hWnd = CreateWindow (
413 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
415 GetParent(pdi->hDate),
421 switch (pdi->dwFormat)
423 case 0: pdi->hYear = hWnd; break;
424 case 1: pdi->hYear = hWnd; break;
425 case 2: pdi->hDay = hWnd; break;
428 // Subclass the edit controls
430 Subclass_AddHook (pdi->hYear, DateEditProc);
431 Subclass_AddHook (pdi->hMonth, DateEditProc);
432 Subclass_AddHook (pdi->hDay, DateEditProc);
434 HFONT hf = (HFONT)SendMessage (GetParent (pdi->hDate), WM_GETFONT, 0, 0);
435 SendMessage (pdi->hYear, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
436 SendMessage (pdi->hSep1, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
437 SendMessage (pdi->hMonth, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
438 SendMessage (pdi->hSep2, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
439 SendMessage (pdi->hDay, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
441 EnableWindow (pdi->hYear, IsWindowEnabled (pdi->hDate));
442 EnableWindow (pdi->hSep1, IsWindowEnabled (pdi->hDate));
443 EnableWindow (pdi->hMonth, IsWindowEnabled (pdi->hDate));
444 EnableWindow (pdi->hSep2, IsWindowEnabled (pdi->hDate));
445 EnableWindow (pdi->hDay, IsWindowEnabled (pdi->hDate));
447 ShowWindow (pdi->hYear, TRUE);
448 ShowWindow (pdi->hSep1, TRUE);
449 ShowWindow (pdi->hMonth, TRUE);
450 ShowWindow (pdi->hSep2, TRUE);
451 ShowWindow (pdi->hDay, TRUE);
454 GetWindowRect (pdi->hDate, &rWindow);
455 rWindow.right += (cxYear + cxMonth + cxDay + sDateSep.cx*2) - cxRECT(rDate);
457 SetWindowPos (pdi->hDate, NULL, 0, 0, cxRECT(rWindow), cyRECT(rWindow),
458 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
461 GetRectInParent (pdi->hDate, &rSpinner);
462 rSpinner.left = rSpinner.right;
463 rSpinner.right = rSpinner.left + GetSystemMetrics (SM_CXVSCROLL);
464 rSpinner.bottom -= 2; // just like Win95 does
465 CreateSpinner (pdi->hFirst, 10, FALSE, 0, iiFirst, 3000, &rSpinner);
466 pdi->hSpinner = SP_GetSpinner (pdi->hFirst);
467 pdi->hSpinnerBuddy = pdi->hFirst;
469 Date_Edit_SetText (pdi, pdi->hYear);
470 Date_Edit_SetText (pdi, pdi->hMonth);
471 Date_Edit_SetText (pdi, pdi->hDay);
472 Date_Edit_OnSetFocus (pdi, pdi->hFirst);
476 void Date_OnDestroy (DateInfo *pdi)
478 Subclass_RemoveHook (GetParent (pdi->hDate), DateDlgProc);
483 void Date_OnButtonDown (DateInfo *pdi, UINT msg, WPARAM wp, LPARAM lp)
485 DWORD dw = GetMessagePos();
488 pt.y = HIWORD(dw); // in screen coordinates
493 GetWindowRect (pdi->hYear, &rTarget);
494 if (PtInRect (&rTarget, pt))
495 hTarget = pdi->hYear;
497 GetWindowRect (pdi->hMonth, &rTarget);
498 if (PtInRect (&rTarget, pt))
499 hTarget = pdi->hMonth;
501 GetWindowRect (pdi->hDay, &rTarget);
502 if (PtInRect (&rTarget, pt))
507 PostMessage (hTarget, msg, wp, lp);
512 BOOL Date_OnGetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
514 SYSTEMTIME *pdate = (SYSTEMTIME*)lp;
515 pdate->wYear = pdi->dateNow.wYear;
516 pdate->wMonth = pdi->dateNow.wMonth;
517 pdate->wDayOfWeek = pdi->dateNow.wDayOfWeek;
518 pdate->wDay = pdi->dateNow.wDay;
523 BOOL Date_OnSetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
525 SYSTEMTIME *pdate = (SYSTEMTIME*)lp;
526 pdi->dateNow = *pdate;
527 Date_Edit_SetText (pdi, pdi->hYear);
528 Date_Edit_SetText (pdi, pdi->hMonth);
529 Date_Edit_SetText (pdi, pdi->hDay);
534 BOOL CALLBACK DateDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
536 PVOID oldProc = Subclass_FindNextHook (hDlg, DateDlgProc);
541 case WM_CTLCOLOREDIT:
542 case WM_CTLCOLORSTATIC:
543 for (iDate = 0; iDate < cDate; ++iDate)
545 if (aDate[ iDate ].hDate == NULL)
547 if ( (aDate[ iDate ].hYear == (HWND)lp) ||
548 (aDate[ iDate ].hSep1 == (HWND)lp) ||
549 (aDate[ iDate ].hMonth == (HWND)lp) ||
550 (aDate[ iDate ].hSep2 == (HWND)lp) ||
551 (aDate[ iDate ].hDay == (HWND)lp) )
554 if (IsWindowEnabled (aDate[ iDate ].hDate))
555 clr = GetSysColor (COLOR_WINDOW);
557 clr = GetSysColor (COLOR_BTNFACE);
558 SetBkColor ((HDC)wp, clr);
559 return (BOOL)CreateSolidBrush (clr);
565 for (iDate = 0; iDate < cDate; ++iDate)
567 if (aDate[ iDate ].hDate == NULL)
569 if ( (aDate[ iDate ].idYear == LOWORD(wp)) ||
570 (aDate[ iDate ].idMonth == LOWORD(wp)) ||
571 (aDate[ iDate ].idDay == LOWORD(wp)) )
573 if (HIWORD(wp) == SPN_UPDATE)
575 Date_Edit_OnUpdate (&aDate[ iDate ], GetDlgItem(hDlg,LOWORD(wp)));
584 return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
586 return DefWindowProc (hDlg, msg, wp, lp);
590 BOOL CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
592 DateInfo *pdi = NULL;
594 EnterCriticalSection (&csDate);
596 for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
598 if ( (aDate[ iDate ].hYear == hEdit) ||
599 (aDate[ iDate ].hMonth == hEdit) ||
600 (aDate[ iDate ].hDay == hEdit) )
602 pdi = &aDate[ iDate ];
606 LeaveCriticalSection (&csDate);
613 Date_Edit_OnSetFocus (pdi, hEdit);
618 PVOID oldProc = Subclass_FindNextHook (hEdit, DateEditProc);
620 return CallWindowProc ((WNDPROC)oldProc, hEdit, msg, wp, lp);
622 return DefWindowProc (hEdit, msg, wp, lp);
626 void Date_Edit_OnSetFocus (DateInfo *pdi, HWND hEdit)
632 if (hEdit == pdi->hYear)
635 dwNow = pdi->dateNow.wYear;
638 else if (hEdit == pdi->hMonth)
641 dwNow = pdi->dateNow.wMonth;
644 else // (hEdit == pdi->hDay)
647 dwNow = pdi->dateNow.wDay;
651 if (pdi->hSpinnerBuddy != hEdit)
653 SP_SetBuddy (pdi->hSpinnerBuddy, hEdit, FALSE);
654 pdi->hSpinnerBuddy = hEdit;
657 SP_SetRange (hEdit, dwMin, dwMax);
658 SP_SetPos (hEdit, dwNow);
660 SendMessage (hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1); // select all
664 void Date_Edit_OnUpdate (DateInfo *pdi, HWND hEdit)
666 TCHAR szText[ cchRESOURCE ];
668 if (hEdit == pdi->hYear)
670 GetWindowText (pdi->hYear, szText, 256);
671 pdi->dateNow.wYear = (WORD)atol (szText);
674 if (hEdit == pdi->hMonth)
676 GetWindowText (pdi->hMonth, szText, 256);
677 pdi->dateNow.wMonth = (WORD)atol (szText);
680 if (hEdit == pdi->hDay)
682 GetWindowText (pdi->hDay, szText, 256);
683 pdi->dateNow.wDay = (WORD)atol (szText);
686 SYSTEMTIME st = pdi->dateNow;
687 Date_SendCallback (pdi, DN_UPDATE, (LPARAM)&st);
691 void Date_Edit_SetText (DateInfo *pdi, HWND hEdit)
695 if (hEdit == pdi->hYear)
696 dwNow = pdi->dateNow.wYear;
697 else if (hEdit == pdi->hMonth)
698 dwNow = pdi->dateNow.wMonth;
699 else if (hEdit == pdi->hDay)
700 dwNow = pdi->dateNow.wDay;
702 if (pdi->hSpinnerBuddy == hEdit)
704 SP_SetPos (hEdit, dwNow);
708 TCHAR szText[ cchRESOURCE ];
709 wsprintf (szText, TEXT("%lu"), dwNow);
710 SetWindowText (hEdit, szText);