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