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 BOOL CALLBACK SpinnerProc (HWND hSpin, UINT msg, WPARAM wp, LPARAM lp);
105 BOOL CALLBACK SpinnerDialogProc (HWND hSpin, UINT msg, WPARAM wp, LPARAM lp);
106 BOOL 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)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);
186 for (size_t ii = 0; ii < cSpinners; ++ii)
188 if (!aSpinners[ ii ].hSpinner)
193 if (!REALLOC (aSpinners, cSpinners, 1+ii, 4))
195 LeaveCriticalSection (&csSpinners);
200 memset (&aSpinners[ ii ], 0x00, sizeof(SpinnerInfo));
202 aSpinners[ ii ].hBuddy = hBuddy;
203 aSpinners[ ii ].dwMin = dwMin;
204 aSpinners[ ii ].dwMax = dwMax;
205 aSpinners[ ii ].wBase = wBase;
206 aSpinners[ ii ].fSigned = fSigned;
207 aSpinners[ ii ].dwPos = dwPos;
209 if (prTarget != NULL)
210 aSpinners[ ii ].rReq = *prTarget;
212 aSpinners[ ii ].hSpinner = CreateWindowEx (
213 0, // extended window style
214 cszSPINNERCLASS, // pointer to registered class name
215 TEXT(""), // pointer to window name
216 WS_CHILD | SBS_VERT, // window style
217 0, 0, 1, 1, // spinner moves/resizes itself
218 GetParent(hBuddy), // handle to parent or owner window
219 (HMENU)-1, // handle to menu, or child-window identifier
220 THIS_HINST, // handle to application instance
221 (LPVOID)ii); // pointer to window-creation data
223 LeaveCriticalSection (&csSpinners);
225 if (aSpinners[ ii ].hSpinner == NULL)
228 ShowWindow (aSpinners[ ii ].hSpinner, SW_SHOW);
230 if (!IsWindowEnabled (aSpinners[ ii ].hBuddy))
231 EnableWindow (aSpinners[ ii ].hSpinner, FALSE);
237 BOOL fHasSpinner (HWND hBuddy)
239 if (!RegisterSpinnerClass())
242 return (Spinner_FindSpinnerInfo (NULL, hBuddy) == NULL) ? FALSE : TRUE;
246 void Spinner_OnDestroy (SpinnerInfo *psi)
248 Subclass_RemoveHook (GetParent (psi->hSpinner), SpinnerDialogProc);
249 Subclass_RemoveHook (psi->hBuddy, SpinnerBuddyProc);
253 Free (psi->pszFormat);
254 psi->pszFormat = NULL;
256 psi->hSpinner = NULL;
261 BOOL CALLBACK SpinnerProc (HWND hSpinner, UINT msg, WPARAM wp, LPARAM lp)
263 EnterCriticalSection (&csSpinners);
265 if (msg == WM_CREATE)
267 aSpinners[ (int)((LPCREATESTRUCT)lp)->lpCreateParams ].hSpinner = hSpinner;
270 SpinnerInfo *psi = Spinner_FindSpinnerInfo (hSpinner, NULL);
272 LeaveCriticalSection (&csSpinners);
279 Spinner_OnCreate (psi);
283 PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
287 Spinner_OnDestroy (psi);
295 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_LINEUP, (LPARAM)psi->hSpinner);
299 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_LINEDOWN, (LPARAM)psi->hSpinner);
303 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_PAGEUP, (LPARAM)psi->hSpinner);
307 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_PAGEDOWN, (LPARAM)psi->hSpinner);
311 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_TOP, (LPARAM)psi->hSpinner);
315 PostMessage (GetParent(psi->hSpinner), WM_VSCROLL, SB_BOTTOM, (LPARAM)psi->hSpinner);
322 if (oldSpinnerProc == 0)
323 return DefWindowProc (hSpinner, msg, wp, lp);
325 return CallWindowProc ((WNDPROC)oldSpinnerProc, hSpinner, msg, wp, lp);
329 BOOL CALLBACK SpinnerDialogProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
331 PVOID oldProc = Subclass_FindNextHook (hDlg, SpinnerDialogProc);
337 if ((psi = Spinner_FindSpinnerInfo (NULL, (HWND)lp)) != NULL)
341 // case CBN_SELCHANGE: --same value as LBN_SELCHANGE
344 Spinner_GetNewText (psi);
346 if (psi->fCanCallBack == TRUE)
347 SpinnerSendCallback (psi, SPN_UPDATE, (LPARAM)psi->dwPos);
349 oldProc = NULL; // don't forward this notification message
353 oldProc = NULL; // don't forward this notification message
360 if ((psi = Spinner_FindSpinnerInfo ((HWND)lp, NULL)) != NULL)
364 WORD wBaseOld = psi->wBase;
365 Spinner_GetNewText (psi);
366 if ((wBaseOld != psi->wBase) && !(psi->pszFormat))
367 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
374 DWORD dw = psi->dwPos;
375 SpinnerSendCallback (psi, SPN_CHANGE_UP, (LPARAM)&dw);
376 if (dw == psi->dwPos)
378 else if (dw != SPVAL_UNCHANGED)
381 if (psi->wBase == 10 && psi->fSigned)
382 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
384 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
386 Spinner_SetNewText (psi, TRUE);
387 PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
393 DWORD dw = psi->dwPos;
394 SpinnerSendCallback (psi, SPN_CHANGE_DOWN, (LPARAM)&dw);
395 if (dw == psi->dwPos)
397 if ((psi->dwPos > 0) || (psi->wBase == 10 && psi->fSigned))
400 else if (dw != SPVAL_UNCHANGED)
403 if (psi->wBase == 10 && psi->fSigned)
404 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
406 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
408 Spinner_SetNewText (psi, TRUE);
409 PostMessage (GetParent(psi->hSpinner), WM_NEXTDLGCTL, (WPARAM)psi->hBuddy, TRUE);
418 return DefWindowProc (hDlg, msg, wp, lp);
420 return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
425 BOOL CALLBACK SpinnerBuddyProc (HWND hBuddy, UINT msg, WPARAM wp, LPARAM lp)
427 PVOID oldProc = Subclass_FindNextHook (hBuddy, SpinnerBuddyProc);
430 if ((psi = Spinner_FindSpinnerInfo (NULL, hBuddy)) != NULL)
444 SendMessage (psi->hSpinner, msg, wp, lp);
450 psi->fNewText = TRUE;
455 PostMessage (hBuddy, SPM_REATTACH, 0, 0);
459 EnableWindow (psi->hSpinner, wp);
463 Spinner_GetNewText (psi);
464 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
468 return Spinner_OnGetRange (psi, wp, lp);
471 return Spinner_OnSetRange (psi, wp, lp);
474 return Spinner_OnGetPos (psi, wp, lp);
477 return Spinner_OnSetPos (psi, wp, lp);
480 return Spinner_OnGetBase (psi, wp, lp);
483 return Spinner_OnSetBase (psi, wp, lp);
486 return Spinner_OnReattach (psi, wp, lp);
489 return Spinner_OnSetRect (psi, wp, lp);
492 return Spinner_OnGetSpinner (psi, wp, lp);
495 return Spinner_OnSetFormat (psi, wp, lp);
498 return Spinner_OnSetBuddy (psi, wp, lp);
503 return CallWindowProc ((WNDPROC)oldProc, hBuddy, msg, wp, lp);
505 return DefWindowProc (hBuddy, msg, wp, lp);
509 void SpinnerSendCallback (SpinnerInfo *psi, WORD spm, LPARAM lp)
511 if ((psi->fCanCallBack == TRUE) && !psi->fCallingBack)
513 psi->fCallingBack = TRUE;
515 SendMessage (GetParent (psi->hSpinner),
517 MAKELONG ((WORD)GetWindowLong (psi->hBuddy, GWL_ID), spm),
520 psi->fCallingBack = FALSE;
525 void Spinner_OnCreate (SpinnerInfo *psi)
527 Subclass_AddHook (GetParent(psi->hSpinner), SpinnerDialogProc);
528 Subclass_AddHook (psi->hBuddy, SpinnerBuddyProc);
530 Spinner_OnReattach (psi, 0, 0);
531 Spinner_SetNewText (psi, FALSE);
533 psi->fCanCallBack = TRUE;
537 BOOL Spinner_OnGetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
540 *(LPDWORD)wp = psi->dwMin;
542 *(LPDWORD)lp = psi->dwMax;
546 BOOL Spinner_OnSetRange (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
548 psi->dwMin = (DWORD)wp;
549 psi->dwMax = (DWORD)lp;
550 Spinner_SetNewText (psi, FALSE);
554 BOOL Spinner_OnGetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
558 Spinner_GetNewText (psi);
559 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
561 return (BOOL)psi->dwPos;
564 BOOL Spinner_OnSetPos (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
566 psi->dwPos = (DWORD)lp;
567 Spinner_SetNewText (psi, FALSE);
571 BOOL Spinner_OnGetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
575 Spinner_GetNewText (psi);
576 Spinner_OnSetBase (psi, psi->wBase, psi->fSigned);
580 *(WORD *)wp = psi->wBase;
582 *(BOOL *)lp = psi->fSigned;
586 BOOL Spinner_OnSetBase (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
589 Spinner_GetNewText (psi);
597 psi->wBase = (WORD)wp;
605 if (psi->wBase != 10)
606 psi->fSigned = FALSE;
608 psi->fSigned = (BOOL)lp;
610 Spinner_SetNewText (psi, FALSE);
615 BOOL Spinner_OnReattach (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
618 if (psi->rReq.right != 0)
620 rSpinner = psi->rReq;
627 ClientToScreen (GetParent (psi->hBuddy), &pt);
629 GetWindowRect (psi->hBuddy, &rBuddyInParent);
630 rBuddyInParent.left -= pt.x;
631 rBuddyInParent.right -= pt.x;
632 rBuddyInParent.top -= pt.y;
633 rBuddyInParent.bottom -= pt.y;
635 rSpinner.top = rBuddyInParent.top;
636 rSpinner.bottom = rBuddyInParent.bottom -2; // just like Win95 does
637 rSpinner.left = rBuddyInParent.right;
638 rSpinner.right = rBuddyInParent.right +GetSystemMetrics (SM_CXVSCROLL);
641 SetWindowPos (psi->hSpinner, NULL,
642 rSpinner.left, rSpinner.top,
643 rSpinner.right-rSpinner.left,
644 rSpinner.bottom-rSpinner.top,
645 SWP_NOACTIVATE | SWP_NOZORDER);
650 BOOL Spinner_OnSetRect (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
653 if ((prTarget = (LPRECT)lp) == NULL)
654 SetRectEmpty (&psi->rReq);
656 psi->rReq = *prTarget;
658 Spinner_OnReattach (psi, 0, 0);
663 BOOL Spinner_OnGetSpinner (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
665 return (BOOL)psi->hSpinner;
669 BOOL Spinner_OnSetFormat (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
673 Free (psi->pszFormat);
674 psi->pszFormat = NULL;
678 psi->pszFormat = (LPTSTR)Allocate (sizeof(TCHAR) * (1+lstrlen((LPTSTR)lp)));
679 lstrcpy (psi->pszFormat, (LPTSTR)lp);
681 Spinner_SetNewText (psi, FALSE);
686 BOOL Spinner_OnSetBuddy (SpinnerInfo *psi, WPARAM wp, LPARAM lp)
688 HWND hBuddyNew = (HWND)wp;
689 BOOL fMove = (BOOL)lp;
691 // First un-subclass our buddy.
692 // Then subclass the new buddy.
694 Subclass_RemoveHook (psi->hBuddy, SpinnerBuddyProc);
695 psi->hBuddy = hBuddyNew;
696 Subclass_AddHook (psi->hBuddy, SpinnerBuddyProc);
698 // Update our SpinnerInfo structure, and move if requested.
700 Spinner_GetNewText (psi);
704 SetRectEmpty (&psi->rReq);
705 Spinner_OnReattach (psi, 0, 0);
712 void Spinner_GetNewText (SpinnerInfo *psi)
714 // First find out what kind of buddy we have.
715 // That will determine what we do here.
717 TCHAR szBuddyClass[256];
718 GetClassName (psi->hBuddy, szBuddyClass, 256);
720 // For comboboxes and listboxes, the dwPos value is actually
721 // the selected item's index.
723 if (!lstrcmpi (szBuddyClass, TEXT("listbox")))
725 psi->dwPos = (DWORD)LB_GetSelected (psi->hBuddy);
728 if (!lstrcmpi (szBuddyClass, TEXT("combobox")))
730 psi->dwPos = (DWORD)CB_GetSelected (psi->hBuddy);
733 // For edit controls, the dwPos value is actually
734 // the control's text's value.
736 if (!lstrcmpi (szBuddyClass, TEXT("edit")))
739 LPTSTR pszText = szText;
740 BOOL fNegative = FALSE;
742 psi->fNewText = FALSE;
746 GetWindowText (psi->hBuddy, szText, 256);
748 while (*pszText == TEXT(' ') || *pszText == TEXT('\t'))
751 if (*pszText == TEXT('0'))
753 if ((*(1+pszText) == 'x') || (*(1+pszText) == 'X'))
759 else if ((*(1+pszText) == 'b') || (*(1+pszText) == 'B') || (*(1+pszText) == '!'))
765 else if (*(1+pszText) != '\0')
767 // psi->wBase = 8; // ignore octal--time controls use "4:08" etc
772 for ( ; *pszText == TEXT('-'); ++pszText)
774 fNegative = !fNegative;
777 for ( ; *pszText; ++pszText)
779 if (!isdigit( *pszText ) &&
780 !(psi->wBase == 16 && ishexdigit( *pszText )))
785 psi->dwPos *= psi->wBase;
787 if ((DWORD)digitval(*pszText) < (DWORD)psi->wBase)
788 psi->dwPos += digitval( *pszText );
791 if (fNegative && psi->wBase == 10 && psi->fSigned)
793 psi->dwPos = (DWORD)(0 - (signed long)psi->dwPos);
797 if (psi->wBase == 10 && psi->fSigned)
798 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
800 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
804 void Spinner_SetNewText (SpinnerInfo *psi, BOOL fCallBack)
808 // First find out what kind of buddy we have.
809 // That will determine what we do here.
811 TCHAR szBuddyClass[256];
812 GetClassName (psi->hBuddy, szBuddyClass, 256);
814 // Be sure to notify the parent window that the selection may be changing.
818 DWORD dw = psi->dwPos;
819 SpinnerSendCallback (psi, SPN_CHANGE, (LPARAM)&dw);
820 if (dw != SPVAL_UNCHANGED)
824 if (psi->wBase == 10 && psi->fSigned)
825 psi->dwPos = (DWORD)limit ((signed long)psi->dwMin, (signed long)psi->dwPos, (signed long)psi->dwMax);
827 psi->dwPos = (DWORD)limit (psi->dwMin, psi->dwPos, psi->dwMax);
830 psi->fCanCallBack --;
832 // For comboboxes and listboxes, select the item specified by the
835 if (!lstrcmpi (szBuddyClass, TEXT("listbox")))
837 LB_SetSelected (psi->hBuddy, psi->dwPos);
840 if (!lstrcmpi (szBuddyClass, TEXT("combobox")))
842 CB_SetSelected (psi->hBuddy, psi->dwPos);
845 // For edit controls, fill in the new value as text--expressed in the
846 // requested base, using the requested format.
848 if (!lstrcmpi (szBuddyClass, TEXT("edit")))
854 wsprintf (szText, psi->pszFormat, (unsigned long)psi->dwPos);
855 else if (psi->fSigned)
856 wsprintf (szText, TEXT("%ld"), (signed long)psi->dwPos);
858 wsprintf (szText, TEXT("%lu"), (unsigned long)psi->dwPos);
862 wsprintf (szText, TEXT("0x%lX"), (unsigned long)psi->dwPos);
867 LPTSTR pszTemp = szTemp;
868 LPTSTR pszText = szText;
871 *pszTemp++ = TEXT('0');
874 for (DWORD dwRemainder = psi->dwPos; dwRemainder != 0; dwRemainder /= (DWORD)psi->wBase)
876 DWORD dw = (dwRemainder % (DWORD)psi->wBase);
877 *pszTemp++ = TEXT('0') + (TCHAR)dw;
883 *pszText++ = TEXT('0');
885 else if (psi->wBase == 2)
887 *pszText++ = TEXT('0');
888 *pszText++ = TEXT('b');
891 for (--pszTemp; pszTemp >= szTemp; )
893 *pszText++ = *pszTemp--;
895 *pszText = TEXT('\0');
899 SetWindowText (psi->hBuddy, szText);
903 psi->fCanCallBack ++;
905 SpinnerSendCallback (psi, SPN_UPDATE, (LPARAM)psi->dwPos);