ticket-2618-patches-20031207
[openafs.git] / src / WINNT / afsapplib / ctl_time.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include <windows.h>
16 #include <windowsx.h>
17 #include <commctrl.h>
18 #include <stdlib.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_time.h>
24 #include <WINNT/TaLocale.h>
25
26 #ifndef cchRESOURCE
27 #define cchRESOURCE 256
28 #endif
29
30
31 /*
32  * MISCELLANEOUS ______________________________________________________________
33  *
34  */
35
36 #ifndef REALLOC
37 #define REALLOC(_a,_c,_r,_i) TimeReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
38 BOOL TimeReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
39 {
40    LPVOID pNew;
41    size_t cNew;
42
43    if (cReq <= *pcTarget)
44       return TRUE;
45
46    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
47       return FALSE;
48
49    if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
50       return FALSE;
51    memset (pNew, 0x00, cbElement * cNew);
52
53    if (*pcTarget != 0)
54       {
55       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
56       Free (*ppTarget);
57       }
58
59    *ppTarget = pNew;
60    *pcTarget = cNew;
61    return TRUE;
62 }
63 #endif
64
65
66
67 /*
68  * TIME _______________________________________________________________________
69  *
70  */
71
72 typedef struct
73    {
74    HWND hTime;
75    HWND hHours;
76    HWND hSep1;
77    HWND hMinutes;
78    HWND hSep2;
79    HWND hAMPM;
80    HWND hSpinner;
81    HWND hSpinnerBuddy;
82
83    WORD idHours;
84    WORD idMinutes;
85    WORD idAMPM;
86
87    SYSTEMTIME timeNow;  // only hour and minute fields are relevant
88
89    BOOL  f24Hour;
90    BOOL  f0Hours;
91    BOOL  fCallingBack;
92    BOOL  fCanCallBack;
93    } TimeInfo;
94
95 static CRITICAL_SECTION csTime;
96 static TimeInfo        *aTime = NULL;
97 static size_t           cTime = 0;
98
99 #define cszTIMECLASS TEXT("Time")
100
101 BOOL CALLBACK TimeProc (HWND hTime, UINT msg, WPARAM wp, LPARAM lp);
102 BOOL CALLBACK TimeEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp);
103 BOOL CALLBACK TimeDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
104
105 void Time_SendCallback (TimeInfo *pti, WORD eln, LPARAM lp);
106
107 void Time_OnCreate (TimeInfo *pti);
108 void Time_OnDestroy (TimeInfo *pti);
109 void Time_OnButtonDown (TimeInfo *pti, UINT msg, WPARAM wp, LPARAM lp);
110 BOOL Time_OnGetTime (TimeInfo *pti, WPARAM wp, LPARAM lp);
111 BOOL Time_OnSetTime (TimeInfo *pti, WPARAM wp, LPARAM lp);
112
113 void Time_Edit_OnSetFocus (TimeInfo *pti, HWND hEdit);
114 void Time_Edit_OnUpdate (TimeInfo *pti, HWND hEdit);
115 void Time_Edit_SetText (TimeInfo *pti, HWND hEdit);
116
117 void Time_GetAMPMSize (HDC hdc, SIZE *pSize, LPTSTR pszAM, LPTSTR pszPM);
118
119
120 BOOL RegisterTimeClass (void)
121 {
122    static BOOL fRegistered = FALSE;
123
124    if (!fRegistered)
125       {
126       InitializeCriticalSection (&csTime);
127
128       WNDCLASS wc;
129       memset (&wc, 0x00, sizeof(wc));
130       wc.style = CS_GLOBALCLASS;
131       wc.lpfnWndProc = (WNDPROC)TimeProc;
132       wc.hInstance = THIS_HINST;
133       wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
134       wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
135       wc.lpszClassName = cszTIMECLASS;
136
137       if (RegisterClass (&wc))
138          fRegistered = TRUE;
139       }
140
141    return fRegistered;
142 }
143
144
145 void Time_SendCallback (TimeInfo *pti, WORD eln, LPARAM lp)
146 {
147    if ((pti->fCanCallBack == TRUE) && !pti->fCallingBack)
148       {
149       pti->fCallingBack = TRUE;
150
151       SendMessage (GetParent (pti->hTime),
152                    WM_COMMAND,
153                    MAKELONG ((WORD)GetWindowLong (pti->hTime, GWL_ID), eln),
154                    lp);
155
156       pti->fCallingBack = FALSE;
157       }
158 }
159
160
161 BOOL CALLBACK TimeProc (HWND hTime, UINT msg, WPARAM wp, LPARAM lp)
162 {
163    TimeInfo *pti = NULL;
164
165    EnterCriticalSection (&csTime);
166
167    if (msg == WM_CREATE)
168       {
169       for (size_t iTime = 0; iTime < cTime; ++iTime)
170          {
171          if (aTime[ iTime ].hTime == NULL)
172             break;
173          }
174       if (iTime >= cTime)
175          {
176          if (!REALLOC (aTime, cTime, 1+iTime, 4))
177             return FALSE;
178          }
179
180       memset (&aTime[ iTime ], 0x00, sizeof(TimeInfo));
181       aTime[ iTime ].hTime = hTime;
182
183       pti = &aTime[ iTime ];
184       }
185    else
186       {
187       for (size_t iTime = 0; !pti && iTime < cTime; ++iTime)
188          {
189          if (aTime[ iTime ].hTime == hTime)
190             pti = &aTime[ iTime ];
191          }
192       }
193
194    LeaveCriticalSection (&csTime);
195
196    if (pti != NULL)
197       {
198       switch (msg)
199          {
200          case WM_CREATE:
201             Time_OnCreate (pti);
202             break;
203
204          case WM_DESTROY:
205             Time_OnDestroy (pti);
206             break;
207
208          case WM_RBUTTONDOWN:
209          case WM_LBUTTONDOWN:
210             Time_OnButtonDown (pti, msg, wp, lp);
211             break;
212
213          case WM_SETFOCUS:
214             PostMessage (GetParent(hTime), WM_NEXTDLGCTL, (WPARAM)pti->hHours, TRUE);
215             break;
216
217          case WM_ENABLE:
218             EnableWindow (pti->hHours,   IsWindowEnabled (hTime));
219             EnableWindow (pti->hSep1,    IsWindowEnabled (hTime));
220             EnableWindow (pti->hMinutes, IsWindowEnabled (hTime));
221             EnableWindow (pti->hSpinner, IsWindowEnabled (hTime));
222
223             if (!pti->f24Hour)
224                {
225                EnableWindow (pti->hSep2, IsWindowEnabled (hTime));
226                EnableWindow (pti->hAMPM, IsWindowEnabled (hTime));
227                }
228
229             RECT rTime;
230             GetRectInParent (hTime, &rTime);
231             InvalidateRect (GetParent(hTime), &rTime, TRUE);
232             UpdateWindow (GetParent(hTime));
233             break;
234
235          case WM_SYSCHAR:
236          case WM_CHAR:
237             switch (wp)
238                {
239                case VK_UP:
240                   PostMessage (GetParent(pti->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)pti->hSpinner);
241                   break;
242
243                case VK_DOWN:
244                   PostMessage (GetParent(pti->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)pti->hSpinner);
245                   break;
246
247                case VK_PRIOR:
248                   PostMessage (GetParent(pti->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)pti->hSpinner);
249                   break;
250
251                case VK_NEXT:
252                   PostMessage (GetParent(pti->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)pti->hSpinner);
253                   break;
254
255                case VK_HOME:
256                   PostMessage (GetParent(pti->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)pti->hSpinner);
257                   break;
258
259                case VK_END:
260                   PostMessage (GetParent(pti->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)pti->hSpinner);
261                   break;
262                }
263             break;
264
265          case TM_GETTIME:
266             return Time_OnGetTime (pti, wp, lp);
267
268          case TM_SETTIME:
269             return Time_OnSetTime (pti, wp, lp);
270          }
271       }
272
273    return DefWindowProc (hTime, msg, wp, lp);
274 }
275
276
277 void Time_OnCreate (TimeInfo *pti)
278 {
279    Subclass_AddHook (GetParent(pti->hTime), TimeDlgProc);
280
281    TCHAR szTimeSep[ cchRESOURCE ];
282    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE))
283       lstrcpy (szTimeSep, TEXT(":"));
284
285    TCHAR sz24Hour[ cchRESOURCE ];
286    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITIME, sz24Hour, cchRESOURCE))
287       pti->f24Hour = FALSE;
288    else
289       pti->f24Hour = (sz24Hour[0] == TEXT('1')) ? TRUE : FALSE;
290
291    TCHAR sz0Hour[ cchRESOURCE ];
292    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITLZERO, sz0Hour, cchRESOURCE))
293       pti->f0Hours = FALSE;
294    else
295       pti->f0Hours = (sz0Hour[0] == TEXT('1')) ? TRUE : FALSE;
296
297    RECT rTime;
298    GetClientRect (pti->hTime, &rTime);
299
300    POINT ptTime = {0,0};
301    ClientToScreen (pti->hTime, &ptTime);
302    ScreenToClient (GetParent (pti->hTime), &ptTime);
303
304    SIZE s88; // size of widest likely double-digit number
305    SIZE sTimeSep; // size of ":"
306    SIZE sAMPM; // size of "AM/PM" listbox
307    TCHAR szAM[ cchRESOURCE ];
308    TCHAR szPM[ cchRESOURCE ];
309
310    HDC hdc = GetDC (GetParent (pti->hTime));
311    GetTextExtentPoint (hdc, TEXT("88"), lstrlen(TEXT("88")), &s88);
312    GetTextExtentPoint (hdc, szTimeSep, lstrlen(szTimeSep), &sTimeSep);
313    if (!pti->f24Hour)
314       Time_GetAMPMSize (hdc, &sAMPM, szAM, szPM);
315    ReleaseDC (GetParent (pti->hTime), hdc);
316
317    LONG cxNumbers = cxRECT(rTime) - GetSystemMetrics (SM_CXVSCROLL) - (sTimeSep.cx);
318    if (!pti->f24Hour)
319       cxNumbers -= sTimeSep.cx + sAMPM.cx;
320    LONG cxMinutes = max( cxNumbers/2, s88.cx );
321    LONG cxHours   = cxNumbers - cxMinutes;
322
323    cxMinutes = min (cxMinutes, (LONG)( 1.2 * s88.cx ));
324    cxHours   = min (cxHours,   (LONG)( 1.2 * s88.cx ));  // don't be TOO wide.
325
326    LONG cy = cyRECT(rTime);
327    LONG yy = ptTime.y;
328
329    pti->idHours = 100+NextControlID (GetParent (pti->hTime));
330    pti->hHours = CreateWindow (
331                 TEXT("EDIT"),
332                 TEXT(""),
333                 WS_CHILD | WS_TABSTOP | ES_MULTILINE | ES_RIGHT | ES_NUMBER,
334                 ptTime.x, yy, cxHours, cy,
335                 GetParent(pti->hTime),
336                 (HMENU)pti->idHours,
337                 THIS_HINST,
338                 0);
339
340    pti->hSep1 = CreateWindow (
341                 TEXT("STATIC"),
342                 szTimeSep,
343                 WS_CHILD | SS_CENTER,
344                 ptTime.x + cxHours, yy, sTimeSep.cx, cy,
345                 GetParent(pti->hTime),
346                 (HMENU)-1,
347                 THIS_HINST,
348                 0);
349
350    pti->idMinutes = 100+NextControlID (GetParent (pti->hTime));
351    pti->hMinutes = CreateWindow (
352                 TEXT("EDIT"),
353                 TEXT(""),
354                 WS_CHILD | WS_TABSTOP | ES_MULTILINE | ES_RIGHT | ES_NUMBER,
355                 ptTime.x + cxHours + sTimeSep.cx, yy, cxMinutes, cy,
356                 GetParent(pti->hTime),
357                 (HMENU)pti->idMinutes,
358                 THIS_HINST,
359                 0);
360
361    if (!pti->f24Hour)
362       {
363       pti->hSep2 = CreateWindow (
364                    TEXT("STATIC"),
365                    TEXT(""),
366                    WS_CHILD | SS_CENTER,
367                    ptTime.x + cxHours + cxMinutes + sTimeSep.cx, yy, sTimeSep.cx, cy,
368                    GetParent(pti->hTime),
369                    (HMENU)-1,
370                    THIS_HINST,
371                    0);
372
373       pti->idAMPM = 100+NextControlID (GetParent (pti->hTime));
374       pti->hAMPM = CreateWindow (
375                    TEXT("LISTBOX"),
376                    TEXT(""),
377                    WS_CHILD | WS_TABSTOP | LBS_NOINTEGRALHEIGHT | LBS_HASSTRINGS | LBS_OWNERDRAWFIXED | LBS_NOTIFY,
378                    ptTime.x + cxHours + cxMinutes + 2 * sTimeSep.cx, yy, sAMPM.cx, cy,
379                    GetParent(pti->hTime),
380                    (HMENU)pti->idAMPM,
381                    THIS_HINST,
382                    0);
383       }
384
385    Subclass_AddHook (pti->hHours, TimeEditProc);
386    Subclass_AddHook (pti->hMinutes, TimeEditProc);
387
388    if (!pti->f24Hour)
389       Subclass_AddHook (pti->hAMPM, TimeEditProc);
390
391    HFONT hf = (HFONT)SendMessage (GetParent (pti->hTime), WM_GETFONT, 0, 0);
392    SendMessage (pti->hHours,   WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
393    SendMessage (pti->hSep1,    WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
394    SendMessage (pti->hMinutes, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
395
396    if (!pti->f24Hour)
397       {
398       SendMessage (pti->hSep2, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
399       SendMessage (pti->hAMPM, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
400       }
401
402    EnableWindow (pti->hHours,   IsWindowEnabled (pti->hTime));
403    EnableWindow (pti->hSep1,    IsWindowEnabled (pti->hTime));
404    EnableWindow (pti->hMinutes, IsWindowEnabled (pti->hTime));
405
406    if (!pti->f24Hour)
407       {
408       EnableWindow (pti->hSep2, IsWindowEnabled (pti->hTime));
409       EnableWindow (pti->hAMPM, IsWindowEnabled (pti->hTime));
410
411       LB_StartChange (pti->hAMPM, TRUE);
412       LB_AddItem (pti->hAMPM, szAM, 0);
413       LB_AddItem (pti->hAMPM, szPM, 1);
414       LB_EndChange (pti->hAMPM);
415       LB_SetSelected (pti->hAMPM, 1);
416       }
417
418    ShowWindow (pti->hHours,   TRUE);
419    ShowWindow (pti->hSep1,    TRUE);
420    ShowWindow (pti->hMinutes, TRUE);
421
422    if (!pti->f24Hour)
423       {
424       ShowWindow (pti->hSep2, TRUE);
425       ShowWindow (pti->hAMPM, TRUE);
426       }
427
428    RECT rWindow;
429    GetWindowRect (pti->hTime, &rWindow);
430    rWindow.right += (cxHours + cxMinutes + sTimeSep.cx) - cxRECT(rTime);
431    if (!pti->f24Hour)
432       rWindow.right += sTimeSep.cx + sAMPM.cx;
433
434    SetWindowPos (pti->hTime, NULL, 0, 0, cxRECT(rWindow), cyRECT(rWindow),
435                  SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
436
437    RECT rSpinner;
438    GetRectInParent (pti->hTime, &rSpinner);
439    rSpinner.left = rSpinner.right;
440    rSpinner.right = rSpinner.left + GetSystemMetrics (SM_CXVSCROLL);
441    rSpinner.bottom -= 2; // just like Win95 does
442    CreateSpinner (pti->hHours, 10, FALSE, 0, pti->timeNow.wHour, 24, &rSpinner);
443    pti->hSpinner = SP_GetSpinner (pti->hHours);
444    pti->hSpinnerBuddy = pti->hHours;
445
446    Time_Edit_SetText (pti, pti->hHours);
447    Time_Edit_SetText (pti, pti->hMinutes);
448    Time_Edit_OnSetFocus (pti, pti->hHours);
449    pti->fCanCallBack = TRUE;
450 }
451
452
453 void Time_OnDestroy (TimeInfo *pti)
454 {
455    Subclass_RemoveHook (GetParent(pti->hTime), TimeDlgProc);
456    pti->hTime = NULL;
457 }
458
459
460 void Time_OnButtonDown (TimeInfo *pti, UINT msg, WPARAM wp, LPARAM lp)
461 {
462    DWORD dw = GetMessagePos();
463    POINT pt;
464    pt.x = LOWORD(dw);
465    pt.y = HIWORD(dw);  // in screen coordinates
466
467    RECT rTarget;
468    HWND hTarget = 0;
469
470    GetWindowRect (pti->hHours, &rTarget);
471    if (PtInRect (&rTarget, pt))
472       hTarget = pti->hHours;
473
474    GetWindowRect (pti->hMinutes, &rTarget);
475    if (PtInRect (&rTarget, pt))
476       hTarget = pti->hMinutes;
477
478    if (!pti->f24Hour)
479       {
480       GetWindowRect (pti->hAMPM, &rTarget);
481       if (PtInRect (&rTarget, pt))
482          hTarget = pti->hAMPM;
483       }
484
485    if (hTarget != 0)
486       {
487       PostMessage (hTarget, msg, wp, lp);
488       }
489 }
490
491
492 BOOL Time_OnGetTime (TimeInfo *pti, WPARAM wp, LPARAM lp)
493 {
494    SYSTEMTIME *ptime = (SYSTEMTIME*)lp;
495    ptime->wHour = pti->timeNow.wHour;
496    ptime->wMinute = pti->timeNow.wMinute;
497    ptime->wSecond = pti->timeNow.wSecond;
498    ptime->wMilliseconds = pti->timeNow.wMilliseconds;
499    return TRUE;
500 }
501
502
503 BOOL Time_OnSetTime (TimeInfo *pti, WPARAM wp, LPARAM lp)
504 {
505    SYSTEMTIME *ptime = (SYSTEMTIME*)lp;
506    pti->timeNow = *ptime;
507
508    Time_Edit_SetText (pti, pti->hHours);
509    Time_Edit_SetText (pti, pti->hMinutes);
510
511    return TRUE;
512 }
513
514
515 BOOL CALLBACK TimeDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
516 {
517    PVOID oldProc = Subclass_FindNextHook (hDlg, TimeDlgProc);
518    size_t iTime;
519
520    switch (msg)
521       {
522       case WM_CTLCOLOREDIT:
523       case WM_CTLCOLORSTATIC:
524       case WM_CTLCOLORLISTBOX:
525          for (iTime = 0; iTime < cTime; ++iTime)
526             {
527             if (aTime[ iTime ].hTime == NULL)
528                continue;
529             if ( (aTime[ iTime ].hHours   == (HWND)lp) ||
530                  (aTime[ iTime ].hSep1    == (HWND)lp) ||
531                  (aTime[ iTime ].hMinutes == (HWND)lp) ||
532                  ((!aTime[ iTime ].f24Hour) && (aTime[ iTime ].hSep2 == (HWND)lp)) ||
533                  ((!aTime[ iTime ].f24Hour) && (aTime[ iTime ].hAMPM == (HWND)lp)) )
534                {
535                COLORREF clr;
536                if (IsWindowEnabled (aTime[ iTime ].hTime))
537                   clr = GetSysColor (COLOR_WINDOW);
538                else
539                   clr = GetSysColor (COLOR_BTNFACE);
540                SetBkColor ((HDC)wp, clr);
541                return (BOOL)CreateSolidBrush (clr);
542                }
543             }
544          break;
545
546       case WM_MEASUREITEM: 
547          LPMEASUREITEMSTRUCT lpmis;
548          lpmis = (LPMEASUREITEMSTRUCT)lp; 
549          HDC hdc;
550          hdc = GetDC (hDlg);
551          SIZE sAMPM;
552          Time_GetAMPMSize (hdc, &sAMPM, NULL, NULL);
553          ReleaseDC (hDlg, hdc);
554          lpmis->itemHeight = sAMPM.cy;
555          return TRUE; 
556
557       case WM_DRAWITEM: 
558          LPDRAWITEMSTRUCT lpdis;
559          lpdis = (LPDRAWITEMSTRUCT)lp; 
560
561          COLORREF clrBack;
562          COLORREF clrFore;
563          if (!IsWindowEnabled (lpdis->hwndItem))
564             {
565             clrBack = GetSysColor (COLOR_BTNFACE);
566             clrFore = GetSysColor (COLOR_GRAYTEXT);
567             }
568          else if ((lpdis->itemState & ODS_SELECTED) && (GetFocus() == lpdis->hwndItem))
569             {
570             clrBack = GetSysColor (COLOR_HIGHLIGHT);
571             clrFore = GetSysColor (COLOR_HIGHLIGHTTEXT);
572             }
573          else
574             {
575             clrBack = GetSysColor (COLOR_WINDOW);
576             clrFore = GetSysColor (COLOR_BTNTEXT);
577             }
578
579          TCHAR szText[ cchRESOURCE ];
580          SendMessage (lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM)szText); 
581
582          SetTextColor (lpdis->hDC, clrFore);
583          SetBkColor (lpdis->hDC, clrBack);
584          SetBkMode (lpdis->hDC, OPAQUE);
585          TextOut (lpdis->hDC, 0, lpdis->rcItem.top, szText, lstrlen(szText));
586          return TRUE;
587
588       case WM_COMMAND:
589          for (iTime = 0; iTime < cTime; ++iTime)
590             {
591             if (aTime[ iTime ].hTime == NULL)
592                continue;
593             if ( (aTime[ iTime ].idHours   == LOWORD(wp)) ||
594                  (aTime[ iTime ].idMinutes == LOWORD(wp)) ||
595                  ((!aTime[ iTime ].f24Hour) && (aTime[ iTime ].idAMPM == LOWORD(wp))) )
596                {
597                if (HIWORD(wp) == SPN_UPDATE)
598                   {
599                   Time_Edit_OnUpdate (&aTime[ iTime ], GetDlgItem(hDlg,LOWORD(wp)));
600                   }
601                break;
602                }
603             }
604          break;
605       }
606
607    if (oldProc)
608       return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
609    else
610       return DefWindowProc (hDlg, msg, wp, lp);
611 }
612
613
614 BOOL CALLBACK TimeEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
615 {
616    TimeInfo *pti = NULL;
617
618    EnterCriticalSection (&csTime);
619
620    for (size_t iTime = 0; !pti && iTime < cTime; ++iTime)
621       {
622       if ( (aTime[ iTime ].hHours   == hEdit) ||
623            (aTime[ iTime ].hMinutes == hEdit) ||
624            ((!aTime[ iTime ].f24Hour) && (aTime[ iTime ].hAMPM == hEdit)) )
625          {
626          pti = &aTime[ iTime ];
627          }
628       }
629
630    LeaveCriticalSection (&csTime);
631
632    if (pti)
633       {
634       switch (msg)
635          {
636          case WM_SETFOCUS:
637             Time_Edit_OnSetFocus (pti, hEdit);
638             break;
639          }
640       }
641
642    PVOID oldProc = Subclass_FindNextHook (hEdit, TimeEditProc);
643    if (oldProc)
644       return CallWindowProc ((WNDPROC)oldProc, hEdit, msg, wp, lp);
645    else
646       return DefWindowProc (hEdit, msg, wp, lp);
647 }
648
649
650 void Time_Edit_OnSetFocus (TimeInfo *pti, HWND hEdit)
651 {
652    DWORD dwMin;
653    DWORD dwNow;
654    DWORD dwMax;
655
656    pti->fCanCallBack --;
657
658    if (hEdit == pti->hHours)
659       {
660       dwMin = (pti->f24Hour) ? 0 : 1;
661       dwNow = (pti->f24Hour) ? pti->timeNow.wHour : (pti->timeNow.wHour % 12);
662       dwMax = (pti->f24Hour) ? 24 : 12;
663
664       if (!pti->f24Hour && !dwNow)
665          dwNow = 12;
666       }
667    else if (hEdit == pti->hMinutes)
668       {
669       dwMin = 0;
670       dwNow = pti->timeNow.wMinute;
671       dwMax = 59;
672       }
673    else // (hEdit == pti->hAMPM)
674       {
675       dwMin = 0;
676       dwNow = (pti->timeNow.wHour >= 12) ? 1 : 0;
677       dwMax = 1;
678       }
679
680    if (pti->hSpinnerBuddy != hEdit)
681       {
682       SP_SetBuddy (pti->hSpinnerBuddy, hEdit, FALSE);
683       pti->hSpinnerBuddy = hEdit;
684       }
685
686    SP_SetRange (hEdit, dwMin, dwMax);
687    SP_SetPos (hEdit, dwNow);
688
689    if ((hEdit == pti->hHours) && (!pti->f0Hours))
690       SP_SetFormat (hEdit, TEXT("%lu"));
691    else if ((hEdit == pti->hHours) || (hEdit == pti->hMinutes))
692       SP_SetFormat (hEdit, TEXT("%02lu"));
693
694    if ((hEdit == pti->hHours) || (hEdit == pti->hMinutes))
695       SendMessage (hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1);  // select all
696
697    pti->fCanCallBack ++;
698 }
699
700
701 void Time_Edit_OnUpdate (TimeInfo *pti, HWND hEdit)
702 {
703    TCHAR szText[ cchRESOURCE ];
704
705    if ((hEdit == pti->hHours) || (hEdit == pti->hAMPM))
706       {
707       GetWindowText (pti->hHours, szText, 256);
708       pti->timeNow.wHour = (WORD)atol (szText);
709
710       if (!pti->f24Hour)
711          {
712          if ((pti->timeNow.wHour < 12) && (LB_GetSelected (pti->hAMPM) == 1))
713             pti->timeNow.wHour += 12;
714          else if ((pti->timeNow.wHour >= 12) && (LB_GetSelected (pti->hAMPM) == 0))
715             pti->timeNow.wHour -= 12;
716          }
717       }
718
719    if (hEdit == pti->hMinutes)
720       {
721       GetWindowText (pti->hMinutes, szText, 256);
722       pti->timeNow.wMinute = (WORD)atol (szText);
723       }
724
725    if ((pti->timeNow.wHour == 24) && (pti->timeNow.wMinute != 0))
726       {
727       pti->timeNow.wMinute = 0;
728       Time_Edit_SetText (pti, pti->hMinutes);
729       }
730
731    SYSTEMTIME st = pti->timeNow;
732    Time_SendCallback (pti, TN_UPDATE, (LPARAM)&st);
733 }
734
735
736 void Time_Edit_SetText (TimeInfo *pti, HWND hEdit)
737 {
738    DWORD dwNow;
739
740    if (hEdit == pti->hHours)
741       {
742       BOOL fPM = (pti->timeNow.wHour >= 12) ? TRUE : FALSE;
743
744       if (pti->f24Hour)
745          dwNow = pti->timeNow.wHour;
746       else if ((dwNow = pti->timeNow.wHour % 12) == 0)
747          dwNow = 12;
748
749       if (pti->hSpinnerBuddy == hEdit)
750          {
751          SP_SetPos (hEdit, dwNow);
752          }
753       else
754          {
755          TCHAR szText[ cchRESOURCE ];
756
757          if (!pti->f0Hours)
758             wsprintf (szText, TEXT("%lu"), dwNow);
759          else
760             wsprintf (szText, TEXT("%02lu"), dwNow);
761
762          SetWindowText (hEdit, szText);
763          }
764
765       if (!pti->f24Hour)
766          {
767          LB_SetSelected (pti->hAMPM, (int)fPM);
768          }
769       }
770    else if (hEdit == pti->hMinutes)
771       {
772       if (pti->hSpinnerBuddy == hEdit)
773          {
774          SP_SetPos (hEdit, pti->timeNow.wMinute);
775          }
776       else
777          {
778          TCHAR szText[ cchRESOURCE ];
779          wsprintf (szText, TEXT("%02lu"), pti->timeNow.wMinute);
780          SetWindowText (hEdit, szText);
781          }
782       }
783 }
784
785
786 void Time_GetAMPMSize (HDC hdc, SIZE *pSize, LPTSTR pszAM, LPTSTR pszPM)
787 {
788    TCHAR szAM[ cchRESOURCE ];
789    TCHAR szPM[ cchRESOURCE ];
790    if (!pszAM)
791       pszAM = szAM;
792    if (!pszPM)
793       pszPM = szPM;
794
795    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_S1159, pszAM, cchRESOURCE))
796       lstrcpy (pszAM, TEXT("AM"));
797
798    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_S2359, pszPM, cchRESOURCE))
799       lstrcpy (pszPM, TEXT("PM"));
800
801    SIZE sAM; // size of "AM"
802    GetTextExtentPoint (hdc, pszAM, lstrlen(pszAM), &sAM);
803
804    SIZE sPM; // size of "PM"
805    GetTextExtentPoint (hdc, pszPM, lstrlen(pszPM), &sPM);
806    pSize->cx = max( sAM.cx, sPM.cx );
807    pSize->cy = max( sAM.cy, sPM.cy );
808 }
809