2 * Copyright 2000, International Business Machines Corporation and others.
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
11 #include <afs/param.h>
18 #include <WINNT/dialog.h>
19 #include <WINNT/subclass.h>
20 #include <WINNT/ctl_spinner.h>
21 #include <WINNT/TaLocale.h>
24 #define cchRESOURCE 256
29 * MISCELLANEOUS ______________________________________________________________
33 #define ishexdigit(_ch) ( (((_ch) >= 'a') && ((_ch) <= 'f')) || \
34 (((_ch) >= 'A') && ((_ch) <= 'F')) )
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) \
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)
51 if (cReq <= *pcTarget)
54 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
57 if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
59 memset (pNew, 0x00, cbElement * cNew);
63 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
75 * SPINNERS ___________________________________________________________________
97 static CRITICAL_SECTION csSpinners;
98 static SpinnerInfo *aSpinners = NULL;
99 static size_t cSpinners = 0;
100 static LONG oldSpinnerProc = 0;
102 #define cszSPINNERCLASS TEXT("Spinner")
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);
108 void SpinnerSendCallback (SpinnerInfo *psi, WORD spm, LPARAM lp);
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);
123 void Spinner_GetNewText (SpinnerInfo *psi);
124 void Spinner_SetNewText (SpinnerInfo *psi, BOOL fCallback);
127 BOOL RegisterSpinnerClass (void)
129 static BOOL fRegistered = FALSE;
135 InitializeCriticalSection (&csSpinners);
137 if (GetClassInfo (THIS_HINST, TEXT("scrollbar"), &wc))
139 oldSpinnerProc = (LONG)(LONG_PTR)wc.lpfnWndProc;
141 wc.lpfnWndProc = (WNDPROC)SpinnerProc;
142 wc.style = CS_GLOBALCLASS;
143 wc.hInstance = THIS_HINST;
144 wc.lpszClassName = cszSPINNERCLASS;
146 if (RegisterClass (&wc))
155 SpinnerInfo *Spinner_FindSpinnerInfo (HWND hSpinner, HWND hBuddy)
157 SpinnerInfo *psi = NULL;
159 EnterCriticalSection (&csSpinners);
161 for (size_t ii = 0; ii < cSpinners; ++ii)
163 if ( (hSpinner && (aSpinners[ ii ].hSpinner == hSpinner)) ||
164 (hBuddy && (aSpinners[ ii ].hBuddy == hBuddy)) )
166 psi = &aSpinners[ ii ];
171 LeaveCriticalSection (&csSpinners);
176 BOOL CreateSpinner (HWND hBuddy,
177 WORD wBase, BOOL fSigned,
178 DWORD dwMin, DWORD dwPos, DWORD dwMax,
181 if (!RegisterSpinnerClass())
184 EnterCriticalSection (&csSpinners);
187 for (ii = 0; ii < cSpinners; ++ii)
189 if (!aSpinners[ ii ].hSpinner)
194 if (!REALLOC (aSpinners, cSpinners, 1+ii, 4))
196 LeaveCriticalSection (&csSpinners);
201 memset (&aSpinners[ ii ], 0x00, sizeof(SpinnerInfo));
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;
210 if (prTarget != NULL)
211 aSpinners[ ii ].rReq = *prTarget;
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
224 LeaveCriticalSection (&csSpinners);
226 if (aSpinners[ ii ].hSpinner == NULL)
229 ShowWindow (aSpinners[ ii ].hSpinner, SW_SHOW);
231 if (!IsWindowEnabled (aSpinners[ ii ].hBuddy))
232 EnableWindow (aSpinners[ ii ].hSpinner, FALSE);
238 BOOL fHasSpinner (HWND hBuddy)
240 if (!RegisterSpinnerClass())
243 return (Spinner_FindSpinnerInfo (NULL, hBuddy) == NULL) ? FALSE : TRUE;
247 void Spinner_OnDestroy (SpinnerInfo *psi)
249 Subclass_RemoveHook (GetParent (psi->hSpinner), SpinnerDialogProc);
250 Subclass_RemoveHook (psi->hBuddy, SpinnerBuddyProc);
254 Free (psi->pszFormat);
255 psi->pszFormat = NULL;
257 psi->hSpinner = NULL;
262 HRESULT CALLBACK SpinnerProc (HWND hSpinner, UINT msg, WPARAM wp, LPARAM lp)
264 EnterCriticalSection (&csSpinners);
266 if (msg == WM_CREATE)
268 aSpinners[ (int)(INT_PTR)((LPCREATESTRUCT)lp)->lpCreateParams ].hSpinner = hSpinner;
271 SpinnerInfo *psi = Spinner_FindSpinnerInfo (hSpinner, NULL);
273 LeaveCriticalSection (&csSpinners);
280 Spinner_OnCreate (psi);
284 PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
288 Spinner_OnDestroy (psi);
296 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)psi->hSpinner);
300 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)psi->hSpinner);
304 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)psi->hSpinner);
308 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)psi->hSpinner);
312 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)psi->hSpinner);
316 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)psi->hSpinner);
323 if (oldSpinnerProc == 0)
324 return DefWindowProc (hSpinner, msg, wp, lp);
326 return CallWindowProc ((WNDPROC)(LONG_PTR)oldSpinnerProc, hSpinner, msg, wp, lp);
330 HRESULT CALLBACK SpinnerDialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
332 PVOID oldProc = Subclass_FindNextHook (hDlg, SpinnerDialogProc);
338 if ((psi = Spinner_FindSpinnerInfo (NULL, (HWND)lp)) != NULL)
342 // case CBN_SELCHANGE: --same value as LBN_SELCHANGE
345 Spinner_GetNewText (psi);
347 if (psi->fCanCallBack == TRUE)
348 SpinnerSendCallback (psi, SPN_UPDATE, (LPARAM)psi->dwPos);
350 oldProc = NULL; // don't forward this notification message
354 oldProc = NULL; // don't forward this notification message
361 if ((psi = Spinner_FindSpinnerInfo ((HWND)lp, NULL)) != NULL)
365 WORD wBaseOld = psi->wBase;
366 Spinner_GetNewText (psi);
367 if ((wBaseOld != psi->wBase) && !(psi->pszFormat))
368 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
375 DWORD dw = psi->dwPos;
376 SpinnerSendCallback (psi, SPN_CHANGE_UP, (LPARAM)&dw);
377 if (dw == psi->dwPos)
379 else if (dw != SPVAL_UNCHANGED)
382 if (psi->wBase == 10 && psi->fSigned)
383 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
385 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
387 Spinner_SetNewText (psi, TRUE);
388 PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
394 DWORD dw = psi->dwPos;
395 SpinnerSendCallback (psi, SPN_CHANGE_DOWN, (LPARAM)&dw);
396 if (dw == psi->dwPos)
398 if ((psi->dwPos > 0) || (psi->wBase == 10 && psi->fSigned))
401 else if (dw != SPVAL_UNCHANGED)
404 if (psi->wBase == 10 && psi->fSigned)
405 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
407 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
409 Spinner_SetNewText (psi, TRUE);
410 PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
419 return DefWindowProc (hDlg, msg, wp, lp);
421 return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
426 HRESULT CALLBACK SpinnerBuddyProc (HWND hBuddy, UINT msg, WPARAM wp, LPARAM lp)
428 PVOID oldProc = Subclass_FindNextHook (hBuddy, SpinnerBuddyProc);
431 if ((psi = Spinner_FindSpinnerInfo (NULL, hBuddy)) != NULL)
445 SendMessage (psi->hSpinner, msg, wp, lp);
451 psi->fNewText = TRUE;
456 PostMessage (hBuddy, SPM_REATTACH, 0, 0);
460 EnableWindow (psi->hSpinner, (BOOL)wp);
464 Spinner_GetNewText (psi);
465 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
469 return Spinner_OnGetRange (psi, wp, lp);
472 return Spinner_OnSetRange (psi, wp, lp);
475 return Spinner_OnGetPos (psi, wp, lp);
478 return Spinner_OnSetPos (psi, wp, lp);
481 return Spinner_OnGetBase (psi, wp, lp);
484 return Spinner_OnSetBase (psi, wp, lp);
487 return Spinner_OnReattach (psi, wp, lp);
490 return Spinner_OnSetRect (psi, wp, lp);
493 return Spinner_OnGetSpinner (psi, wp, lp);
496 return Spinner_OnSetFormat (psi, wp, lp);
499 return Spinner_OnSetBuddy (psi, wp, lp);
504 return CallWindowProc ((WNDPROC)oldProc, hBuddy, msg, wp, lp);
506 return DefWindowProc (hBuddy, msg, wp, lp);
510 void SpinnerSendCallback (SpinnerInfo *psi, WORD spm, LPARAM lp)
512 if ((psi->fCanCallBack == TRUE) && !psi->fCallingBack)
514 psi->fCallingBack = TRUE;
516 SendMessage (GetParent (psi->hSpinner),
518 MAKELONG ((WORD)GetWindowLong (psi->hBuddy, GWL_ID), spm),
521 psi->fCallingBack = FALSE;
526 void Spinner_OnCreate (SpinnerInfo *psi)
528 Subclass_AddHook (GetParent(psi->hSpinner), SpinnerDialogProc);
529 Subclass_AddHook (psi->hBuddy, SpinnerBuddyProc);
531 Spinner_OnReattach (psi, 0, 0);
532 Spinner_SetNewText (psi, FALSE);
534 psi->fCanCallBack = TRUE;
538 BOOL Spinner_OnGetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
541 *(LPDWORD)wp = psi->dwMin;
543 *(LPDWORD)lp = psi->dwMax;
547 BOOL Spinner_OnSetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
549 psi->dwMin = (DWORD)wp;
550 psi->dwMax = (DWORD)lp;
551 Spinner_SetNewText (psi, FALSE);
555 BOOL Spinner_OnGetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
559 Spinner_GetNewText (psi);
560 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
562 return (BOOL)psi->dwPos;
565 BOOL Spinner_OnSetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
567 psi->dwPos = (DWORD)lp;
568 Spinner_SetNewText (psi, FALSE);
572 BOOL Spinner_OnGetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
576 Spinner_GetNewText (psi);
577 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
581 *(WORD *)wp = psi->wBase;
583 *(BOOL *)lp = psi->fSigned;
587 BOOL Spinner_OnSetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
590 Spinner_GetNewText (psi);
598 psi->wBase = (WORD)wp;
606 if (psi->wBase != 10)
607 psi->fSigned = FALSE;
609 psi->fSigned = (BOOL)lp;
611 Spinner_SetNewText (psi, FALSE);
616 BOOL Spinner_OnReattach (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
619 if (psi->rReq.right != 0)
621 rSpinner = psi->rReq;
628 ClientToScreen (GetParent (psi->hBuddy), &pt);
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;
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);
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);
651 BOOL Spinner_OnSetRect (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
654 if ((prTarget = (LPRECT)lp) == NULL)
655 SetRectEmpty (&psi->rReq);
657 psi->rReq = *prTarget;
659 Spinner_OnReattach (psi, 0, 0);
664 BOOL Spinner_OnGetSpinner (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
666 return (BOOL)(INT_PTR)psi->hSpinner;
670 BOOL Spinner_OnSetFormat (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
674 Free (psi->pszFormat);
675 psi->pszFormat = NULL;
679 psi->pszFormat = (LPTSTR)Allocate (sizeof(TCHAR) * (1+lstrlen((LPTSTR)lp)));
680 lstrcpy (psi->pszFormat, (LPTSTR)lp);
682 Spinner_SetNewText (psi, FALSE);
687 BOOL Spinner_OnSetBuddy (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
689 HWND hBuddyNew = (HWND)wp;
690 BOOL fMove = (BOOL)lp;
692 // First un-subclass our buddy.
693 // Then subclass the new buddy.
695 Subclass_RemoveHook (psi->hBuddy, SpinnerBuddyProc);
696 psi->hBuddy = hBuddyNew;
697 Subclass_AddHook (psi->hBuddy, SpinnerBuddyProc);
699 // Update our SpinnerInfo structure, and move if requested.
701 Spinner_GetNewText (psi);
705 SetRectEmpty (&psi->rReq);
706 Spinner_OnReattach (psi, 0, 0);
713 void Spinner_GetNewText (SpinnerInfo *psi)
715 // First find out what kind of buddy we have.
716 // That will determine what we do here.
718 TCHAR szBuddyClass[256];
719 GetClassName (psi->hBuddy, szBuddyClass, 256);
721 // For comboboxes and listboxes, the dwPos value is actually
722 // the selected item's index.
724 if (!lstrcmpi (szBuddyClass, TEXT("listbox")))
726 psi->dwPos = (DWORD)LB_GetSelected (psi->hBuddy);
729 if (!lstrcmpi (szBuddyClass, TEXT("combobox")))
731 psi->dwPos = (DWORD)CB_GetSelected (psi->hBuddy);
734 // For edit controls, the dwPos value is actually
735 // the control's text's value.
737 if (!lstrcmpi (szBuddyClass, TEXT("edit")))
740 LPTSTR pszText = szText;
741 BOOL fNegative = FALSE;
743 psi->fNewText = FALSE;
747 GetWindowText (psi->hBuddy, szText, 256);
749 while (*pszText == TEXT(' ') || *pszText == TEXT('\t'))
752 if (*pszText == TEXT('0'))
754 if ((*(1+pszText) == 'x') || (*(1+pszText) == 'X'))
760 else if ((*(1+pszText) == 'b') || (*(1+pszText) == 'B') || (*(1+pszText) == '!'))
766 else if (*(1+pszText) != '\0')
768 // psi->wBase = 8; // ignore octal--time controls use "4:08" etc
773 for ( ; *pszText == TEXT('-'); ++pszText)
775 fNegative = !fNegative;
778 for ( ; *pszText; ++pszText)
780 if (!isdigit( *pszText ) &&
781 !(psi->wBase == 16 && ishexdigit( *pszText )))
786 psi->dwPos *= psi->wBase;
788 if ((DWORD)digitval(*pszText) < (DWORD)psi->wBase)
789 psi->dwPos += digitval( *pszText );
792 if (fNegative && psi->wBase == 10 && psi->fSigned)
794 psi->dwPos = (DWORD)(0 - (signed long)psi->dwPos);
798 if (psi->wBase == 10 && psi->fSigned)
799 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
801 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
805 void Spinner_SetNewText (SpinnerInfo *psi, BOOL fCallBack)
809 // First find out what kind of buddy we have.
810 // That will determine what we do here.
812 TCHAR szBuddyClass[256];
813 GetClassName (psi->hBuddy, szBuddyClass, 256);
815 // Be sure to notify the parent window that the selection may be changing.
819 DWORD dw = psi->dwPos;
820 SpinnerSendCallback (psi, SPN_CHANGE, (LPARAM)&dw);
821 if (dw != SPVAL_UNCHANGED)
825 if (psi->wBase == 10 && psi->fSigned)
826 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
828 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
831 psi->fCanCallBack --;
833 // For comboboxes and listboxes, select the item specified by the
836 if (!lstrcmpi (szBuddyClass, TEXT("listbox")))
838 LB_SetSelected (psi->hBuddy, psi->dwPos);
841 if (!lstrcmpi (szBuddyClass, TEXT("combobox")))
843 CB_SetSelected (psi->hBuddy, psi->dwPos);
846 // For edit controls, fill in the new value as text--expressed in the
847 // requested base, using the requested format.
849 if (!lstrcmpi (szBuddyClass, TEXT("edit")))
855 wsprintf (szText, psi->pszFormat, (unsigned long)psi->dwPos);
856 else if (psi->fSigned)
857 wsprintf (szText, TEXT("%ld"), (signed long)psi->dwPos);
859 wsprintf (szText, TEXT("%lu"), (unsigned long)psi->dwPos);
863 wsprintf (szText, TEXT("0x%lX"), (unsigned long)psi->dwPos);
868 LPTSTR pszTemp = szTemp;
869 LPTSTR pszText = szText;
872 *pszTemp++ = TEXT('0');
875 for (DWORD dwRemainder = psi->dwPos; dwRemainder != 0; dwRemainder /= (DWORD)psi->wBase)
877 DWORD dw = (dwRemainder % (DWORD)psi->wBase);
878 *pszTemp++ = TEXT('0') + (TCHAR)dw;
884 *pszText++ = TEXT('0');
886 else if (psi->wBase == 2)
888 *pszText++ = TEXT('0');
889 *pszText++ = TEXT('b');
892 for (--pszTemp; pszTemp >= szTemp; )
894 *pszText++ = *pszTemp--;
896 *pszText = TEXT('\0');
900 SetWindowText (psi->hBuddy, szText);
904 psi->fCanCallBack ++;
906 SpinnerSendCallback (psi, SPN_UPDATE, (LPARAM)psi->dwPos);