Windows: Update fs newcell and add VIOCNEWCELL2
[openafs.git] / src / WINNT / afsapplib / ctl_spinner.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/subclass.h>
20 #include <WINNT/ctl_spinner.h>
21 #include <WINNT/TaLocale.h>
22
23 #ifndef cchRESOURCE
24 #define cchRESOURCE 256
25 #endif
26
27
28 /*
29  * MISCELLANEOUS ______________________________________________________________
30  *
31  */
32
33 #define ishexdigit(_ch) ( (((_ch) >= 'a') && ((_ch) <= 'f')) || \
34                           (((_ch) >= 'A') && ((_ch) <= 'F')) )
35
36 #define digitval(_ch) (isdigit(_ch) \
37                        ? ((DWORD)(_ch) - (DWORD)TEXT('0')) \
38                        : (((_ch) >= 'a') && ((_ch) <= 'f')) \
39                          ? ((DWORD)(_ch) - (DWORD)TEXT('a') + 10) \
40                          : (((_ch) >= 'A') && ((_ch) <= 'F')) \
41                            ? ((DWORD)(_ch) - (DWORD)TEXT('A') + 10) \
42                            : 0)
43
44 #ifndef REALLOC
45 #define REALLOC(_a,_c,_r,_i) SpinnerReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
46 BOOL SpinnerReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
47 {
48    LPVOID pNew;
49    size_t cNew;
50
51    if (cReq <= *pcTarget)
52       return TRUE;
53
54    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
55       return FALSE;
56
57    if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
58       return FALSE;
59    memset (pNew, 0x00, cbElement * cNew);
60
61    if (*pcTarget != 0)
62       {
63       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
64       Free (*ppTarget);
65       }
66
67    *ppTarget = pNew;
68    *pcTarget = cNew;
69    return TRUE;
70 }
71 #endif
72
73
74 /*
75  * SPINNERS ___________________________________________________________________
76  *
77  */
78
79 typedef struct
80    {
81    HWND hSpinner;
82    HWND hBuddy;
83    RECT rReq;
84
85    DWORD dwMin;
86    DWORD dwMax;
87    WORD  wBase;
88    BOOL  fSigned;
89    DWORD dwPos;
90
91    BOOL  fNewText;
92    BOOL  fCallingBack;
93    BOOL  fCanCallBack;
94    LPTSTR pszFormat;
95    } SpinnerInfo;
96
97 static CRITICAL_SECTION csSpinners;
98 static SpinnerInfo     *aSpinners = NULL;
99 static size_t           cSpinners = 0;
100 static LONG             oldSpinnerProc = 0;
101
102 #define cszSPINNERCLASS TEXT("Spinner")
103
104 HRESULT CALLBACK SpinnerProc (HWND hSpin, UINT msg, WPARAM wp, LPARAM lp);
105 HRESULT CALLBACK SpinnerDialogProc (HWND hSpin, UINT msg, WPARAM wp, LPARAM lp);
106 HRESULT CALLBACK SpinnerBuddyProc (HWND hBuddy, UINT msg, WPARAM wp, LPARAM lp);
107
108 void SpinnerSendCallback (SpinnerInfo *psi, WORD spm, LPARAM lp);
109
110 void Spinner_OnCreate (SpinnerInfo *psi);
111 BOOL Spinner_OnGetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
112 BOOL Spinner_OnSetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
113 BOOL Spinner_OnGetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
114 BOOL Spinner_OnSetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
115 BOOL Spinner_OnGetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
116 BOOL Spinner_OnSetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
117 BOOL Spinner_OnReattach (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
118 BOOL Spinner_OnSetRect (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
119 BOOL Spinner_OnGetSpinner (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
120 BOOL Spinner_OnSetFormat (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
121 BOOL Spinner_OnSetBuddy (SpinnerInfo *psi, WPARAM wp, LPARAM lp);
122
123 void Spinner_GetNewText (SpinnerInfo *psi);
124 void Spinner_SetNewText (SpinnerInfo *psi, BOOL fCallback);
125
126
127 BOOL RegisterSpinnerClass (void)
128 {
129    static BOOL fRegistered = FALSE;
130
131    if (!fRegistered)
132       {
133       WNDCLASS wc;
134
135       InitializeCriticalSection (&csSpinners);
136
137       if (GetClassInfo (THIS_HINST, TEXT("scrollbar"), &wc))
138          {
139          oldSpinnerProc = (LONG)(LONG_PTR)wc.lpfnWndProc;
140
141          wc.lpfnWndProc = (WNDPROC)SpinnerProc;
142          wc.style = CS_GLOBALCLASS;
143          wc.hInstance = THIS_HINST;
144          wc.lpszClassName = cszSPINNERCLASS;
145
146          if (RegisterClass (&wc))
147             fRegistered = TRUE;
148          }
149       }
150
151    return fRegistered;
152 }
153
154
155 SpinnerInfo *Spinner_FindSpinnerInfo (HWND hSpinner, HWND hBuddy)
156 {
157    SpinnerInfo *psi = NULL;
158
159    EnterCriticalSection (&csSpinners);
160
161    for (size_t ii = 0; ii < cSpinners; ++ii)
162       {
163       if ( (hSpinner && (aSpinners[ ii ].hSpinner == hSpinner)) ||
164            (hBuddy   && (aSpinners[ ii ].hBuddy == hBuddy)) )
165          {
166          psi = &aSpinners[ ii ];
167          break;
168          }
169       }
170
171    LeaveCriticalSection (&csSpinners);
172    return psi;
173 }
174
175
176 BOOL CreateSpinner (HWND hBuddy,
177                     WORD wBase, BOOL fSigned,
178                     DWORD dwMin, DWORD dwPos, DWORD dwMax,
179                     LPRECT prTarget)
180 {
181    if (!RegisterSpinnerClass())
182       return FALSE;
183
184    EnterCriticalSection (&csSpinners);
185
186    size_t ii;
187    for (ii = 0; ii < cSpinners; ++ii)
188       {
189       if (!aSpinners[ ii ].hSpinner)
190          break;
191       }
192    if (ii >= cSpinners)
193       {
194       if (!REALLOC (aSpinners, cSpinners, 1+ii, 4))
195          {
196          LeaveCriticalSection (&csSpinners);
197          return FALSE;
198          }
199       }
200
201    memset (&aSpinners[ ii ], 0x00, sizeof(SpinnerInfo));
202
203    aSpinners[ ii ].hBuddy = hBuddy;
204    aSpinners[ ii ].dwMin = dwMin;
205    aSpinners[ ii ].dwMax = dwMax;
206    aSpinners[ ii ].wBase = wBase;
207    aSpinners[ ii ].fSigned = fSigned;
208    aSpinners[ ii ].dwPos = dwPos;
209
210    if (prTarget != NULL)
211       aSpinners[ ii ].rReq = *prTarget;
212
213    aSpinners[ ii ].hSpinner = CreateWindowEx (
214                 0,      // extended window style
215                 cszSPINNERCLASS,        // pointer to registered class name
216                 TEXT(""),       // pointer to window name
217                 WS_CHILD | SBS_VERT,    // window style
218                 0, 0, 1, 1,     // spinner moves/resizes itself
219                 GetParent(hBuddy),      // handle to parent or owner window
220                 (HMENU)-1,      // handle to menu, or child-window identifier
221                 THIS_HINST,     // handle to application instance
222                 (LPVOID)ii);    // pointer to window-creation data
223
224    LeaveCriticalSection (&csSpinners);
225
226    if (aSpinners[ ii ].hSpinner == NULL)
227       return FALSE;
228
229    ShowWindow (aSpinners[ ii ].hSpinner, SW_SHOW);
230
231    if (!IsWindowEnabled (aSpinners[ ii ].hBuddy))
232       EnableWindow (aSpinners[ ii ].hSpinner, FALSE);
233
234    return TRUE;
235 }
236
237
238 BOOL fHasSpinner (HWND hBuddy)
239 {
240    if (!RegisterSpinnerClass())
241       return FALSE;
242
243    return (Spinner_FindSpinnerInfo (NULL, hBuddy) == NULL) ? FALSE : TRUE;
244 }
245
246
247 void Spinner_OnDestroy (SpinnerInfo *psi)
248 {
249    Subclass_RemoveHook (GetParent (psi->hSpinner), SpinnerDialogProc);
250    Subclass_RemoveHook (psi->hBuddy, SpinnerBuddyProc);
251
252    if (psi->pszFormat)
253       {
254       Free (psi->pszFormat);
255       psi->pszFormat = NULL;
256       }
257    psi->hSpinner = NULL;
258    psi->hBuddy = NULL;
259 }
260
261
262 HRESULT CALLBACK SpinnerProc (HWND hSpinner, UINT msg, WPARAM wp, LPARAM lp)
263 {
264    EnterCriticalSection (&csSpinners);
265
266    if (msg == WM_CREATE)
267       {
268       aSpinners[ (int)(INT_PTR)((LPCREATESTRUCT)lp)->lpCreateParams ].hSpinner = hSpinner;
269       }
270
271    SpinnerInfo *psi = Spinner_FindSpinnerInfo (hSpinner, NULL);
272
273    LeaveCriticalSection (&csSpinners);
274
275    if (psi != NULL)
276       {
277       switch (msg)
278          {
279          case WM_CREATE:
280             Spinner_OnCreate (psi);
281             break;
282
283          case WM_SETFOCUS:
284             PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
285             break;
286
287          case WM_DESTROY:
288             Spinner_OnDestroy (psi);
289             break;
290
291          case WM_SYSCHAR:
292          case WM_CHAR:
293             switch (wp)
294                {
295                case VK_UP:
296                   PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)psi->hSpinner);
297                   break;
298
299                case VK_DOWN:
300                   PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)psi->hSpinner);
301                   break;
302
303                case VK_PRIOR:
304                   PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)psi->hSpinner);
305                   break;
306
307                case VK_NEXT:
308                   PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)psi->hSpinner);
309                   break;
310
311                case VK_HOME:
312                   PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)psi->hSpinner);
313                   break;
314
315                case VK_END:
316                   PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)psi->hSpinner);
317                   break;
318                }
319             break;
320          }
321       }
322
323    if (oldSpinnerProc == 0)
324       return DefWindowProc (hSpinner, msg, wp, lp);
325    else
326       return CallWindowProc ((WNDPROC)(LONG_PTR)oldSpinnerProc, hSpinner, msg, wp, lp);
327 }
328
329
330 HRESULT CALLBACK SpinnerDialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
331 {
332    PVOID oldProc = Subclass_FindNextHook (hDlg, SpinnerDialogProc);
333    SpinnerInfo *psi;
334
335    switch (msg)
336       {
337       case WM_COMMAND:
338          if ((psi = Spinner_FindSpinnerInfo (NULL, (HWND)lp)) != NULL)
339             {
340             switch (HIWORD(wp))
341                {
342                // case CBN_SELCHANGE: --same value as LBN_SELCHANGE
343                case LBN_SELCHANGE:
344                case EN_UPDATE:
345                   Spinner_GetNewText (psi);
346
347                   if (psi->fCanCallBack == TRUE)
348                      SpinnerSendCallback (psi, SPN_UPDATE, (LPARAM)psi->dwPos);
349                   else
350                      oldProc = NULL; // don't forward this notification message
351                   break;
352
353                default:
354                   oldProc = NULL; // don't forward this notification message
355                   break;
356                }
357             }
358          break;
359
360       case WM_VSCROLL:
361          if ((psi = Spinner_FindSpinnerInfo ((HWND)lp, NULL)) != NULL)
362             {
363             if (psi->fNewText)
364                {
365                WORD wBaseOld = psi->wBase;
366                Spinner_GetNewText (psi);
367                if ((wBaseOld != psi->wBase) && !(psi->pszFormat))
368                   Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
369                }
370
371             switch (LOWORD(wp))
372                {
373                case SB_LINEUP:
374                   {
375                   DWORD dw = psi->dwPos;
376                   SpinnerSendCallback (psi, SPN_CHANGE_UP, (LPARAM)&dw);
377                   if (dw == psi->dwPos)
378                      psi->dwPos ++;
379                   else if (dw != SPVAL_UNCHANGED)
380                      psi->dwPos = dw;
381
382                   if (psi->wBase == 10 && psi->fSigned)
383                      psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
384                   else
385                      psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
386
387                   Spinner_SetNewText (psi, TRUE);
388                   PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
389                   break;
390                   }
391
392                case SB_LINEDOWN:
393                   {
394                   DWORD dw = psi->dwPos;
395                   SpinnerSendCallback (psi, SPN_CHANGE_DOWN, (LPARAM)&dw);
396                   if (dw == psi->dwPos)
397                      {
398                      if ((psi->dwPos > 0) || (psi->wBase == 10 && psi->fSigned))
399                         psi->dwPos --;
400                      }
401                   else if (dw != SPVAL_UNCHANGED)
402                      psi->dwPos = dw;
403
404                   if (psi->wBase == 10 && psi->fSigned)
405                      psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
406                   else
407                      psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
408
409                   Spinner_SetNewText (psi, TRUE);
410                   PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
411                   break;
412                   }
413                }
414             }
415          break;
416       }
417
418    if (oldProc == 0)
419       return DefWindowProc (hDlg, msg, wp, lp);
420    else
421       return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
422 }
423
424
425
426 HRESULT CALLBACK SpinnerBuddyProc (HWND hBuddy, UINT msg, WPARAM wp, LPARAM lp)
427 {
428    PVOID oldProc = Subclass_FindNextHook (hBuddy, SpinnerBuddyProc);
429
430    SpinnerInfo *psi;
431    if ((psi = Spinner_FindSpinnerInfo (NULL, hBuddy)) != NULL)
432       {
433       switch (msg)
434          {
435          case WM_KEYDOWN:
436          case WM_KEYUP:
437             switch (wp)
438                {
439                case VK_HOME:
440                case VK_END:
441                case VK_PRIOR:
442                case VK_NEXT:
443                case VK_UP:
444                case VK_DOWN:
445                   SendMessage (psi->hSpinner, msg, wp, lp);
446                   return FALSE;
447                }
448             break;
449
450          case WM_CHAR:
451             psi->fNewText = TRUE;
452             break;
453
454          case WM_MOVE:
455          case WM_SIZE:
456             PostMessage (hBuddy, SPM_REATTACH, 0, 0);
457             break;
458
459          case WM_ENABLE:
460             EnableWindow (psi->hSpinner, (BOOL)wp);
461             break;
462
463          case WM_KILLFOCUS:
464             Spinner_GetNewText (psi);
465             Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
466             break;
467
468          case SPM_GETRANGE:
469             return Spinner_OnGetRange (psi, wp, lp);
470
471          case SPM_SETRANGE:
472             return Spinner_OnSetRange (psi, wp, lp);
473
474          case SPM_GETPOS:
475             return Spinner_OnGetPos (psi, wp, lp);
476
477          case SPM_SETPOS:
478             return Spinner_OnSetPos (psi, wp, lp);
479
480          case SPM_GETBASE:
481             return Spinner_OnGetBase (psi, wp, lp);
482
483          case SPM_SETBASE:
484             return Spinner_OnSetBase (psi, wp, lp);
485
486          case SPM_REATTACH:
487             return Spinner_OnReattach (psi, wp, lp);
488
489          case SPM_SETRECT:
490             return Spinner_OnSetRect (psi, wp, lp);
491
492          case SPM_GETSPINNER:
493             return Spinner_OnGetSpinner (psi, wp, lp);
494
495          case SPM_SETFORMAT:
496             return Spinner_OnSetFormat (psi, wp, lp);
497
498          case SPM_SETBUDDY:
499             return Spinner_OnSetBuddy (psi, wp, lp);
500          }
501       }
502
503    if (oldProc)
504       return CallWindowProc ((WNDPROC)oldProc, hBuddy, msg, wp, lp);
505    else
506       return DefWindowProc (hBuddy, msg, wp, lp);
507 }
508
509
510 void SpinnerSendCallback (SpinnerInfo *psi, WORD spm, LPARAM lp)
511 {
512    if ((psi->fCanCallBack == TRUE) && !psi->fCallingBack)
513       {
514       psi->fCallingBack = TRUE;
515
516       SendMessage (GetParent (psi->hSpinner),
517                    WM_COMMAND,
518                    MAKELONG ((WORD)GetWindowLong (psi->hBuddy, GWL_ID), spm),
519                    lp);
520
521       psi->fCallingBack = FALSE;
522       }
523 }
524
525
526 void Spinner_OnCreate (SpinnerInfo *psi)
527 {
528    Subclass_AddHook (GetParent(psi->hSpinner), SpinnerDialogProc);
529    Subclass_AddHook (psi->hBuddy, SpinnerBuddyProc);
530
531    Spinner_OnReattach (psi, 0, 0);
532    Spinner_SetNewText (psi, FALSE);
533
534    psi->fCanCallBack = TRUE;
535 }
536
537
538 BOOL Spinner_OnGetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
539 {
540    if (wp != 0)
541       *(LPDWORD)wp = psi->dwMin;
542    if (lp != 0)
543       *(LPDWORD)lp = psi->dwMax;
544    return TRUE;
545 }
546
547 BOOL Spinner_OnSetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
548 {
549    psi->dwMin = (DWORD)wp;
550    psi->dwMax = (DWORD)lp;
551    Spinner_SetNewText (psi, FALSE);
552    return TRUE;
553 }
554
555 BOOL Spinner_OnGetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
556 {
557    if (psi->fNewText)
558       {
559       Spinner_GetNewText (psi);
560       Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
561       }
562    return (BOOL)psi->dwPos;
563 }
564
565 BOOL Spinner_OnSetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
566 {
567    psi->dwPos = (DWORD)lp;
568    Spinner_SetNewText (psi, FALSE);
569    return TRUE;
570 }
571
572 BOOL Spinner_OnGetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
573 {
574    if (psi->fNewText)
575       {
576       Spinner_GetNewText (psi);
577       Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
578       }
579
580    if (wp != 0)
581       *(WORD *)wp = psi->wBase;
582    if (lp != 0)
583       *(BOOL *)lp = psi->fSigned;
584    return TRUE;
585 }
586
587 BOOL Spinner_OnSetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
588 {
589    if (psi->fNewText)
590       Spinner_GetNewText (psi);
591
592    switch ((WORD)wp)
593       {
594       case  2:
595       case  8:
596       case 10:
597       case 16:
598          psi->wBase = (WORD)wp;
599          break;
600
601       default:
602          psi->wBase = 10;
603          break;
604       }
605
606    if (psi->wBase != 10)
607       psi->fSigned = FALSE;
608    else
609       psi->fSigned = (BOOL)lp;
610
611    Spinner_SetNewText (psi, FALSE);
612    return TRUE;
613 }
614
615
616 BOOL Spinner_OnReattach (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
617 {
618    RECT  rSpinner;
619    if (psi->rReq.right != 0)
620       {
621       rSpinner = psi->rReq;
622       }
623    else
624       {
625       RECT  rBuddyInParent;
626       POINT pt = { 0, 0 };
627
628       ClientToScreen (GetParent (psi->hBuddy), &pt);
629
630       GetWindowRect (psi->hBuddy, &rBuddyInParent);
631       rBuddyInParent.left -= pt.x;
632       rBuddyInParent.right -= pt.x;
633       rBuddyInParent.top -= pt.y;
634       rBuddyInParent.bottom -= pt.y;
635
636       rSpinner.top = rBuddyInParent.top;
637       rSpinner.bottom = rBuddyInParent.bottom -2; // just like Win95 does
638       rSpinner.left = rBuddyInParent.right;
639       rSpinner.right = rBuddyInParent.right +GetSystemMetrics (SM_CXVSCROLL);
640       }
641
642    SetWindowPos (psi->hSpinner, NULL,
643                  rSpinner.left, rSpinner.top,
644                  rSpinner.right-rSpinner.left,
645                  rSpinner.bottom-rSpinner.top,
646                  SWP_NOACTIVATE | SWP_NOZORDER);
647    return TRUE;
648 }
649
650
651 BOOL Spinner_OnSetRect (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
652 {
653    LPRECT prTarget;
654    if ((prTarget = (LPRECT)lp) == NULL)
655       SetRectEmpty (&psi->rReq);
656    else
657       psi->rReq = *prTarget;
658
659    Spinner_OnReattach (psi, 0, 0);
660    return TRUE;
661 }
662
663
664 BOOL Spinner_OnGetSpinner (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
665 {
666    return (BOOL)(INT_PTR)psi->hSpinner;
667 }
668
669
670 BOOL Spinner_OnSetFormat (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
671 {
672    if (psi->pszFormat)
673       {
674       Free (psi->pszFormat);
675       psi->pszFormat = NULL;
676       }
677    if (lp != 0)
678       {
679       psi->pszFormat = (LPTSTR)Allocate (sizeof(TCHAR) * (1+lstrlen((LPTSTR)lp)));
680       lstrcpy (psi->pszFormat, (LPTSTR)lp);
681       }
682    Spinner_SetNewText (psi, FALSE);
683    return TRUE;
684 }
685
686
687 BOOL Spinner_OnSetBuddy (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
688 {
689    HWND hBuddyNew = (HWND)wp;
690    BOOL fMove = (BOOL)lp;
691
692    // First un-subclass our buddy.
693    // Then subclass the new buddy.
694    //
695    Subclass_RemoveHook (psi->hBuddy, SpinnerBuddyProc);
696    psi->hBuddy = hBuddyNew;
697    Subclass_AddHook (psi->hBuddy, SpinnerBuddyProc);
698
699    // Update our SpinnerInfo structure, and move if requested.
700    //
701    Spinner_GetNewText (psi);
702
703    if (fMove)
704       {
705       SetRectEmpty (&psi->rReq);
706       Spinner_OnReattach (psi, 0, 0);
707       }
708
709    return TRUE;
710 }
711
712
713 void Spinner_GetNewText (SpinnerInfo *psi)
714 {
715    // First find out what kind of buddy we have.
716    // That will determine what we do here.
717    //
718    TCHAR szBuddyClass[256];
719    GetClassName (psi->hBuddy, szBuddyClass, 256);
720
721    // For comboboxes and listboxes, the dwPos value is actually
722    // the selected item's index.
723    //
724    if (!lstrcmpi (szBuddyClass, TEXT("listbox")))
725       {
726       psi->dwPos = (DWORD)LB_GetSelected (psi->hBuddy);
727       }
728
729    if (!lstrcmpi (szBuddyClass, TEXT("combobox")))
730       {
731       psi->dwPos = (DWORD)CB_GetSelected (psi->hBuddy);
732       }
733
734    // For edit controls, the dwPos value is actually
735    // the control's text's value.
736    //
737    if (!lstrcmpi (szBuddyClass, TEXT("edit")))
738       {
739       TCHAR szText[256];
740       LPTSTR pszText = szText;
741       BOOL fNegative = FALSE;
742
743       psi->fNewText = FALSE;
744       psi->dwPos = 0;
745       psi->wBase = 10;
746
747       GetWindowText (psi->hBuddy, szText, 256);
748
749       while (*pszText == TEXT(' ') || *pszText == TEXT('\t'))
750          ++pszText;
751
752       if (*pszText == TEXT('0'))
753          {
754          if ((*(1+pszText) == 'x') || (*(1+pszText) == 'X'))
755             {
756             psi->wBase = 16;
757             ++pszText;
758             ++pszText;
759             }
760          else if ((*(1+pszText) == 'b') || (*(1+pszText) == 'B') || (*(1+pszText) == '!'))
761             {
762             psi->wBase = 2;
763             ++pszText;
764             ++pszText;
765             }
766          else if (*(1+pszText) != '\0')
767             {
768             // psi->wBase = 8; // ignore octal--time controls use "4:08" etc
769             ++pszText;
770             }
771          }
772
773       for ( ; *pszText == TEXT('-'); ++pszText)
774          {
775          fNegative = !fNegative;
776          }
777
778       for ( ; *pszText; ++pszText)
779          {
780          if (!isdigit( *pszText ) &&
781              !(psi->wBase == 16 && ishexdigit( *pszText )))
782             {
783             break;
784             }
785
786          psi->dwPos *= psi->wBase;
787
788          if ((DWORD)digitval(*pszText) < (DWORD)psi->wBase)
789             psi->dwPos += digitval( *pszText );
790          }
791
792       if (fNegative && psi->wBase == 10 && psi->fSigned)
793          {
794          psi->dwPos = (DWORD)(0 - (signed long)psi->dwPos);
795          }
796       }
797
798    if (psi->wBase == 10 && psi->fSigned)
799       psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
800    else
801       psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
802 }
803
804
805 void Spinner_SetNewText (SpinnerInfo *psi, BOOL fCallBack)
806 {
807    TCHAR szText[256];
808
809    // First find out what kind of buddy we have.
810    // That will determine what we do here.
811    //
812    TCHAR szBuddyClass[256];
813    GetClassName (psi->hBuddy, szBuddyClass, 256);
814
815    // Be sure to notify the parent window that the selection may be changing.
816    //
817    if (fCallBack)
818       {
819       DWORD dw = psi->dwPos;
820       SpinnerSendCallback (psi, SPN_CHANGE, (LPARAM)&dw);
821       if (dw != SPVAL_UNCHANGED)
822          psi->dwPos = dw;
823       }
824
825    if (psi->wBase == 10 && psi->fSigned)
826       psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
827    else
828       psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
829
830    if (!fCallBack)
831       psi->fCanCallBack --;
832
833    // For comboboxes and listboxes, select the item specified by the
834    // given index.
835    //
836    if (!lstrcmpi (szBuddyClass, TEXT("listbox")))
837       {
838       LB_SetSelected (psi->hBuddy, psi->dwPos);
839       }
840
841    if (!lstrcmpi (szBuddyClass, TEXT("combobox")))
842       {
843       CB_SetSelected (psi->hBuddy, psi->dwPos);
844       }
845
846    // For edit controls, fill in the new value as text--expressed in the
847    // requested base, using the requested format.
848    //
849    if (!lstrcmpi (szBuddyClass, TEXT("edit")))
850       {
851       switch (psi->wBase)
852          {
853          case 10:
854             if (psi->pszFormat)
855                wsprintf (szText, psi->pszFormat, (unsigned long)psi->dwPos);
856             else if (psi->fSigned)
857                wsprintf (szText, TEXT("%ld"), (signed long)psi->dwPos);
858             else
859                wsprintf (szText, TEXT("%lu"), (unsigned long)psi->dwPos);
860             break;
861
862          case 16:
863             wsprintf (szText, TEXT("0x%lX"), (unsigned long)psi->dwPos);
864             break;
865
866          default:
867             TCHAR szTemp[256];
868             LPTSTR pszTemp = szTemp;
869             LPTSTR pszText = szText;
870
871             if (psi->dwPos == 0)
872                *pszTemp++ = TEXT('0');
873             else
874                {
875                for (DWORD dwRemainder = psi->dwPos; dwRemainder != 0; dwRemainder /= (DWORD)psi->wBase)
876                   {
877                   DWORD dw = (dwRemainder % (DWORD)psi->wBase);
878                   *pszTemp++ = TEXT('0') + (TCHAR)dw;
879                   }
880                }
881
882             if (psi->wBase == 8)
883                {
884                *pszText++ = TEXT('0');
885                }
886             else if (psi->wBase == 2)
887                {
888                *pszText++ = TEXT('0');
889                *pszText++ = TEXT('b');
890                }
891
892             for (--pszTemp; pszTemp >= szTemp; )
893                {
894                *pszText++ = *pszTemp--;
895                }
896             *pszText = TEXT('\0');
897             break;
898          }
899
900       SetWindowText (psi->hBuddy, szText);
901       }
902
903    if (!fCallBack)
904       psi->fCanCallBack ++;
905    else
906       SpinnerSendCallback (psi, SPN_UPDATE, (LPARAM)psi->dwPos);
907 }
908