c4595434e9e885096d611058ea23ff24d380428e
[openafs.git] / src / WINNT / afsapplib / ctl_elapsed.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 <WINNT/dialog.h>
19 #include <WINNT/resize.h>       // for GetRectInParent()
20 #include <WINNT/subclass.h>
21 #include <WINNT/ctl_spinner.h>
22 #include <WINNT/ctl_elapsed.h>
23 #include <WINNT/TaLocale.h>
24
25 #ifndef cchRESOURCE
26 #define cchRESOURCE 256
27 #endif
28
29
30 /*
31  * MISCELLANEOUS ______________________________________________________________
32  *
33  */
34
35 #ifndef REALLOC
36 #define REALLOC(_a,_c,_r,_i) ElapsedReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
37 BOOL ElapsedReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
38 {
39    LPVOID pNew;
40    size_t cNew;
41
42    if (cReq <= *pcTarget)
43       return TRUE;
44
45    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
46       return FALSE;
47
48    if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
49       return FALSE;
50    memset (pNew, 0x00, cbElement * cNew);
51
52    if (*pcTarget != 0)
53       {
54       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
55       Free (*ppTarget);
56       }
57
58    *ppTarget = pNew;
59    *pcTarget = cNew;
60    return TRUE;
61 }
62 #endif
63
64
65
66 /*
67  * ELAPSEDS ___________________________________________________________________
68  *
69  */
70
71 typedef struct
72    {
73    HWND hElapsed;
74    HWND hHours;
75    HWND hSep1;
76    HWND hMinutes;
77    HWND hSep2;
78    HWND hSeconds;
79    HWND hSpinner;
80    HWND hSpinnerBuddy;
81
82    WORD idHours;
83    WORD idMinutes;
84    WORD idSeconds;
85
86    SYSTEMTIME timeMin;  // only hour, minute, second fields are relevant
87    SYSTEMTIME timeNow;  // only hour, minute, second fields are relevant
88    SYSTEMTIME timeMax;  // only hour, minute, second fields are relevant
89
90    BOOL  fCallingBack;
91    BOOL  fCanCallBack;
92    } ElapsedInfo;
93
94 static CRITICAL_SECTION csElapsed;
95 static ElapsedInfo     *aElapsed = NULL;
96 static size_t           cElapsed = 0;
97
98 #define cszELAPSEDCLASS TEXT("Elapsed")
99
100 HRESULT CALLBACK ElapsedProc (HWND hElapsed, UINT msg, WPARAM wp, LPARAM lp);
101 HRESULT CALLBACK ElapsedEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp);
102 HRESULT CALLBACK ElapsedDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
103
104 void Elapsed_SendCallback (ElapsedInfo *pei, WORD eln, LPARAM lp);
105
106 void Elapsed_OnCreate (ElapsedInfo *psi);
107 void Elapsed_OnDestroy (ElapsedInfo *psi);
108 void Elapsed_OnButtonDown (ElapsedInfo *pei, UINT msg, WPARAM wp, LPARAM lp);
109 BOOL Elapsed_OnGetRange (ElapsedInfo *psi, WPARAM wp, LPARAM lp);
110 BOOL Elapsed_OnSetRange (ElapsedInfo *psi, WPARAM wp, LPARAM lp);
111 BOOL Elapsed_OnGetTime (ElapsedInfo *psi, WPARAM wp, LPARAM lp);
112 BOOL Elapsed_OnSetTime (ElapsedInfo *psi, WPARAM wp, LPARAM lp);
113
114 void Elapsed_Edit_OnSetFocus (ElapsedInfo *pei, HWND hEdit);
115 void Elapsed_Edit_GetSpinnerRange (ElapsedInfo *pei, HWND hEdit, DWORD *pdwMin, DWORD *pdwPos, DWORD *pdwMax);
116 void Elapsed_Edit_OnUpdate (ElapsedInfo *pei, HWND hEdit, DWORD dwNew);
117 void Elapsed_Edit_OnEnforceRange (ElapsedInfo *pei, HWND hEdit);
118 void Elapsed_Edit_SetText (ElapsedInfo *pei, HWND hEdit);
119
120
121 BOOL RegisterElapsedClass (void)
122 {
123    static BOOL fRegistered = FALSE;
124
125    if (!fRegistered)
126       {
127       InitializeCriticalSection (&csElapsed);
128
129       WNDCLASS wc;
130       memset (&wc, 0x00, sizeof(wc));
131       wc.style = CS_GLOBALCLASS;
132       wc.lpfnWndProc = (WNDPROC)ElapsedProc;
133       wc.hInstance = THIS_HINST;
134       wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
135       wc.hbrBackground = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
136       wc.lpszClassName = cszELAPSEDCLASS;
137
138       if (RegisterClass (&wc))
139          fRegistered = TRUE;
140       }
141
142    return fRegistered;
143 }
144
145
146 void Elapsed_SendCallback (ElapsedInfo *pei, WORD eln, LPARAM lp)
147 {
148    if ((pei->fCanCallBack == TRUE) && !pei->fCallingBack)
149       {
150       pei->fCallingBack = TRUE;
151
152       SendMessage (GetParent (pei->hElapsed),
153                    WM_COMMAND,
154                    MAKELONG ((WORD)GetWindowLong (pei->hElapsed, GWL_ID), eln),
155                    lp);
156
157       pei->fCallingBack = FALSE;
158       }
159 }
160
161
162 HRESULT CALLBACK ElapsedProc (HWND hElapsed, UINT msg, WPARAM wp, LPARAM lp)
163 {
164    ElapsedInfo *pei = NULL;
165
166    EnterCriticalSection (&csElapsed);
167
168    if (msg == WM_CREATE)
169       {
170       size_t iElapsed;
171       for (iElapsed = 0; iElapsed < cElapsed; ++iElapsed)
172          {
173          if (aElapsed[ iElapsed ].hElapsed == NULL)
174             break;
175          }
176       if (iElapsed >= cElapsed)
177          {
178          if (!REALLOC (aElapsed, cElapsed, 1+iElapsed, 4))
179             return FALSE;
180          }
181
182       memset (&aElapsed[ iElapsed ], 0x00, sizeof(ElapsedInfo));
183       aElapsed[ iElapsed ].hElapsed = hElapsed;
184
185       pei = &aElapsed[ iElapsed ];
186       }
187    else
188       {
189       for (size_t iElapsed = 0; !pei && iElapsed < cElapsed; ++iElapsed)
190          {
191          if (aElapsed[ iElapsed ].hElapsed == hElapsed)
192             pei = &aElapsed[ iElapsed ];
193          }
194       }
195
196    LeaveCriticalSection (&csElapsed);
197
198    if (pei != NULL)
199       {
200       switch (msg)
201          {
202          case WM_CREATE:
203             Elapsed_OnCreate (pei);
204             break;
205
206          case WM_DESTROY:
207             Elapsed_OnDestroy (pei);
208             break;
209
210          case WM_RBUTTONDOWN:
211          case WM_LBUTTONDOWN:
212             Elapsed_OnButtonDown (pei, msg, wp, lp);
213             break;
214
215          case WM_SETFOCUS:
216             PostMessage (GetParent(hElapsed), WM_NEXTDLGCTL, (WPARAM)pei->hHours, TRUE);
217             break;
218
219          case WM_ENABLE:
220             EnableWindow (pei->hHours,   IsWindowEnabled (hElapsed));
221             EnableWindow (pei->hSep1,    IsWindowEnabled (hElapsed));
222             EnableWindow (pei->hMinutes, IsWindowEnabled (hElapsed));
223             EnableWindow (pei->hSep2,    IsWindowEnabled (hElapsed));
224             EnableWindow (pei->hSeconds, IsWindowEnabled (hElapsed));
225             EnableWindow (pei->hSpinner, IsWindowEnabled (hElapsed));
226
227             RECT rElapsed;
228             GetRectInParent (hElapsed, &rElapsed);
229             InvalidateRect (GetParent(hElapsed), &rElapsed, TRUE);
230             UpdateWindow (GetParent(hElapsed));
231             break;
232
233          case WM_SYSCHAR:
234          case WM_CHAR:
235             switch (wp)
236                {
237                case VK_UP:
238                   PostMessage (GetParent(pei->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)pei->hSpinner);
239                   break;
240
241                case VK_DOWN:
242                   PostMessage (GetParent(pei->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)pei->hSpinner);
243                   break;
244
245                case VK_PRIOR:
246                   PostMessage (GetParent(pei->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)pei->hSpinner);
247                   break;
248
249                case VK_NEXT:
250                   PostMessage (GetParent(pei->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)pei->hSpinner);
251                   break;
252
253                case VK_HOME:
254                   PostMessage (GetParent(pei->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)pei->hSpinner);
255                   break;
256
257                case VK_END:
258                   PostMessage (GetParent(pei->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)pei->hSpinner);
259                   break;
260                }
261             break;
262
263          case ELM_GETRANGE:
264             return Elapsed_OnGetRange (pei, wp, lp);
265
266          case ELM_SETRANGE:
267             return Elapsed_OnSetRange (pei, wp, lp);
268
269          case ELM_GETTIME:
270             return Elapsed_OnGetTime (pei, wp, lp);
271
272          case ELM_SETTIME:
273             return Elapsed_OnSetTime (pei, wp, lp);
274          }
275       }
276
277    return DefWindowProc (hElapsed, msg, wp, lp);
278 }
279
280
281 void Elapsed_OnCreate (ElapsedInfo *pei)
282 {
283    Subclass_AddHook (GetParent(pei->hElapsed), ElapsedDlgProc);
284
285    TCHAR szTimeSep[ cchRESOURCE ];
286    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSep, cchRESOURCE))
287       lstrcpy (szTimeSep, TEXT(":"));
288
289    RECT rElapsed;
290    GetClientRect (pei->hElapsed, &rElapsed);
291
292    POINT ptElapsed = {0,0};
293    ClientToScreen (pei->hElapsed, &ptElapsed);
294    ScreenToClient (GetParent (pei->hElapsed), &ptElapsed);
295
296    SIZE s88; // size of widest likely double-digit number
297    SIZE s888; // maximum width to which we'll go for second- and minute- fields
298    SIZE sTimeSep; // size of ":"
299
300    HDC hdc = GetDC (GetParent (pei->hElapsed));
301    GetTextExtentPoint (hdc, TEXT("88"), lstrlen(TEXT("88")), &s88);
302    GetTextExtentPoint (hdc, szTimeSep, lstrlen(szTimeSep), &sTimeSep);
303    ReleaseDC (GetParent (pei->hElapsed), hdc);
304
305    s888 = s88;
306    s888.cx = (LONG)( (double)(s88.cx) * 1.2 );
307
308    LONG cxNumbers = cxRECT(rElapsed) - (2 * sTimeSep.cx) - GetSystemMetrics (SM_CXVSCROLL);
309    LONG cxSeconds = min( max( cxNumbers/3, s88.cx ), s888.cx );
310    LONG cxMinutes = min( max( cxNumbers/3, s88.cx ), s888.cx );
311    LONG cxHours   = cxNumbers - cxSeconds - cxMinutes;
312    LONG yy = ptElapsed.y;
313    LONG cy = cyRECT(rElapsed);
314
315    pei->idHours = 100+NextControlID (GetParent (pei->hElapsed));
316    pei->hHours = CreateWindow (
317                 TEXT("EDIT"),
318                 TEXT(""),
319                 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER,
320                 ptElapsed.x, yy, cxHours, cy,
321                 GetParent(pei->hElapsed),
322                 (HMENU)pei->idHours,
323                 THIS_HINST,
324                 0);
325
326    pei->hSep1 = CreateWindow (
327                 TEXT("STATIC"),
328                 szTimeSep,
329                 WS_CHILD,
330                 ptElapsed.x + cxHours, yy, sTimeSep.cx, cy,
331                 GetParent(pei->hElapsed),
332                 (HMENU)-1,
333                 THIS_HINST,
334                 0);
335
336    pei->idMinutes = 100+NextControlID (GetParent (pei->hElapsed));
337    pei->hMinutes = CreateWindow (
338                 TEXT("EDIT"),
339                 TEXT(""),
340                 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER,
341                 ptElapsed.x + cxHours + sTimeSep.cx, yy, cxMinutes, cy,
342                 GetParent(pei->hElapsed),
343                 (HMENU)pei->idMinutes,
344                 THIS_HINST,
345                 0);
346
347    pei->hSep2 = CreateWindow (
348                 TEXT("STATIC"),
349                 szTimeSep,
350                 WS_CHILD,
351                 ptElapsed.x + cxHours + cxMinutes + sTimeSep.cx, yy, sTimeSep.cx, cy,
352                 GetParent(pei->hElapsed),
353                 (HMENU)-1,
354                 THIS_HINST,
355                 0);
356
357    pei->idSeconds = 100+NextControlID (GetParent (pei->hElapsed));
358    pei->hSeconds = CreateWindow (
359                 TEXT("EDIT"),
360                 TEXT(""),
361                 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER,
362                 ptElapsed.x + cxHours + cxMinutes + 2 * sTimeSep.cx, yy, cxSeconds, cy,
363                 GetParent(pei->hElapsed),
364                 (HMENU)pei->idSeconds,
365                 THIS_HINST,
366                 0);
367
368    Subclass_AddHook (pei->hHours,   ElapsedEditProc);
369    Subclass_AddHook (pei->hMinutes, ElapsedEditProc);
370    Subclass_AddHook (pei->hSeconds, ElapsedEditProc);
371
372    HFONT hf = (HFONT)SendMessage (GetParent (pei->hElapsed), WM_GETFONT, 0, 0);
373    SendMessage (pei->hHours,   WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
374    SendMessage (pei->hSep1,    WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
375    SendMessage (pei->hMinutes, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
376    SendMessage (pei->hSep2,    WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
377    SendMessage (pei->hSeconds, WM_SETFONT, (WPARAM)hf, MAKELPARAM(TRUE,0));
378
379    EnableWindow (pei->hHours,   IsWindowEnabled (pei->hElapsed));
380    EnableWindow (pei->hSep1,    IsWindowEnabled (pei->hElapsed));
381    EnableWindow (pei->hMinutes, IsWindowEnabled (pei->hElapsed));
382    EnableWindow (pei->hSep2,    IsWindowEnabled (pei->hElapsed));
383    EnableWindow (pei->hSeconds, IsWindowEnabled (pei->hElapsed));
384
385    ShowWindow (pei->hHours,   TRUE);
386    ShowWindow (pei->hSep1,    TRUE);
387    ShowWindow (pei->hMinutes, TRUE);
388    ShowWindow (pei->hSep2,    TRUE);
389    ShowWindow (pei->hSeconds, TRUE);
390
391    RECT rWindow;
392    GetWindowRect (pei->hElapsed, &rWindow);
393    rWindow.right += (cxHours + cxMinutes + cxSeconds + 2 * sTimeSep.cx) - cxRECT(rElapsed);
394
395    SetWindowPos (pei->hElapsed, NULL, 0, 0, cxRECT(rWindow), cyRECT(rWindow),
396                  SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
397
398    SET_ELAPSED_TIME(&pei->timeMin,  0, 0, 0);
399    SET_ELAPSED_TIME(&pei->timeMax, 24, 0, 0);
400
401    Elapsed_Edit_OnEnforceRange (pei, pei->hHours);
402    Elapsed_Edit_OnEnforceRange (pei, pei->hMinutes);
403    Elapsed_Edit_OnEnforceRange (pei, pei->hSeconds);
404
405    Elapsed_Edit_OnSetFocus (pei, pei->hHours);
406
407    pei->fCanCallBack = TRUE;
408 }
409
410
411 void Elapsed_OnDestroy (ElapsedInfo *pei)
412 {
413    Subclass_RemoveHook (GetParent(pei->hElapsed), ElapsedDlgProc);
414    pei->hElapsed = NULL;
415 }
416
417
418 void Elapsed_OnButtonDown (ElapsedInfo *pei, UINT msg, WPARAM wp, LPARAM lp)
419 {
420    DWORD dw = GetMessagePos();
421    POINT pt;
422    pt.x = LOWORD(dw);
423    pt.y = HIWORD(dw);  // in screen coordinates
424
425    RECT rTarget;
426    HWND hTarget = 0;
427
428    GetWindowRect (pei->hHours, &rTarget);
429    if (PtInRect (&rTarget, pt))
430       hTarget = pei->hHours;
431
432    GetWindowRect (pei->hMinutes, &rTarget);
433    if (PtInRect (&rTarget, pt))
434       hTarget = pei->hMinutes;
435
436    GetWindowRect (pei->hSeconds, &rTarget);
437    if (PtInRect (&rTarget, pt))
438       hTarget = pei->hSeconds;
439
440    if (hTarget != 0)
441       {
442       PostMessage (hTarget, msg, wp, lp);
443       }
444 }
445
446
447 BOOL Elapsed_OnGetRange (ElapsedInfo *pei, WPARAM wp, LPARAM lp)
448 {
449    SYSTEMTIME *ptimeMin = (SYSTEMTIME*)wp;
450    SYSTEMTIME *ptimeMax = (SYSTEMTIME*)lp;
451
452    *ptimeMin = pei->timeMin;
453    *ptimeMax = pei->timeMax;
454    return TRUE;
455 }
456
457
458 BOOL Elapsed_OnSetRange (ElapsedInfo *pei, WPARAM wp, LPARAM lp)
459 {
460    SYSTEMTIME *ptimeMin = (SYSTEMTIME*)wp;
461    SYSTEMTIME *ptimeMax = (SYSTEMTIME*)lp;
462
463    pei->timeMin = *ptimeMin;
464    pei->timeMax = *ptimeMax;
465
466    Elapsed_Edit_OnEnforceRange (pei, pei->hHours);
467    Elapsed_Edit_OnEnforceRange (pei, pei->hMinutes);
468    Elapsed_Edit_OnEnforceRange (pei, pei->hSeconds);
469
470    return TRUE;
471 }
472
473
474 BOOL Elapsed_OnGetTime (ElapsedInfo *pei, WPARAM wp, LPARAM lp)
475 {
476    SYSTEMTIME *ptime = (SYSTEMTIME*)lp;
477    *ptime = pei->timeNow;
478    return TRUE;
479 }
480
481
482 BOOL Elapsed_OnSetTime (ElapsedInfo *pei, WPARAM wp, LPARAM lp)
483 {
484    SYSTEMTIME *ptime = (SYSTEMTIME*)lp;
485    pei->timeNow = *ptime;
486    Elapsed_Edit_SetText (pei, pei->hHours);
487    Elapsed_Edit_SetText (pei, pei->hMinutes);
488    Elapsed_Edit_SetText (pei, pei->hSeconds);
489    return TRUE;
490 }
491
492
493 HRESULT CALLBACK ElapsedDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
494 {
495    PVOID oldProc = Subclass_FindNextHook (hDlg, ElapsedDlgProc);
496    size_t iElapsed;
497
498    switch (msg)
499       {
500       case WM_CTLCOLOREDIT:
501       case WM_CTLCOLORSTATIC:
502          for (iElapsed = 0; iElapsed < cElapsed; ++iElapsed)
503             {
504             if (aElapsed[ iElapsed ].hElapsed == NULL)
505                continue;
506             if ( (aElapsed[ iElapsed ].hHours   == (HWND)lp) ||
507                  (aElapsed[ iElapsed ].hSep1    == (HWND)lp) ||
508                  (aElapsed[ iElapsed ].hMinutes == (HWND)lp) ||
509                  (aElapsed[ iElapsed ].hSep2    == (HWND)lp) ||
510                  (aElapsed[ iElapsed ].hSeconds == (HWND)lp) )
511                {
512                COLORREF clr;
513                if (IsWindowEnabled (aElapsed[ iElapsed ].hElapsed))
514                   clr = GetSysColor (COLOR_WINDOW);
515                else
516                   clr = GetSysColor (COLOR_BTNFACE);
517                SetBkColor ((HDC)wp, clr);
518                return (HRESULT)(INT_PTR)CreateSolidBrush (clr);
519                }
520             }
521          break;
522
523       case WM_COMMAND:
524          for (iElapsed = 0; iElapsed < cElapsed; ++iElapsed)
525             {
526             if (aElapsed[ iElapsed ].hElapsed == NULL)
527                continue;
528             if ( (aElapsed[ iElapsed ].idHours   == LOWORD(wp)) ||
529                  (aElapsed[ iElapsed ].idMinutes == LOWORD(wp)) ||
530                  (aElapsed[ iElapsed ].idSeconds == LOWORD(wp)) )
531                {
532                if (HIWORD(wp) == SPN_UPDATE)
533                   {
534                   Elapsed_Edit_OnUpdate (&aElapsed[ iElapsed ], GetDlgItem (hDlg, LOWORD(wp)), (DWORD)lp);
535                   }
536                break;
537                }
538             }
539          break;
540       }
541
542    if (oldProc)
543       return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
544    else
545       return DefWindowProc (hDlg, msg, wp, lp);
546 }
547
548
549 HRESULT CALLBACK ElapsedEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
550 {
551    ElapsedInfo *pei = NULL;
552
553    EnterCriticalSection (&csElapsed);
554
555    for (size_t iElapsed = 0; !pei && iElapsed < cElapsed; ++iElapsed)
556       {
557       if ( (aElapsed[ iElapsed ].hHours   == hEdit) ||
558            (aElapsed[ iElapsed ].hMinutes == hEdit) ||
559            (aElapsed[ iElapsed ].hSeconds == hEdit) )
560          {
561          pei = &aElapsed[ iElapsed ];
562          }
563       }
564
565    LeaveCriticalSection (&csElapsed);
566
567    if (pei)
568       {
569       switch (msg)
570          {
571          case WM_SETFOCUS:
572             Elapsed_Edit_OnSetFocus (pei, hEdit);
573             break;
574          }
575       }
576
577    PVOID oldProc = Subclass_FindNextHook (hEdit, ElapsedEditProc);
578    if (oldProc)
579       return CallWindowProc ((WNDPROC)oldProc, hEdit, msg, wp, lp);
580    else
581       return DefWindowProc (hEdit, msg, wp, lp);
582 }
583
584
585 void Elapsed_Edit_OnSetFocus (ElapsedInfo *pei, HWND hEdit)
586 {
587    pei->fCanCallBack --;
588
589    RECT rSpinner;
590    GetRectInParent (pei->hElapsed, &rSpinner);
591    rSpinner.left = rSpinner.right;
592    rSpinner.right = rSpinner.left + GetSystemMetrics (SM_CXVSCROLL);
593    rSpinner.bottom -= 2; // just like Win95 does
594
595    DWORD dwMin;
596    DWORD dwPos;
597    DWORD dwMax;
598    Elapsed_Edit_GetSpinnerRange (pei, hEdit, &dwMin, &dwPos, &dwMax);
599
600    CreateSpinner (hEdit, 10, FALSE, dwMin, dwPos, dwMax, &rSpinner);
601    if (pei->hSpinner)
602       DestroyWindow (pei->hSpinner);
603
604    if (hEdit == pei->hHours)
605       SP_SetFormat (hEdit, TEXT("%lu"));
606    else
607       SP_SetFormat (hEdit, TEXT("%02lu"));
608
609    pei->hSpinner = SP_GetSpinner (hEdit);
610    pei->hSpinnerBuddy = hEdit;
611
612    SendMessage (hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1);  // select all
613
614    pei->fCanCallBack ++;
615 }
616
617
618 void Elapsed_Edit_GetSpinnerRange (ElapsedInfo *pei, HWND hEdit, DWORD *pdwMin, DWORD *pdwNow, DWORD *pdwMax)
619 {
620    if (hEdit == pei->hHours)
621       {
622       *pdwMin = pei->timeMin.wHour + pei->timeMin.wDay * 24;
623       *pdwNow = pei->timeNow.wHour + pei->timeNow.wDay * 24;
624       *pdwMax = pei->timeMax.wHour + pei->timeMax.wDay * 24;
625       }
626    else if (hEdit == pei->hMinutes)
627       {
628       if ( (pei->timeNow.wDay == pei->timeMin.wDay) &&
629            (pei->timeNow.wHour == pei->timeMin.wHour) )
630          *pdwMin = pei->timeMin.wMinute;
631       else
632          *pdwMin = 0;
633
634       *pdwNow = pei->timeNow.wMinute;
635
636       if ( (pei->timeNow.wDay == pei->timeMax.wDay) &&
637            (pei->timeNow.wHour == pei->timeMax.wHour) )
638          *pdwMax = pei->timeMax.wMinute;
639       else
640          *pdwMax = 59;
641       }
642    else if (hEdit == pei->hSeconds)
643       {
644       if ( (pei->timeNow.wDay == pei->timeMin.wDay) &&
645            (pei->timeNow.wHour == pei->timeMin.wHour) &&
646            (pei->timeNow.wMinute == pei->timeMin.wMinute) )
647          *pdwMin = pei->timeMin.wSecond;
648       else
649          *pdwMin = 0;
650
651       *pdwNow = pei->timeNow.wSecond;
652
653       if ( (pei->timeNow.wDay == pei->timeMax.wDay) &&
654            (pei->timeNow.wHour == pei->timeMax.wHour) &&
655            (pei->timeNow.wMinute == pei->timeMax.wMinute) )
656          *pdwMax = pei->timeMax.wSecond;
657       else
658          *pdwMax = 59;
659       }
660 }
661
662
663 void Elapsed_Edit_OnUpdate (ElapsedInfo *pei, HWND hEdit, DWORD dwNew)
664 {
665    DWORD dwMin;
666    DWORD dwNow;
667    DWORD dwMax;
668
669    Elapsed_Edit_GetSpinnerRange (pei, hEdit, &dwMin, &dwNow, &dwMax);
670
671    dwNow = limit( dwMin, dwNew, dwMax );
672
673    if (hEdit == pei->hHours)
674       {
675       pei->timeNow.wDay = (WORD)dwNow / 24;
676       pei->timeNow.wHour = (WORD)dwNow % 24;
677       Elapsed_Edit_OnEnforceRange (pei, pei->hMinutes);
678       Elapsed_Edit_OnEnforceRange (pei, pei->hSeconds);
679       }
680    else if (hEdit == pei->hMinutes)
681       {
682       pei->timeNow.wMinute = (WORD)dwNow;
683       Elapsed_Edit_OnEnforceRange (pei, pei->hSeconds);
684       }
685    else if (hEdit == pei->hSeconds)
686       {
687       pei->timeNow.wSecond = (WORD)dwNow;
688       }
689
690    SYSTEMTIME st = pei->timeNow;
691    Elapsed_SendCallback (pei, ELN_UPDATE, (LPARAM)&st);
692 }
693
694
695 void Elapsed_Edit_OnEnforceRange (ElapsedInfo *pei, HWND hEdit)
696 {
697    DWORD dwMin;
698    DWORD dwNow;
699    DWORD dwMax;
700    Elapsed_Edit_GetSpinnerRange (pei, hEdit, &dwMin, &dwNow, &dwMax);
701
702    if (!inlimit( dwMin, dwNow, dwMax ))
703       {
704       dwNow = limit( dwMin, dwNow, dwMax );
705
706       if (hEdit == pei->hHours)
707          {
708          pei->timeNow.wDay = (WORD)dwNow / 24;
709          pei->timeNow.wHour = (WORD)dwNow % 24;
710          }
711       else if (hEdit == pei->hMinutes)
712          {
713          pei->timeNow.wMinute = (WORD)dwNow;
714          }
715       else if (hEdit == pei->hSeconds)
716          {
717          pei->timeNow.wSecond = (WORD)dwNow;
718          }
719
720       if (pei->hSpinnerBuddy == hEdit)
721          SP_SetRange (hEdit, dwMin, dwMax);
722       Elapsed_Edit_SetText (pei, hEdit);
723       }
724    else if (pei->hSpinnerBuddy == hEdit)
725       {
726       SP_SetRange (hEdit, dwMin, dwMax);
727       }
728 }
729
730
731 void Elapsed_Edit_SetText (ElapsedInfo *pei, HWND hEdit)
732 {
733    DWORD dwMin;
734    DWORD dwNow;
735    DWORD dwMax;
736    Elapsed_Edit_GetSpinnerRange (pei, hEdit, &dwMin, &dwNow, &dwMax);
737
738    if (pei->hSpinnerBuddy == hEdit)
739       {
740       SP_SetPos (hEdit, dwNow);
741       }
742    else
743       {
744       TCHAR szText[ cchRESOURCE ];
745
746       if (hEdit == pei->hHours)
747          wsprintf (szText, TEXT("%lu"), dwNow);
748       else
749          wsprintf (szText, TEXT("%02lu"), dwNow);
750
751       SetWindowText (hEdit, szText);
752       }
753 }
754