1642608a544f09bdd92f36322b288ab26f6e49ef
[openafs.git] / src / WINNT / afsapplib / ctl_date.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_date.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) 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)
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  * DATE _______________________________________________________________________
68  *
69  */
70
71 typedef struct
72    {
73    HWND hDate;
74    HWND hFirst; // one of hYear, hMonth, hDay
75    HWND hYear;
76    HWND hSep1;
77    HWND hMonth;
78    HWND hSep2;
79    HWND hDay;
80    HWND hSpinner;
81    HWND hSpinnerBuddy;
82
83    WORD idYear;
84    WORD idMonth;
85    WORD idDay;
86
87    SYSTEMTIME dateNow;  // only wYear,wMonth,wDay fields are relevant
88
89    DWORD dwFormat;      // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D
90    BOOL  fCallingBack;
91    } DateInfo;
92
93 static CRITICAL_SECTION csDate;
94 static DateInfo        *aDate = NULL;
95 static size_t           cDate = 0;
96
97 #define cszDATECLASS TEXT("Date")
98
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);
102
103 void Date_SendCallback (DateInfo *pdi, WORD eln, LPARAM lp);
104
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);
110
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);
114
115
116 BOOL RegisterDateClass (void)
117 {
118    static BOOL fRegistered = FALSE;
119
120    if (!fRegistered)
121       {
122       InitializeCriticalSection (&csDate);
123
124       WNDCLASS wc;
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;
132
133       if (RegisterClass (&wc))
134          fRegistered = TRUE;
135       }
136
137    return fRegistered;
138 }
139
140
141 void Date_SendCallback (DateInfo *pdi, WORD eln, LPARAM lp)
142 {
143    if (!pdi->fCallingBack)
144       {
145       pdi->fCallingBack = TRUE;
146
147       SendMessage (GetParent (pdi->hDate),
148                    WM_COMMAND,
149                    MAKELONG ((WORD)GetWindowLong (pdi->hDate, GWL_ID), eln),
150                    lp);
151
152       pdi->fCallingBack = FALSE;
153       }
154 }
155
156
157 BOOL CALLBACK DateProc (HWND hDate, UINT msg, WPARAM wp, LPARAM lp)
158 {
159    DateInfo *pdi = NULL;
160
161    EnterCriticalSection (&csDate);
162
163    if (msg == WM_CREATE)
164       {
165       for (size_t iDate = 0; iDate < cDate; ++iDate)
166          {
167          if (aDate[ iDate ].hDate == NULL)
168             break;
169          }
170       if (iDate >= cDate)
171          {
172          if (!REALLOC (aDate, cDate, 1+iDate, 4))
173             return FALSE;
174          }
175
176       memset (&aDate[ iDate ], 0x00, sizeof(DateInfo));
177       aDate[ iDate ].hDate = hDate;
178
179       pdi = &aDate[ iDate ];
180       }
181    else
182       {
183       for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
184          {
185          if (aDate[ iDate ].hDate == hDate)
186             pdi = &aDate[ iDate ];
187          }
188       }
189
190    LeaveCriticalSection (&csDate);
191
192    if (pdi != NULL)
193       {
194       switch (msg)
195          {
196          case WM_CREATE:
197             Date_OnCreate (pdi);
198             break;
199
200          case WM_DESTROY:
201             Date_OnDestroy (pdi);
202             break;
203
204          case WM_RBUTTONDOWN:
205          case WM_LBUTTONDOWN:
206             Date_OnButtonDown (pdi, msg, wp, lp);
207             break;
208
209          case WM_SETFOCUS:
210             PostMessage (GetParent(hDate), WM_NEXTDLGCTL, (WPARAM)pdi->hFirst, TRUE);
211             break;
212
213          case WM_ENABLE:
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));
219
220             RECT rDate;
221             GetRectInParent (hDate, &rDate);
222             InvalidateRect (GetParent(hDate), &rDate, TRUE);
223             UpdateWindow (GetParent(hDate));
224             break;
225
226          case WM_SYSCHAR:
227          case WM_CHAR:
228             switch (wp)
229                {
230                case VK_UP:
231                   PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)pdi->hSpinner);
232                   break;
233
234                case VK_DOWN:
235                   PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)pdi->hSpinner);
236                   break;
237
238                case VK_PRIOR:
239                   PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)pdi->hSpinner);
240                   break;
241
242                case VK_NEXT:
243                   PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)pdi->hSpinner);
244                   break;
245
246                case VK_HOME:
247                   PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)pdi->hSpinner);
248                   break;
249
250                case VK_END:
251                   PostMessage (GetParent(pdi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)pdi->hSpinner);
252                   break;
253                }
254             break;
255
256          case DM_GETDATE:
257             return Date_OnGetDate (pdi, wp, lp);
258
259          case DM_SETDATE:
260             return Date_OnSetDate (pdi, wp, lp);
261          }
262       }
263
264    return DefWindowProc (hDate, msg, wp, lp);
265 }
266
267
268 void Date_OnCreate (DateInfo *pdi)
269 {
270    Subclass_AddHook (GetParent(pdi->hDate), DateDlgProc);
271
272    TCHAR szDateSep[ cchRESOURCE ];
273    if (!GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SDATE, szDateSep, cchRESOURCE))
274       lstrcpy (szDateSep, TEXT("/"));
275
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
279    else
280       pdi->dwFormat = atoi(szDateFormat);
281
282    RECT rDate;
283    GetClientRect (pdi->hDate, &rDate);
284
285    POINT ptDate = {0,0};
286    ClientToScreen (pdi->hDate, &ptDate);
287    ScreenToClient (GetParent (pdi->hDate), &ptDate);
288
289    SIZE s88; // size of widest likely double-digit number
290    SIZE sDateSep; // size of ":"
291
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);
297
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 );
302
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.
306
307    pdi->idYear  = 100+NextControlID (GetParent (pdi->hDate));
308    pdi->idMonth = pdi->idYear +1;
309    pdi->idDay   = pdi->idYear +2;
310
311    LONG xx = ptDate.x;
312    LONG yy = ptDate.y;
313    LONG cy = cyRECT(rDate);
314
315    LONG cx;
316    int id;
317    HWND hWnd;
318    int iiFirst;
319
320    // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=M,1=D,2=Y
321    //
322    switch (pdi->dwFormat)
323       {
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;
327       }
328
329    hWnd = CreateWindow (
330                 TEXT("EDIT"),
331                 TEXT(""),
332                 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
333                 xx, yy, cx, cy,
334                 GetParent(pdi->hDate),
335                 (HMENU)id,
336                 THIS_HINST,
337                 0);
338    xx += cx;
339
340    switch (pdi->dwFormat)
341       {
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;
345       }
346    pdi->hFirst = hWnd;
347
348    // Create a separator
349    //
350    pdi->hSep1 = CreateWindow (
351                 TEXT("STATIC"),
352                 szDateSep,
353                 WS_CHILD | SS_CENTER,
354                 xx, yy, sDateSep.cx, cy,
355                 GetParent(pdi->hDate),
356                 (HMENU)-1,
357                 THIS_HINST,
358                 0);
359    xx += sDateSep.cx;
360
361    // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=D,1=M,2=M
362    //
363    switch (pdi->dwFormat)
364       {
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;
368       }
369
370    hWnd = CreateWindow (
371                 TEXT("EDIT"),
372                 TEXT(""),
373                 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
374                 xx, yy, cx, cy,
375                 GetParent(pdi->hDate),
376                 (HMENU)id,
377                 THIS_HINST,
378                 0);
379    xx += cx;
380
381    switch (pdi->dwFormat)
382       {
383       case 0:  pdi->hDay    = hWnd;  break;
384       case 1:  pdi->hMonth  = hWnd;  break;
385       case 2:  pdi->hMonth  = hWnd;  break;
386       }
387
388    // Create a separator
389    //
390    pdi->hSep2 = CreateWindow (
391                 TEXT("STATIC"),
392                 szDateSep,
393                 WS_CHILD | SS_CENTER,
394                 xx, yy, sDateSep.cx, cy,
395                 GetParent(pdi->hDate),
396                 (HMENU)-1,
397                 THIS_HINST,
398                 0);
399    xx += sDateSep.cx;
400
401    // 0=M/D/Y, 1=D/M/Y, 2=Y/M/D --so, create edit box: 0=Y,1=Y,2=D
402    //
403    switch (pdi->dwFormat)
404       {
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;
408       }
409
410    hWnd = CreateWindow (
411                 TEXT("EDIT"),
412                 TEXT(""),
413                 WS_CHILD | WS_TABSTOP | ES_RIGHT | ES_NUMBER | ES_MULTILINE,
414                 xx, yy, cx, cy,
415                 GetParent(pdi->hDate),
416                 (HMENU)id,
417                 THIS_HINST,
418                 0);
419    xx += cx;
420
421    switch (pdi->dwFormat)
422       {
423       case 0:  pdi->hYear   = hWnd;  break;
424       case 1:  pdi->hYear   = hWnd;  break;
425       case 2:  pdi->hDay    = hWnd;  break;
426       }
427
428    // Subclass the edit controls
429    //
430    Subclass_AddHook (pdi->hYear,  DateEditProc);
431    Subclass_AddHook (pdi->hMonth, DateEditProc);
432    Subclass_AddHook (pdi->hDay,   DateEditProc);
433
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));
440
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));
446
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);
452
453    RECT rWindow;
454    GetWindowRect (pdi->hDate, &rWindow);
455    rWindow.right += (cxYear + cxMonth + cxDay + sDateSep.cx*2) - cxRECT(rDate);
456
457    SetWindowPos (pdi->hDate, NULL, 0, 0, cxRECT(rWindow), cyRECT(rWindow),
458                  SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
459
460    RECT rSpinner;
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;
468
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);
473 }
474
475
476 void Date_OnDestroy (DateInfo *pdi)
477 {
478    Subclass_RemoveHook (GetParent (pdi->hDate), DateDlgProc);
479    pdi->hDate = NULL;
480 }
481
482
483 void Date_OnButtonDown (DateInfo *pdi, UINT msg, WPARAM wp, LPARAM lp)
484 {
485    DWORD dw = GetMessagePos();
486    POINT pt;
487    pt.x = LOWORD(dw);
488    pt.y = HIWORD(dw);  // in screen coordinates
489
490    RECT rTarget;
491    HWND hTarget = 0;
492
493    GetWindowRect (pdi->hYear, &rTarget);
494    if (PtInRect (&rTarget, pt))
495       hTarget = pdi->hYear;
496
497    GetWindowRect (pdi->hMonth, &rTarget);
498    if (PtInRect (&rTarget, pt))
499       hTarget = pdi->hMonth;
500
501    GetWindowRect (pdi->hDay, &rTarget);
502    if (PtInRect (&rTarget, pt))
503       hTarget = pdi->hDay;
504
505    if (hTarget != 0)
506       {
507       PostMessage (hTarget, msg, wp, lp);
508       }
509 }
510
511
512 BOOL Date_OnGetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
513 {
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;
519    return TRUE;
520 }
521
522
523 BOOL Date_OnSetDate (DateInfo *pdi, WPARAM wp, LPARAM lp)
524 {
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);
530    return TRUE;
531 }
532
533
534 BOOL CALLBACK DateDlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
535 {
536    PVOID oldProc = Subclass_FindNextHook (hDlg, DateDlgProc);
537    size_t iDate;
538
539    switch (msg)
540       {
541       case WM_CTLCOLOREDIT:
542       case WM_CTLCOLORSTATIC:
543          for (iDate = 0; iDate < cDate; ++iDate)
544             {
545             if (aDate[ iDate ].hDate == NULL)
546                continue;
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) )
552                {
553                COLORREF clr;
554                if (IsWindowEnabled (aDate[ iDate ].hDate))
555                   clr = GetSysColor (COLOR_WINDOW);
556                else
557                   clr = GetSysColor (COLOR_BTNFACE);
558                SetBkColor ((HDC)wp, clr);
559                return (BOOL)CreateSolidBrush (clr);
560                }
561             }
562          break;
563
564       case WM_COMMAND:
565          for (iDate = 0; iDate < cDate; ++iDate)
566             {
567             if (aDate[ iDate ].hDate == NULL)
568                continue;
569             if ( (aDate[ iDate ].idYear   == LOWORD(wp)) ||
570                  (aDate[ iDate ].idMonth  == LOWORD(wp)) ||
571                  (aDate[ iDate ].idDay    == LOWORD(wp)) )
572                {
573                if (HIWORD(wp) == SPN_UPDATE)
574                   {
575                   Date_Edit_OnUpdate (&aDate[ iDate ], GetDlgItem(hDlg,LOWORD(wp)));
576                   }
577                break;
578                }
579             }
580          break;
581       }
582
583    if (oldProc)
584       return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
585    else
586       return DefWindowProc (hDlg, msg, wp, lp);
587 }
588
589
590 BOOL CALLBACK DateEditProc (HWND hEdit, UINT msg, WPARAM wp, LPARAM lp)
591 {
592    DateInfo *pdi = NULL;
593
594    EnterCriticalSection (&csDate);
595
596    for (size_t iDate = 0; !pdi && iDate < cDate; ++iDate)
597       {
598       if ( (aDate[ iDate ].hYear  == hEdit) ||
599            (aDate[ iDate ].hMonth == hEdit) ||
600            (aDate[ iDate ].hDay   == hEdit) )
601          {
602          pdi = &aDate[ iDate ];
603          }
604       }
605
606    LeaveCriticalSection (&csDate);
607
608    if (pdi)
609       {
610       switch (msg)
611          {
612          case WM_SETFOCUS:
613             Date_Edit_OnSetFocus (pdi, hEdit);
614             break;
615          }
616       }
617
618    PVOID oldProc = Subclass_FindNextHook (hEdit, DateEditProc);
619    if (oldProc)
620       return CallWindowProc ((WNDPROC)oldProc, hEdit, msg, wp, lp);
621    else
622       return DefWindowProc (hEdit, msg, wp, lp);
623 }
624
625
626 void Date_Edit_OnSetFocus (DateInfo *pdi, HWND hEdit)
627 {
628    DWORD dwMin;
629    DWORD dwNow;
630    DWORD dwMax;
631
632    if (hEdit == pdi->hYear)
633       {
634       dwMin = 1970;
635       dwNow = pdi->dateNow.wYear;
636       dwMax = 2999;
637       }
638    else if (hEdit == pdi->hMonth)
639       {
640       dwMin = 1;
641       dwNow = pdi->dateNow.wMonth;
642       dwMax = 12;
643       }
644    else // (hEdit == pdi->hDay)
645       {
646       dwMin = 1;
647       dwNow = pdi->dateNow.wDay;
648       dwMax = 31;
649       }
650
651    if (pdi->hSpinnerBuddy != hEdit)
652       {
653       SP_SetBuddy (pdi->hSpinnerBuddy, hEdit, FALSE);
654       pdi->hSpinnerBuddy = hEdit;
655       }
656
657    SP_SetRange (hEdit, dwMin, dwMax);
658    SP_SetPos (hEdit, dwNow);
659
660    SendMessage (hEdit, EM_SETSEL, (WPARAM)0, (LPARAM)-1);  // select all
661 }
662
663
664 void Date_Edit_OnUpdate (DateInfo *pdi, HWND hEdit)
665 {
666    TCHAR szText[ cchRESOURCE ];
667
668    if (hEdit == pdi->hYear)
669       {
670       GetWindowText (pdi->hYear, szText, 256);
671       pdi->dateNow.wYear = (WORD)atol (szText);
672       }
673
674    if (hEdit == pdi->hMonth)
675       {
676       GetWindowText (pdi->hMonth, szText, 256);
677       pdi->dateNow.wMonth = (WORD)atol (szText);
678       }
679
680    if (hEdit == pdi->hDay)
681       {
682       GetWindowText (pdi->hDay, szText, 256);
683       pdi->dateNow.wDay = (WORD)atol (szText);
684       }
685
686    SYSTEMTIME st = pdi->dateNow;
687    Date_SendCallback (pdi, DN_UPDATE, (LPARAM)&st);
688 }
689
690
691 void Date_Edit_SetText (DateInfo *pdi, HWND hEdit)
692 {
693    DWORD dwNow;
694
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;
701
702    if (pdi->hSpinnerBuddy == hEdit)
703       {
704       SP_SetPos (hEdit, dwNow);
705       }
706    else
707       {
708       TCHAR szText[ cchRESOURCE ];
709       wsprintf (szText, TEXT("%lu"), dwNow);
710       SetWindowText (hEdit, szText);
711       }
712 }
713