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 HRESULT CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp);
100 HRESULT CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp);
101 HRESULT 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 HRESULT CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp)
159 DateInfo *pdi = NULL;
161 EnterCriticalSection (&csDate);
163 if (msg == WM_CREATE)
166 for (iDate = 0; iDate < cDate; ++iDate)
168 if (aDate[ iDate ].hDate == NULL)
173 if (!REALLOC (aDate, cDate, 1+iDate, 4))
177 memset (&aDate[ iDate ], 0x00, sizeof(DateInfo));
178 aDate[ iDate ].hDate = hDate;
180 pdi = &aDate[ iDate ];
184 for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
186 if (aDate[ iDate ].hDate == hDate)
187 pdi = &aDate[ iDate ];
191 LeaveCriticalSection (&csDate);
202 Date_OnDestroy (pdi);
207 Date_OnButtonDown (pdi, msg, wp, lp);
211 PostMessage (GetParent(hDate), WM_NEXTDLGCTL, (WPARAM)pdi->hFirst, TRUE);
215 EnableWindow (pdi->hYear, IsWindowEnabled (hDate));
216 EnableWindow (pdi->hSep1, IsWindowEnabled (hDate));
217 EnableWindow (pdi->hMonth, IsWindowEnabled (hDate));
218 EnableWindow (pdi->hSep2, IsWindowEnabled (hDate));
219 EnableWindow (pdi->hDay, IsWindowEnabled (hDate));
222 GetRectInParent (hDate, &rDate);
223 InvalidateRect (GetParent(hDate), &rDate, TRUE);
224 UpdateWindow (GetParent(hDate));
232 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)pdi->hSpinner);
236 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)pdi->hSpinner);
240 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)pdi->hSpinner);
244 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)pdi->hSpinner);
248 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)pdi->hSpinner);
252 PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)pdi->hSpinner);
258 return Date_OnGetDate (pdi, wp, lp);
261 return Date_OnSetDate (pdi, wp, lp);
265 return DefWindowProc (hDate, msg, wp, lp);
269 void Date_OnCreate (DateInfo *pdi)
271 Subclass_AddHook (GetParent(pdi->hDate), DateDlgProc);
273 TCHAR szDateSep[ cchRESOURCE ];
274 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SDATE, szDateSep, cchRESOURCE))
275 lstrcpy (szDateSep, TEXT("/"));
277 TCHAR szDateFormat[ cchRESOURCE ];
278 if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_IDATE, szDateFormat, cchRESOURCE))
279 pdi->dwFormat = 0; // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D
281 pdi->dwFormat = atoi(szDateFormat);
284 GetClientRect (pdi->hDate, &rDate);
286 POINT ptDate = {0,0};
287 ClientToScreen (pdi->hDate, &ptDate);
288 ScreenToClient (GetParent (pdi->hDate), &ptDate);
290 SIZE s88; // size of widest likely double-digit number
291 SIZE sDateSep; // size of ":"
293 HDC hdc = GetDC (GetParent (pdi->hDate));
294 GetTextExtentPoint (hdc, TEXT("88"), lstrlen(TEXT("88")), &s88);
295 GetTextExtentPoint (hdc, szDateSep, lstrlen(szDateSep), &sDateSep);
296 sDateSep.cx ++; // we have to disable this; that draws slightly wider
297 ReleaseDC (GetParent (pdi->hDate), hdc);
299 LONG cxNumbers = cxRECT(rDate) - GetSystemMetrics(SM_CXVSCROLL) - (sDateSep.cx)*2;
300 LONG cxYear = max( cxNumbers/2, s88.cx*2 );
301 LONG cxMonth = max( cxNumbers/4, s88.cx );
302 LONG cxDay = max( cxNumbers/4, s88.cx );
304 cxYear = min (cxYear, (LONG)( 1.2 * s88.cx * 2 ));
305 cxMonth = min (cxMonth, (LONG)( 1.2 * s88.cx ));
306 cxDay = min (cxDay, (LONG)( 1.2 * s88.cx )); // don't be TOO wide.
308 pdi->idYear = 100+NextControlID (GetParent (pdi->hDate));
309 pdi->idMonth = pdi->idYear +1;
310 pdi->idDay = pdi->idYear +2;
314 LONG cy = cyRECT(rDate);
321 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=M,1=D,2=Y
323 switch (pdi->dwFormat)
325 case 0: cx = cxMonth; id = pdi->idMonth; break;
326 case 1: cx = cxDay; id = pdi->idDay; break;
327 case 2: cx = cxYear; id = pdi->idYear; break;
330 hWnd = CreateWindow (
333 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
335 GetParent(pdi->hDate),
341 switch (pdi->dwFormat)
343 case 0: pdi->hMonth = hWnd; iiFirst = pdi->dateNow.wMonth; break;
344 case 1: pdi->hDay = hWnd; iiFirst = pdi->dateNow.wDay; break;
345 case 2: pdi->hYear = hWnd; iiFirst = pdi->dateNow.wYear; break;
349 // Create a separator
351 pdi->hSep1 = CreateWindow (
354 WS_CHILD | SS_CENTER,
355 xx, yy, sDateSep.cx, cy,
356 GetParent(pdi->hDate),
362 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=D,1=M,2=M
364 switch (pdi->dwFormat)
366 case 0: cx = cxDay; id = pdi->idDay; break;
367 case 1: cx = cxMonth; id = pdi->idMonth; break;
368 case 2: cx = cxMonth; id = pdi->idMonth; break;
371 hWnd = CreateWindow (
374 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
376 GetParent(pdi->hDate),
382 switch (pdi->dwFormat)
384 case 0: pdi->hDay = hWnd; break;
385 case 1: pdi->hMonth = hWnd; break;
386 case 2: pdi->hMonth = hWnd; break;
389 // Create a separator
391 pdi->hSep2 = CreateWindow (
394 WS_CHILD | SS_CENTER,
395 xx, yy, sDateSep.cx, cy,
396 GetParent(pdi->hDate),
402 // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=Y,1=Y,2=D
404 switch (pdi->dwFormat)
406 case 0: cx = cxYear; id = pdi->idYear; break;
407 case 1: cx = cxYear; id = pdi->idYear; break;
408 case 2: cx = cxDay; id = pdi->idDay; break;
411 hWnd = CreateWindow (
414 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
416 GetParent(pdi->hDate),
422 switch (pdi->dwFormat)
424 case 0: pdi->hYear = hWnd; break;
425 case 1: pdi->hYear = hWnd; break;
426 case 2: pdi->hDay = hWnd; break;
429 // Subclass the edit controls
431 Subclass_AddHook (pdi->hYear, DateEditProc);
432 Subclass_AddHook (pdi->hMonth, DateEditProc);
433 Subclass_AddHook (pdi->hDay, DateEditProc);
435 HFONT hf = (HFONT)SendMessage (GetParent (pdi->hDate), WM_GETFONT, 0, 0);
436 SendMessage (pdi->hYear, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
437 SendMessage (pdi->hSep1, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
438 SendMessage (pdi->hMonth, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
439 SendMessage (pdi->hSep2, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
440 SendMessage (pdi->hDay, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
442 EnableWindow (pdi->hYear, IsWindowEnabled (pdi->hDate));
443 EnableWindow (pdi->hSep1, IsWindowEnabled (pdi->hDate));
444 EnableWindow (pdi->hMonth, IsWindowEnabled (pdi->hDate));
445 EnableWindow (pdi->hSep2, IsWindowEnabled (pdi->hDate));
446 EnableWindow (pdi->hDay, IsWindowEnabled (pdi->hDate));
448 ShowWindow (pdi->hYear, TRUE);
449 ShowWindow (pdi->hSep1, TRUE);
450 ShowWindow (pdi->hMonth, TRUE);
451 ShowWindow (pdi->hSep2, TRUE);
452 ShowWindow (pdi->hDay, TRUE);
455 GetWindowRect (pdi->hDate, &rWindow);
456 rWindow.right += (cxYear + cxMonth + cxDay + sDateSep.cx*2) - cxRECT(rDate);
458 SetWindowPos (pdi->hDate, NULL, 0, 0, cxRECT(rWindow), cyRECT(rWindow),
459 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
462 GetRectInParent (pdi->hDate, &rSpinner);
463 rSpinner.left = rSpinner.right;
464 rSpinner.right = rSpinner.left + GetSystemMetrics (SM_CXVSCROLL);
465 rSpinner.bottom -= 2; // just like Win95 does
466 CreateSpinner (pdi->hFirst, 10, FALSE, 0, iiFirst, 3000, &rSpinner);
467 pdi->hSpinner = SP_GetSpinner (pdi->hFirst);
468 pdi->hSpinnerBuddy = pdi->hFirst;
470 Date_Edit_SetText (pdi, pdi->hYear);
471 Date_Edit_SetText (pdi, pdi->hMonth);
472 Date_Edit_SetText (pdi, pdi->hDay);
473 Date_Edit_OnSetFocus (pdi, pdi->hFirst);
477 void Date_OnDestroy (DateInfo *pdi)
479 Subclass_RemoveHook (GetParent (pdi->hDate), DateDlgProc);
484 void Date_OnButtonDown (DateInfo *pdi, UINT msg, WPARAM wp, LPARAM lp)
486 DWORD dw = GetMessagePos();
489 pt.y = HIWORD(dw); // in screen coordinates
494 GetWindowRect (pdi->hYear, &rTarget);
495 if (PtInRect (&rTarget, pt))
496 hTarget = pdi->hYear;
498 GetWindowRect (pdi->hMonth, &rTarget);
499 if (PtInRect (&rTarget, pt))
500 hTarget = pdi->hMonth;
502 GetWindowRect (pdi->hDay, &rTarget);
503 if (PtInRect (&rTarget, pt))
508 PostMessage (hTarget, msg, wp, lp);
513 BOOL Date_OnGetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
515 SYSTEMTIME *pdate = (SYSTEMTIME*)lp;
516 pdate->wYear = pdi->dateNow.wYear;
517 pdate->wMonth = pdi->dateNow.wMonth;
518 pdate->wDayOfWeek = pdi->dateNow.wDayOfWeek;
519 pdate->wDay = pdi->dateNow.wDay;
524 BOOL Date_OnSetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
526 SYSTEMTIME *pdate = (SYSTEMTIME*)lp;
527 pdi->dateNow = *pdate;
528 Date_Edit_SetText (pdi, pdi->hYear);
529 Date_Edit_SetText (pdi, pdi->hMonth);
530 Date_Edit_SetText (pdi, pdi->hDay);
535 HRESULT CALLBACK DateDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
537 PVOID oldProc = Subclass_FindNextHook (hDlg, DateDlgProc);
542 case WM_CTLCOLOREDIT:
543 case WM_CTLCOLORSTATIC:
544 for (iDate = 0; iDate < cDate; ++iDate)
546 if (aDate[ iDate ].hDate == NULL)
548 if ( (aDate[ iDate ].hYear == (HWND)lp) ||
549 (aDate[ iDate ].hSep1 == (HWND)lp) ||
550 (aDate[ iDate ].hMonth == (HWND)lp) ||
551 (aDate[ iDate ].hSep2 == (HWND)lp) ||
552 (aDate[ iDate ].hDay == (HWND)lp) )
555 if (IsWindowEnabled (aDate[ iDate ].hDate))
556 clr = GetSysColor (COLOR_WINDOW);
558 clr = GetSysColor (COLOR_BTNFACE);
559 SetBkColor ((HDC)wp, clr);
560 return (BOOL)(INT_PTR)CreateSolidBrush (clr);
566 for (iDate = 0; iDate < cDate; ++iDate)
568 if (aDate[ iDate ].hDate == NULL)
570 if ( (aDate[ iDate ].idYear == LOWORD(wp)) ||
571 (aDate[ iDate ].idMonth == LOWORD(wp)) ||
572 (aDate[ iDate ].idDay == LOWORD(wp)) )
574 if (HIWORD(wp) == SPN_UPDATE)
576 Date_Edit_OnUpdate (&aDate[ iDate ], GetDlgItem(hDlg,LOWORD(wp)));
585 return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
587 return DefWindowProc (hDlg, msg, wp, lp);
591 HRESULT CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
593 DateInfo *pdi = NULL;
595 EnterCriticalSection (&csDate);
597 for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
599 if ( (aDate[ iDate ].hYear == hEdit) ||
600 (aDate[ iDate ].hMonth == hEdit) ||
601 (aDate[ iDate ].hDay == hEdit) )
603 pdi = &aDate[ iDate ];
607 LeaveCriticalSection (&csDate);
614 Date_Edit_OnSetFocus (pdi, hEdit);
619 PVOID oldProc = Subclass_FindNextHook (hEdit, DateEditProc);
621 return CallWindowProc ((WNDPROC)oldProc, hEdit, msg, wp, lp);
623 return DefWindowProc (hEdit, msg, wp, lp);
627 void Date_Edit_OnSetFocus (DateInfo *pdi, HWND hEdit)
633 if (hEdit == pdi->hYear)
636 dwNow = pdi->dateNow.wYear;
639 else if (hEdit == pdi->hMonth)
642 dwNow = pdi->dateNow.wMonth;
645 else // (hEdit == pdi->hDay)
648 dwNow = pdi->dateNow.wDay;
652 if (pdi->hSpinnerBuddy != hEdit)
654 SP_SetBuddy (pdi->hSpinnerBuddy, hEdit, FALSE);
655 pdi->hSpinnerBuddy = hEdit;
658 SP_SetRange (hEdit, dwMin, dwMax);
659 SP_SetPos (hEdit, dwNow);
661 SendMessage (hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1); // select all
665 void Date_Edit_OnUpdate (DateInfo *pdi, HWND hEdit)
667 TCHAR szText[ cchRESOURCE ];
669 if (hEdit == pdi->hYear)
671 GetWindowText (pdi->hYear, szText, 256);
672 pdi->dateNow.wYear = (WORD)atol (szText);
675 if (hEdit == pdi->hMonth)
677 GetWindowText (pdi->hMonth, szText, 256);
678 pdi->dateNow.wMonth = (WORD)atol (szText);
681 if (hEdit == pdi->hDay)
683 GetWindowText (pdi->hDay, szText, 256);
684 pdi->dateNow.wDay = (WORD)atol (szText);
687 SYSTEMTIME st = pdi->dateNow;
688 Date_SendCallback (pdi, DN_UPDATE, (LPARAM)&st);
692 void Date_Edit_SetText (DateInfo *pdi, HWND hEdit)
696 if (hEdit == pdi->hYear)
697 dwNow = pdi->dateNow.wYear;
698 else if (hEdit == pdi->hMonth)
699 dwNow = pdi->dateNow.wMonth;
700 else if (hEdit == pdi->hDay)
701 dwNow = pdi->dateNow.wDay;
703 if (pdi->hSpinnerBuddy == hEdit)
705 SP_SetPos (hEdit, dwNow);
709 TCHAR szText[ cchRESOURCE ];
710 wsprintf (szText, TEXT("%lu"), dwNow);
711 SetWindowText (hEdit, szText);