Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1237 lines
35 KiB

  1. //*********************************************************************
  2. //* Microsoft Windows **
  3. //* Copyright(c) Microsoft Corp., 1994 **
  4. //*********************************************************************
  5. #include "admincfg.h"
  6. #include "settings.h"
  7. HFONT hfontDlg=NULL;
  8. BOOL SetHwndBkColor(HWND hWnd,COLORREF color);
  9. BOOL GetTextSize(HWND hWnd,CHAR * szText,SIZE * pSize);
  10. BOOL AdjustWindowToText(HWND hWnd,CHAR * szText,UINT xStart,UINT yStart,UINT yPad,
  11. UINT * pnWidth,UINT * pnHeight);
  12. int AddControlHwnd(POLICYDLGINFO * pdi,POLICYCTRLINFO * pPolicyCtrlInfo);
  13. BOOL SetWindowData(POLICYDLGINFO * pdi,HWND hwndControl,DWORD dwType,
  14. UINT uDataIndex,SETTINGS * pSetting);
  15. VOID ProcessScrollBar(HWND hWnd,WPARAM wParam);
  16. VOID ProcessCommand(HWND hWnd,HWND hwndCtrl);
  17. HWND CreateSetting(POLICYDLGINFO * pdi,CHAR * pszClassName,CHAR * pszWindowName,
  18. DWORD dwExStyle,DWORD dwStyle,int x,int y,int cx,int cy,DWORD dwType,UINT uIndex,
  19. SETTINGS * pSetting);
  20. VOID InsertComboboxItems(HWND hwndControl,CHAR *pSuggestionList);
  21. BOOL ValidateIsNotEmpty(HWND hwndCtrl,HWND hDlg,SETTINGS * pSetting,DWORD dwValidate);
  22. BOOL ObjectTypeHasDataOffset(DWORD dwType);
  23. VOID EnsureSettingControlVisible(HWND hDlg,HWND hwndCtrl);
  24. int FindComboboxItemData(HWND hwndControl,UINT nData);
  25. /*******************************************************************
  26. NAME: ClipWndProc
  27. SYNOPSIS: Window proc for ClipClass window
  28. ********************************************************************/
  29. LRESULT CALLBACK ClipWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
  30. {
  31. switch (message) {
  32. case WM_CREATE:
  33. if (!((CREATESTRUCT *) lParam)->lpCreateParams) {
  34. // this is the clip window in the dialog box.
  35. SetScrollRange(hWnd,SB_VERT,0,0,TRUE);
  36. hfontDlg = (HFONT) SendMessage(GetParent(hWnd),WM_GETFONT,0,0L);
  37. } else {
  38. // this is the container window
  39. // store away the dialog box HWND (the grandparent of this
  40. // window) because the pointer to instance data we need lives
  41. // in the dialog's window data
  42. SetWindowLong(hWnd,0,WT_SETTINGS);
  43. }
  44. break;
  45. case WM_USER:
  46. {
  47. HWND hwndParent = GetParent(hWnd);
  48. POLICYDLGINFO * pdi;
  49. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hwndParent, DWLP_USER)))
  50. return FALSE;
  51. // make a container window that is clipped by this windows
  52. if (!(pdi->hwndSettings=CreateWindow("ClipClass",(CHAR *) szNull,
  53. WS_CHILD | WS_VISIBLE,0,0,400,400,hWnd,NULL,ghInst,
  54. (LPVOID) hWnd)))
  55. return FALSE;
  56. SetWindowLong(hWnd,0,WT_CLIP);
  57. return TRUE;
  58. }
  59. break;
  60. case WM_VSCROLL:
  61. if (GetWindowLong(hWnd,0) == WT_CLIP)
  62. ProcessScrollBar(hWnd,wParam);
  63. else goto defproc;
  64. return 0;
  65. break;
  66. case WM_COMMAND:
  67. if (GetWindowLong(hWnd,0) == WT_SETTINGS)
  68. ProcessCommand(hWnd,(HWND) lParam);
  69. break;
  70. case WM_GETDLGCODE:
  71. if (GetWindowLong(hWnd,0) == WT_CLIP) {
  72. SetWindowLongPtr(GetParent(hWnd),DWLP_MSGRESULT,DLGC_WANTTAB |
  73. DLGC_WANTALLKEYS);
  74. return DLGC_WANTTAB | DLGC_WANTALLKEYS;
  75. }
  76. break;
  77. case WM_SETFOCUS:
  78. // if clip window gains keyboard focus, transfer focus to first
  79. // control owned by settings window
  80. if (GetWindowLong(hWnd,0) == WT_CLIP) {
  81. HWND hwndParent = GetParent(hWnd);
  82. POLICYDLGINFO * pdi;
  83. INT nIndex;
  84. BOOL fForward=TRUE;
  85. HWND hwndOK = GetDlgItem(GetParent(hwndParent),IDOK);
  86. int iDelta;
  87. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hwndParent, DWLP_USER)))
  88. return FALSE;
  89. // if OK button lost focus, then we're going backwards
  90. // in tab order; otherwise we're going forwards
  91. if ( (HWND) wParam == hwndOK)
  92. fForward = FALSE;
  93. // find the first control that has a data index (e.g. is
  94. // not static text) and give it focus
  95. if (pdi->nControls) {
  96. if (fForward) { // search from start of table forwards
  97. nIndex = 0;
  98. iDelta = 1;
  99. } else { // search from end of table backwards
  100. nIndex = pdi->nControls-1;
  101. iDelta = -1;
  102. }
  103. for (;nIndex>=0 && nIndex<(int)pdi->nControls;nIndex += iDelta) {
  104. if (pdi->pControlTable[nIndex].uDataIndex !=
  105. NO_DATA_INDEX &&
  106. IsWindowEnabled(pdi->pControlTable[nIndex].hwnd)) {
  107. SetFocus(pdi->pControlTable[nIndex].hwnd);
  108. EnsureSettingControlVisible(hwndParent,
  109. pdi->pControlTable[nIndex].hwnd);
  110. return FALSE;
  111. }
  112. }
  113. }
  114. // only get here if there are no setting windows that can
  115. // receive keyboard focus. Give keyboard focus to the
  116. // next guy in line. This is the "OK" button, unless we
  117. // shift-tabbed to get here from the "OK" button in which
  118. // case the tree window is the next guy in line
  119. if (fForward)
  120. SetFocus(hwndOK);
  121. else SetFocus(GetDlgItem(hwndParent,IDD_TVPOLICIES));
  122. return FALSE;
  123. }
  124. break;
  125. default:
  126. defproc:
  127. return (DefWindowProc(hWnd, message, wParam, lParam));
  128. }
  129. return (0);
  130. }
  131. /*******************************************************************
  132. NAME: ProcessCommand
  133. SYNOPSIS: WM_COMMAND handler for ClipClass window
  134. ********************************************************************/
  135. VOID ProcessCommand(HWND hWnd,HWND hwndCtrl)
  136. {
  137. // get instance-specific struct from dialog
  138. UINT uID = GetWindowLong(hwndCtrl,GWL_ID);
  139. POLICYDLGINFO * pdi;
  140. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(GetParent(GetParent(hWnd)),
  141. DWLP_USER))) return;
  142. if ( (uID >= IDD_SETTINGCTRL) && (uID < IDD_SETTINGCTRL+pdi->nControls)) {
  143. POLICYCTRLINFO * pPolicyCtrlInfo= &pdi->pControlTable[uID - IDD_SETTINGCTRL];
  144. switch (pPolicyCtrlInfo->dwType) {
  145. case STYPE_CHECKBOX:
  146. SendMessage(hwndCtrl,BM_SETCHECK,
  147. !(SendMessage(hwndCtrl,BM_GETCHECK,0,0)),0);
  148. break;
  149. case STYPE_LISTBOX:
  150. ShowListbox(hwndCtrl,pdi->hUser,pPolicyCtrlInfo->pSetting,
  151. pPolicyCtrlInfo->uDataIndex);
  152. break;
  153. default:
  154. // nothing to do
  155. break;
  156. }
  157. }
  158. }
  159. /*******************************************************************
  160. NAME: CreateSettingsControls
  161. SYNOPSIS: Creates controls in settings window
  162. NOTES: Looks at a table of SETTINGS structs to determine
  163. type of control to create and type-specific information.
  164. For some types, more than one control can be created
  165. (for instance, edit fields get a static control with
  166. the title followed by an edit field control).
  167. ENTRY: hDlg - owner dialog
  168. hTable - table of SETTINGS structs containing setting
  169. control information
  170. ********************************************************************/
  171. BOOL CreateSettingsControls(HWND hDlg,SETTINGS * pSetting,BOOL fEnable)
  172. {
  173. CHAR * pObjectData;
  174. POLICYDLGINFO * pdi;
  175. UINT xMax=0,yStart=SC_YSPACING,nHeight,nWidth,yMax,xWindowMax;
  176. HWND hwndControl,hwndBuddy,hwndParent;
  177. RECT rcParent;
  178. DWORD dwType, dwStyle;
  179. UINT uEnable = (fEnable ? 0 : WS_DISABLED);
  180. WINDOWPLACEMENT wp;
  181. // get instance-specific struct from dialog
  182. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)))
  183. return FALSE;
  184. wp.length = sizeof(wp);
  185. if (!GetWindowPlacement(GetDlgItem(hDlg,IDD_TVPOLICIES),&wp))
  186. return FALSE;
  187. xWindowMax = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
  188. pdi->pCurrentSettings = pSetting;
  189. while (pSetting) {
  190. pObjectData = GETOBJECTDATAPTR(pSetting);
  191. dwType = pSetting->dwType & STYPE_MASK;
  192. nWidth = 0;
  193. switch (dwType) {
  194. case STYPE_TEXT:
  195. // create static text control
  196. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC,
  197. (CHAR *) (GETNAMEPTR(pSetting)),0,SSTYLE_STATIC | uEnable,0,
  198. yStart,0,15,STYPE_TEXT,NO_DATA_INDEX,0)))
  199. return FALSE;
  200. AdjustWindowToText(hwndControl,(CHAR *) (GETNAMEPTR(pSetting))
  201. ,SC_XSPACING,yStart,0,&nWidth,&nHeight);
  202. yStart += nHeight + SC_YSPACING;
  203. nWidth += SC_XSPACING;
  204. break;
  205. case STYPE_CHECKBOX:
  206. // create checkbox control
  207. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szBUTTON,
  208. (CHAR *) (GETNAMEPTR(pSetting)),0,SSTYLE_CHECKBOX | uEnable,
  209. 0,yStart,200,nHeight,STYPE_CHECKBOX,pSetting->uDataIndex,
  210. pSetting)))
  211. return FALSE;
  212. nWidth = 20;
  213. AdjustWindowToText(hwndControl,(CHAR *) (GETNAMEPTR(pSetting))
  214. ,SC_XSPACING,yStart,0,&nWidth,&nHeight);
  215. yStart += nHeight + SC_YSPACING;
  216. nWidth += SC_XSPACING;
  217. break;
  218. case STYPE_EDITTEXT:
  219. case STYPE_COMBOBOX:
  220. // create static text with setting name
  221. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC,
  222. GETNAMEPTR(pSetting),0,SSTYLE_STATIC | uEnable,0,0,0,0,
  223. STYPE_TEXT,NO_DATA_INDEX,0)))
  224. return FALSE;
  225. AdjustWindowToText(hwndControl,
  226. GETNAMEPTR(pSetting),SC_XSPACING,yStart,SC_YPAD,
  227. &nWidth,&nHeight);
  228. nWidth += SC_XSPACING + 5;
  229. if (nWidth + SC_EDITWIDTH> xWindowMax) {
  230. // if next control will stick out of settings window,
  231. // put it on the next line
  232. if (nWidth > xMax)
  233. xMax = nWidth;
  234. yStart += nHeight + SC_YSPACING - SC_YPAD;
  235. nWidth = SC_XINDENT;
  236. }
  237. // create edit field or combo box control
  238. if (dwType == STYPE_EDITTEXT) {
  239. hwndControl = CreateSetting(pdi,(CHAR *) szEDIT,(CHAR *) szNull,
  240. WS_EX_CLIENTEDGE,SSTYLE_EDITTEXT | uEnable,nWidth,yStart,SC_EDITWIDTH,nHeight,
  241. STYPE_EDITTEXT,pSetting->uDataIndex,pSetting);
  242. } else {
  243. dwStyle = SSTYLE_COMBOBOX | uEnable;
  244. if (pSetting->dwFlags & DF_NOSORT) {
  245. dwStyle &= ~CBS_SORT;
  246. }
  247. hwndControl = CreateSetting(pdi,(CHAR *) szCOMBOBOX,(CHAR *)szNull,
  248. WS_EX_CLIENTEDGE,dwStyle,nWidth,yStart,SC_EDITWIDTH,nHeight*3,
  249. STYPE_COMBOBOX,pSetting->uDataIndex,pSetting);
  250. }
  251. if (!hwndControl) return FALSE;
  252. // limit the text length appropriately
  253. SendMessage(hwndControl,EM_SETLIMITTEXT,
  254. (WPARAM) ((EDITTEXTINFO *) pObjectData)->nMaxLen,0L);
  255. if (dwType == STYPE_COMBOBOX &&
  256. ((POLICYCOMBOBOXINFO *) pObjectData)->uOffsetSuggestions)
  257. InsertComboboxItems(hwndControl,(CHAR *) pSetting +
  258. ((POLICYCOMBOBOXINFO *) pObjectData)->uOffsetSuggestions);
  259. yStart += (UINT) ((float) nHeight*1.3) + SC_YSPACING;
  260. nWidth += SC_EDITWIDTH;
  261. break;
  262. case STYPE_NUMERIC:
  263. // create static text for setting
  264. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC,
  265. GETNAMEPTR(pSetting),0,
  266. SSTYLE_STATIC | uEnable,0,0,0,0,STYPE_TEXT,NO_DATA_INDEX,0)))
  267. return FALSE;
  268. AdjustWindowToText(hwndControl,
  269. GETNAMEPTR(pSetting),SC_XSPACING,yStart,SC_YPAD,
  270. &nWidth,&nHeight);
  271. nWidth += SC_XSPACING + 5;
  272. // create edit field
  273. if (!(hwndBuddy = CreateSetting(pdi,(CHAR *) szEDIT,
  274. (CHAR *) szNull,WS_EX_CLIENTEDGE,SSTYLE_EDITTEXT | uEnable,nWidth,yStart,SC_UPDOWNWIDTH,
  275. nHeight,STYPE_NUMERIC,pSetting->uDataIndex,pSetting)))
  276. return FALSE;
  277. // SendMessage(hwndBuddy,EM_LIMITTEXT,4,0);
  278. nWidth += SC_UPDOWNWIDTH + SC_XLEADING;
  279. // create spin (up-down) control if specifed
  280. if (((NUMERICINFO *)pObjectData)->uSpinIncrement) {
  281. UDACCEL udAccel = {0,0};
  282. UINT nMax,nMin;
  283. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szUPDOWN,
  284. (CHAR *) szNull,0,SSTYLE_UPDOWN | UDS_SETBUDDYINT | uEnable,nWidth,yStart,SC_UPDOWNWIDTH2,
  285. nHeight,STYPE_TEXT,NO_DATA_INDEX,0)))
  286. return FALSE;
  287. nWidth += SC_UPDOWNWIDTH2;
  288. nMax = ((NUMERICINFO *) pObjectData)->uMaxValue;
  289. nMin = ((NUMERICINFO *) pObjectData)->uMinValue;
  290. udAccel.nInc = ((NUMERICINFO *) pObjectData)->uSpinIncrement;
  291. SendMessage(hwndControl,UDM_SETBUDDY,(WPARAM) hwndBuddy,0L);
  292. SendMessage(hwndControl,UDM_SETRANGE,0,MAKELONG(nMax,nMin));
  293. SendMessage(hwndControl,UDM_SETACCEL,1,(LPARAM) &udAccel);
  294. }
  295. yStart += nHeight + SC_YSPACING;
  296. break;
  297. case STYPE_DROPDOWNLIST:
  298. // create text description
  299. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC,
  300. GETNAMEPTR(pSetting),0,
  301. SSTYLE_STATIC | uEnable,0,0,0,0,STYPE_TEXT,NO_DATA_INDEX,0)))
  302. return FALSE;
  303. AdjustWindowToText(hwndControl,
  304. GETNAMEPTR(pSetting),SC_XSPACING,yStart,SC_YPAD,&nWidth,&nHeight);
  305. nWidth += SC_XLEADING;
  306. if (nWidth + SC_EDITWIDTH> xWindowMax) {
  307. // if next control will stick out of settings window,
  308. // put it on the next line
  309. if (nWidth > xMax)
  310. xMax = nWidth;
  311. yStart += nHeight + SC_YSPACING - SC_YPAD;
  312. nWidth = SC_XINDENT;
  313. }
  314. dwStyle = SSTYLE_DROPDOWNLIST | uEnable;
  315. if (pSetting->dwFlags & DF_NOSORT) {
  316. dwStyle &= ~CBS_SORT;
  317. }
  318. // create drop down listbox
  319. hwndControl = CreateSetting(pdi,(CHAR *) szCOMBOBOX,(CHAR *) szNull,
  320. WS_EX_CLIENTEDGE,dwStyle,nWidth,yStart,SC_EDITWIDTH,nHeight*3,
  321. STYPE_DROPDOWNLIST,pSetting->uDataIndex,pSetting);
  322. if (!hwndControl) return FALSE;
  323. nWidth += SC_EDITWIDTH;
  324. {
  325. // insert dropdown list items into control
  326. UINT uOffset = pSetting->uOffsetObjectData,nIndex=0;
  327. DROPDOWNINFO * pddi;
  328. int iSel;
  329. while (uOffset) {
  330. pddi = (DROPDOWNINFO *) ( (CHAR *) pSetting + uOffset);
  331. iSel=(int)SendMessage(hwndControl,CB_ADDSTRING,0,(LPARAM)
  332. ((CHAR *) pSetting + pddi->uOffsetItemName));
  333. if (iSel<0) return FALSE;
  334. SendMessage(hwndControl,CB_SETITEMDATA,iSel,nIndex);
  335. nIndex++;
  336. uOffset = pddi->uOffsetNextDropdowninfo;
  337. }
  338. }
  339. yStart += (UINT) ((float) nHeight*1.3) + 1;
  340. break;
  341. case STYPE_LISTBOX:
  342. // create static text with description
  343. if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC,
  344. GETNAMEPTR(pSetting),0,
  345. SSTYLE_STATIC | uEnable,0,0,0,0,STYPE_TEXT,NO_DATA_INDEX,0)))
  346. return FALSE;
  347. AdjustWindowToText(hwndControl,GETNAMEPTR(pSetting),SC_XSPACING,yStart,
  348. SC_YPAD,&nWidth,&nHeight);
  349. nWidth += SC_XLEADING;
  350. if (nWidth + LISTBOX_BTN_WIDTH> xWindowMax) {
  351. // if next control will stick out of settings window,
  352. // put it on the next line
  353. if (nWidth > xMax)
  354. xMax = nWidth;
  355. yStart += nHeight + SC_YSPACING - SC_YPAD;
  356. nWidth = SC_XINDENT;
  357. }
  358. // create pushbutton to show listbox contents
  359. hwndControl = CreateSetting(pdi,(CHAR *) szBUTTON,LoadSz(IDS_LISTBOX_SHOW,
  360. szSmallBuf,sizeof(szSmallBuf)),0,
  361. SSTYLE_LBBUTTON | uEnable,nWidth+5,yStart,
  362. LISTBOX_BTN_WIDTH,nHeight,STYPE_LISTBOX,
  363. pSetting->uDataIndex,pSetting);
  364. if (!hwndControl) return FALSE;
  365. nWidth += LISTBOX_BTN_WIDTH + SC_XLEADING;
  366. yStart += nHeight+1;
  367. }
  368. if (nWidth > xMax)
  369. xMax = nWidth;
  370. pSetting = (SETTINGS *) pSetting->pNext;
  371. }
  372. yMax = yStart - 1;
  373. SetWindowPos(pdi->hwndSettings,NULL,0,0,xMax,yMax,SWP_NOZORDER);
  374. hwndParent = GetParent(pdi->hwndSettings);
  375. GetClientRect(hwndParent,&rcParent);
  376. if (yMax > (UINT) rcParent.bottom-rcParent.top) {
  377. SetScrollRange(hwndParent,SB_VERT,0,100,TRUE);
  378. SetScrollPos(hwndParent,SB_VERT,0,TRUE);
  379. } else SetScrollRange(hwndParent,SB_VERT,0,0,TRUE);
  380. return TRUE;
  381. }
  382. /*******************************************************************
  383. NAME: CreateSettings
  384. SYNOPSIS: Creates a control and add it to the table of settings
  385. controls
  386. ********************************************************************/
  387. HWND CreateSetting(POLICYDLGINFO * pdi,CHAR * pszClassName,CHAR * pszWindowName,
  388. DWORD dwExStyle,DWORD dwStyle,int x,int y,int cx,int cy,DWORD dwType,UINT uIndex,
  389. SETTINGS * pSetting)
  390. {
  391. HWND hwndControl;
  392. if (!(hwndControl = CreateWindowEx(WS_EX_NOPARENTNOTIFY | dwExStyle,
  393. pszClassName,pszWindowName,dwStyle,x,y,cx,cy,pdi->hwndSettings,NULL,
  394. ghInst,NULL))) return NULL;
  395. if (!SetWindowData(pdi,hwndControl,dwType,uIndex,pSetting)) {
  396. DestroyWindow(hwndControl);
  397. return NULL;
  398. }
  399. SendMessage(hwndControl,WM_SETFONT,(WPARAM) hfontDlg,MAKELPARAM(TRUE,0));
  400. return hwndControl;
  401. }
  402. /*******************************************************************
  403. NAME: ProcessSettingsControls
  404. SYNOPSIS: For each settings control, takes currently entered data
  405. from control and saves data in user's buffer.
  406. ********************************************************************/
  407. BOOL ProcessSettingsControls(HWND hDlg,DWORD dwValidate)
  408. {
  409. UINT nIndex;
  410. USERDATA * pUserData;
  411. POLICYDLGINFO * pdi;
  412. SETTINGS * pSetting;
  413. CHAR szTextData[MAXSTRLEN];
  414. BOOL fRet=TRUE;
  415. // get instance-specific struct from dialog
  416. // from instance-specific struct, get user's data buffer and setting info
  417. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)) ||
  418. !(pUserData = (USERDATA *) GlobalLock(pdi->hUser))) {
  419. MsgBox(hDlg,IDS_ErrOUTOFMEMORY,MB_ICONEXCLAMATION,MB_OK);
  420. fRet=FALSE;
  421. goto cleanup;
  422. }
  423. pSetting = pdi->pCurrentSettings;
  424. for (nIndex=0;nIndex<pdi->nControls;nIndex++) {
  425. pSetting = pdi->pControlTable[nIndex].pSetting;
  426. if (pdi->pControlTable[nIndex].uDataIndex != NO_DATA_INDEX) {
  427. switch (pdi->pControlTable[nIndex].dwType) {
  428. case STYPE_CHECKBOX:
  429. pUserData->SettingData[pdi->pControlTable[nIndex].uDataIndex].uData
  430. = (UINT)SendMessage(pdi->pControlTable[nIndex].hwnd,BM_GETCHECK,
  431. 0,0L);
  432. break;
  433. case STYPE_EDITTEXT:
  434. case STYPE_COMBOBOX:
  435. SendMessage(pdi->pControlTable[nIndex].hwnd,WM_GETTEXT,
  436. sizeof(szTextData),(LPARAM) szTextData);
  437. // don't allow empty text if this is required field
  438. if (dwValidate && !ValidateIsNotEmpty(pdi->pControlTable[nIndex].hwnd,
  439. hDlg,pSetting,dwValidate)) {
  440. fRet = FALSE;
  441. goto cleanup;
  442. }
  443. // add this text to buffer. Need to unlock handle before
  444. // call and relock afterwards, as buffer may resize and move
  445. GlobalUnlock(pdi->hUser);
  446. SetVariableLengthData(pdi->hUser,pdi->pControlTable[nIndex].uDataIndex,
  447. szTextData,lstrlen(szTextData)+1);
  448. if (!(pUserData = (USERDATA *) GlobalLock(pdi->hUser)))
  449. return FALSE;
  450. break;
  451. case STYPE_NUMERIC:
  452. {
  453. BOOL fTranslated;
  454. UINT uRet;
  455. uRet=GetDlgItemInt(pdi->hwndSettings,nIndex+IDD_SETTINGCTRL,
  456. &fTranslated,FALSE);
  457. if (dwValidate) {
  458. NUMERICINFO * pNumericInfo;
  459. // don't allow empty text if this is required field
  460. if (!ValidateIsNotEmpty(pdi->pControlTable[nIndex].hwnd,
  461. hDlg,pSetting,dwValidate)) {
  462. fRet = FALSE;
  463. goto cleanup;
  464. }
  465. // check for non-numeric in edit ctrl
  466. if (!fTranslated) {
  467. if (dwValidate == PSC_VALIDATENOISY) {
  468. MsgBoxParam(hDlg,IDS_INVALIDNUM,
  469. GETNAMEPTR(pSetting),MB_ICONINFORMATION,
  470. MB_OK);
  471. SetFocus(pdi->pControlTable[nIndex].hwnd);
  472. SendMessage(pdi->pControlTable[nIndex].hwnd,
  473. EM_SETSEL,0,-1);
  474. }
  475. fRet = FALSE;
  476. goto cleanup;
  477. }
  478. // validate for max and min
  479. pNumericInfo = (NUMERICINFO *) GETOBJECTDATAPTR(pSetting);
  480. if (uRet < pNumericInfo->uMinValue) uRet = pNumericInfo->uMinValue;
  481. if (uRet > pNumericInfo->uMaxValue) uRet = pNumericInfo->uMaxValue;
  482. }
  483. pUserData->SettingData[pdi->pControlTable[nIndex].uDataIndex].uData =
  484. (fTranslated ? uRet : NO_VALUE);
  485. }
  486. break;
  487. case STYPE_DROPDOWNLIST:
  488. {
  489. int iSel;
  490. iSel = (int)SendMessage(pdi->pControlTable[nIndex].hwnd,
  491. CB_GETCURSEL,0,0L);
  492. iSel = (int)SendMessage(pdi->pControlTable[nIndex].hwnd,
  493. CB_GETITEMDATA,iSel,0L);
  494. if (dwValidate && iSel < 0) {
  495. if (dwValidate == PSC_VALIDATENOISY) {
  496. MsgBoxParam(hDlg,IDS_ENTRYREQUIRED,GETNAMEPTR(pSetting),
  497. MB_ICONINFORMATION,MB_OK);
  498. SetFocus(pdi->pControlTable[nIndex].hwnd);
  499. }
  500. return FALSE;
  501. }
  502. pUserData->SettingData[pdi->pControlTable[nIndex].uDataIndex].uData
  503. = (UINT) iSel;
  504. }
  505. break;
  506. }
  507. }
  508. }
  509. cleanup:
  510. GlobalUnlock(pdi->hUser);
  511. return fRet;
  512. }
  513. /*******************************************************************
  514. NAME: FreeSettingsControls
  515. SYNOPSIS: Frees all settings controls
  516. ********************************************************************/
  517. VOID FreeSettingsControls(HWND hDlg)
  518. {
  519. UINT nIndex;
  520. POLICYDLGINFO * pdi;
  521. // get instance-specific struct from dialog
  522. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)))
  523. return;
  524. for (nIndex=0;nIndex<pdi->nControls;nIndex++) {
  525. DestroyWindow(pdi->pControlTable[nIndex].hwnd);
  526. }
  527. pdi->pCurrentSettings = NULL;
  528. pdi->nControls = 0;
  529. SetScrollRange(pdi->hwndSettings,SB_VERT,0,0,TRUE);
  530. }
  531. /*******************************************************************
  532. NAME: SetVariableLengthData
  533. SYNOPSIS: Adds variable length data to user's data buffer, reallocs
  534. if necessary.
  535. CAVEATS: Since this call may realloc the user's data buffer (hUser),
  536. it must always be unlocked before calling.
  537. NOTES: For variable-length data (edit-text, listboxes), the
  538. SettingData struct for a particular setting contains an
  539. offset to the actual data. (The offset is from the beginning
  540. of the USERDATA struct.) If this offset is zero, it means
  541. no data has been entered and there is no allocation for
  542. that setting yet. The offset points to a STRDATA struct,
  543. which contains a size entry followed by data. If an allocation
  544. currently exists but is insufficient for new data (e.g.,
  545. user previously entered a string value, now goes back and
  546. changes it and the new one is longer), then the buffer is
  547. realloced, the end of the buffer is memcopy'd to enlarge the
  548. region of allocation, and offsets into the section that moved
  549. are fixed up.
  550. ********************************************************************/
  551. #define SHRINK_THRESHOLD 64
  552. BOOL SetVariableLengthData(HGLOBAL hUser,UINT nDataIndex,TCHAR * pchData,
  553. DWORD cbData)
  554. {
  555. USERDATA * pUserData;
  556. STRDATA * pStrData = NULL;
  557. DWORD dwCurrentSize,dwStrSize;
  558. UINT uOffsetData;
  559. if (!(pUserData = (USERDATA *) GlobalLock(hUser)))
  560. return FALSE;
  561. uOffsetData = pUserData->SettingData[nDataIndex].uOffsetData;
  562. // if no existing data and no new data, nothing to do
  563. if ((!uOffsetData || uOffsetData == INIT_WITH_DEFAULT) && cbData<1) {
  564. GlobalUnlock(hUser);
  565. return TRUE;
  566. }
  567. if (!uOffsetData || uOffsetData == INIT_WITH_DEFAULT) {
  568. // no data for this setting yet, allocate extra room and stick
  569. // this data at the end of the buffer
  570. pUserData->SettingData[nDataIndex].uOffsetData = pUserData->dwSize;
  571. dwStrSize = 0;
  572. } else {
  573. // there's already data for this setting, make sure there's enough
  574. // room to put the new data in the buffer
  575. pStrData = (STRDATA *) ( (LPBYTE) pUserData +
  576. pUserData->SettingData[nDataIndex].uOffsetData);
  577. dwStrSize = pStrData->dwSize;
  578. }
  579. // if the current string buffer is not large enough, or is larger than
  580. // we need by a lot (SHRINK_THRESHOLD), resize that buffer area to just
  581. // fit the current data (rounded up to DWORD alignment).
  582. // add sizeof(DWORD) to make room for length header in STRDATA struct
  583. if ((dwStrSize < cbData + sizeof(DWORD)) ||
  584. (dwStrSize > cbData+sizeof(DWORD)+SHRINK_THRESHOLD)) {
  585. int nAdditional = RoundToDWord(cbData -
  586. (dwStrSize - sizeof(DWORD)));
  587. DWORD dwOldSize;
  588. dwOldSize = dwCurrentSize = pUserData->dwSize;
  589. // resize the buffer to make more room
  590. if (!(pUserData = (USERDATA *) ResizeBuffer((CHAR *) pUserData,
  591. hUser,dwCurrentSize+nAdditional,&dwCurrentSize)))
  592. return FALSE;
  593. pUserData->dwSize = dwCurrentSize;
  594. pStrData = (STRDATA *) ( (CHAR *) pUserData +
  595. pUserData->SettingData[nDataIndex].uOffsetData);
  596. pStrData->dwSize = dwStrSize;
  597. // we may be expanding the middle of the buffer-- copy the rest of
  598. // the buffer "down" to make room
  599. {
  600. TCHAR * pchFrom,* pchTo;
  601. UINT cbMove;
  602. UINT uOffsetMoved;
  603. UINT nIndex;
  604. pchFrom = (TCHAR *) pStrData + pStrData->dwSize;
  605. pchTo = pchFrom + nAdditional;
  606. uOffsetMoved = (UINT)(pchFrom - (CHAR *) pUserData);
  607. cbMove = dwOldSize - uOffsetMoved;
  608. pStrData->dwSize += nAdditional;
  609. if (cbMove) {
  610. memmove(pchTo,pchFrom,cbMove * sizeof(TCHAR));
  611. // now we've moved a chunk of the buffer, need to fix up
  612. // data offsets that point into the chunk that moved.
  613. for (nIndex = 0;nIndex<pUserData->nSettings;nIndex++) {
  614. if (ObjectTypeHasDataOffset(pUserData->SettingData[nIndex].dwType
  615. & STYPE_MASK)) {
  616. if (pUserData->SettingData[nIndex].uOffsetData != INIT_WITH_DEFAULT) {
  617. if (pUserData->SettingData[nIndex].uOffsetData >= uOffsetMoved) {
  618. pUserData->SettingData[nIndex].uOffsetData
  619. += nAdditional;
  620. }
  621. }
  622. }
  623. }
  624. }
  625. }
  626. }
  627. if (pStrData)
  628. memcpy(pStrData->szData,pchData, cbData * sizeof(TCHAR));
  629. GlobalUnlock(hUser);
  630. return TRUE;
  631. }
  632. /*******************************************************************
  633. NAME: EnableSettingsControls
  634. SYNOPSIS: Enables or disables all setting controls
  635. NOTES: If disabling, "clears" control as appropriate to the
  636. control type (e.g. for edit fields, clears text); if
  637. enabling, restores control contents from user's data buffer
  638. ********************************************************************/
  639. BOOL EnableSettingsControls(HWND hDlg,BOOL fEnable)
  640. {
  641. UINT nIndex;
  642. USERDATA * pUserData;
  643. UINT nDataIndex;
  644. HWND hwndControl;
  645. POLICYDLGINFO * pdi;
  646. SETTINGS * pSetting;
  647. // get instance-specific struct from dialog
  648. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)) ||
  649. !pdi->pCurrentSettings)
  650. return FALSE;
  651. if (!(pUserData = (USERDATA *) GlobalLock(pdi->hUser))) return FALSE;
  652. pSetting = pdi->pCurrentSettings;
  653. for (nIndex=0;nIndex<pdi->nControls;nIndex++) {
  654. nDataIndex = pdi->pControlTable[nIndex].uDataIndex;
  655. hwndControl = pdi->pControlTable[nIndex].hwnd;
  656. switch (pdi->pControlTable[nIndex].dwType) {
  657. case STYPE_CHECKBOX:
  658. SendMessage(hwndControl,BM_SETCHECK,(fEnable ?
  659. pUserData->SettingData[nDataIndex].uData :
  660. 0),0L);
  661. break;
  662. case STYPE_EDITTEXT:
  663. case STYPE_COMBOBOX:
  664. // for edit fields, clear text from edit field if disabled;
  665. // replace text if enabled
  666. // if this is the first time this setting is enabled for this
  667. // user and there is a default value to use, set that in
  668. // the user's data buffer
  669. if (fEnable && pUserData->SettingData[nDataIndex].uOffsetData ==
  670. INIT_WITH_DEFAULT) {
  671. CHAR * pszDefaultText;
  672. pszDefaultText = (CHAR *) pdi->pControlTable[nIndex].pSetting +
  673. ((EDITTEXTINFO *) GETOBJECTDATAPTR(pdi->pControlTable[nIndex].
  674. pSetting))->uOffsetDefText;
  675. // add this text to buffer. Need to unlock handle before
  676. // call and relock afterwards, as buffer may resize and move
  677. GlobalUnlock(pdi->hUser);
  678. SetVariableLengthData(pdi->hUser,pdi->pControlTable[nIndex].uDataIndex,
  679. pszDefaultText,lstrlen(pszDefaultText)+1);
  680. if (!(pUserData = (USERDATA *) GlobalLock(pdi->hUser)))
  681. return FALSE;
  682. }
  683. if (fEnable) {
  684. // set the text appropriately
  685. if (pUserData->SettingData[nDataIndex].uOffsetData) {
  686. CHAR * pchText;
  687. pchText = ((STRDATA *) ((CHAR *) pUserData +
  688. pUserData->SettingData[nDataIndex].uOffsetData))
  689. ->szData;
  690. SendMessage(hwndControl,WM_SETTEXT,0,(LPARAM) pchText);
  691. }
  692. } else {
  693. // clear the text
  694. SendMessage(hwndControl,WM_SETTEXT,0,(LPARAM) szNull);
  695. }
  696. break;
  697. case STYPE_NUMERIC:
  698. if (fEnable) {
  699. UINT uData = pUserData->SettingData[nDataIndex].uData;
  700. if (uData != NO_VALUE)
  701. SetDlgItemInt(pdi->hwndSettings,nIndex+IDD_SETTINGCTRL,
  702. uData,FALSE);
  703. } else {
  704. // clear the number
  705. SendMessage(hwndControl,WM_SETTEXT,0,(LPARAM) szNull);
  706. }
  707. break;
  708. case STYPE_DROPDOWNLIST:
  709. if (fEnable) {
  710. SendMessage(hwndControl,CB_SETCURSEL,
  711. FindComboboxItemData(hwndControl,pUserData->SettingData[nDataIndex].uData),
  712. 0L);
  713. } else {
  714. SendMessage(hwndControl,CB_SETCURSEL,(UINT) -1,0L);
  715. }
  716. break;
  717. case STYPE_LISTBOX:
  718. // nothing to do
  719. break;
  720. }
  721. EnableWindow(pdi->pControlTable[nIndex].hwnd,fEnable);
  722. }
  723. GlobalUnlock(pdi->hUser);
  724. return TRUE;
  725. }
  726. BOOL SetWindowData(POLICYDLGINFO * pdi,HWND hwndControl,DWORD dwType,
  727. UINT uDataIndex,SETTINGS * pSetting)
  728. {
  729. POLICYCTRLINFO PolicyCtrlInfo;
  730. int iCtrl;
  731. PolicyCtrlInfo.hwnd = hwndControl;
  732. PolicyCtrlInfo.dwType = dwType;
  733. PolicyCtrlInfo.uDataIndex = uDataIndex;
  734. PolicyCtrlInfo.pSetting = pSetting;
  735. iCtrl = AddControlHwnd(pdi,&PolicyCtrlInfo);
  736. if (iCtrl < 0) return FALSE;
  737. SetWindowLong(hwndControl,GWL_ID,iCtrl + IDD_SETTINGCTRL);
  738. return TRUE;
  739. }
  740. BOOL AdjustWindowToText(HWND hWnd,CHAR * szText,UINT xStart,UINT yStart,
  741. UINT yPad,UINT * pnWidth,UINT * pnHeight)
  742. {
  743. SIZE size;
  744. if (GetTextSize(hWnd,szText,&size))
  745. {
  746. *pnHeight =size.cy + yPad;
  747. *pnWidth += size.cx;
  748. SetWindowPos(hWnd,NULL,xStart,yStart,*pnWidth,*pnHeight,SWP_NOZORDER);
  749. }
  750. return FALSE;
  751. }
  752. BOOL SetHwndBkColor(HWND hWnd,COLORREF color)
  753. {
  754. HDC hDC;
  755. BOOL fRet;
  756. if (!(hDC = GetDC(hWnd))) return FALSE;
  757. fRet=(SetBkColor(hDC,color) != CLR_INVALID);
  758. ReleaseDC(hWnd,hDC);
  759. return fRet;
  760. }
  761. BOOL GetTextSize(HWND hWnd,CHAR * szText,SIZE * pSize)
  762. {
  763. HDC hDC;
  764. BOOL fRet;
  765. if (!(hDC = GetDC(hWnd))) return FALSE;
  766. SelectObject(hDC, hfontDlg);
  767. fRet=GetTextExtentPoint(hDC,szText,lstrlen(szText),pSize);
  768. ReleaseDC(hWnd,hDC);
  769. return fRet;
  770. }
  771. int AddControlHwnd(POLICYDLGINFO * pdi,POLICYCTRLINFO * pPolicyCtrlInfo)
  772. {
  773. int iRet;
  774. DWORD dwNeeded;
  775. POLICYCTRLINFO * pTemp;
  776. // grow table if necessary
  777. dwNeeded = (pdi->nControls+1) * sizeof(POLICYCTRLINFO);
  778. if (dwNeeded > pdi->dwControlTableSize) {
  779. pTemp = (POLICYCTRLINFO *) GlobalReAlloc(pdi->pControlTable,
  780. dwNeeded,GMEM_ZEROINIT | GMEM_MOVEABLE);
  781. if (!pTemp) return (-1);
  782. pdi->pControlTable = pTemp;
  783. pdi->dwControlTableSize = dwNeeded;
  784. }
  785. pdi->pControlTable[pdi->nControls] = *pPolicyCtrlInfo;
  786. iRet = (int) pdi->nControls;
  787. (pdi->nControls)++;
  788. return iRet;
  789. }
  790. // scrolls the control window into view if it's not visible
  791. VOID EnsureSettingControlVisible(HWND hDlg,HWND hwndCtrl)
  792. {
  793. // get the clip window, which owns the scroll bar
  794. HWND hwndClip = GetDlgItem(hDlg,IDD_TVSETTINGS);
  795. POLICYDLGINFO * pdi;
  796. UINT nPos = GetScrollPos(hwndClip,SB_VERT),ySettingWindowSize,yClipWindowSize;
  797. UINT nExtra;
  798. int iMin,iMax=0;
  799. WINDOWPLACEMENT wp;
  800. RECT rcCtrl;
  801. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)))
  802. return;
  803. // find the scroll range
  804. GetScrollRange(hwndClip,SB_VERT,&iMin,&iMax);
  805. if (!iMax) // no scroll bar, nothing to do
  806. return;
  807. // find the y size of the settings window that contains the settings controls
  808. // (this is clipped by the clip window in the dialog, scroll bar moves the
  809. // setting window up and down behind the clip window)
  810. wp.length = sizeof(wp);
  811. if (!GetWindowPlacement(pdi->hwndSettings,&wp))
  812. return; // unlikely to fail, but just bag out if it does rather than do something wacky
  813. ySettingWindowSize=wp.rcNormalPosition.bottom-wp.rcNormalPosition.top;
  814. // find y size of clip window
  815. if (!GetWindowPlacement(hwndClip,&wp))
  816. return; // unlikely to fail, but just bag out if it does rather than do something wacky
  817. yClipWindowSize=wp.rcNormalPosition.bottom-wp.rcNormalPosition.top;
  818. nExtra = ySettingWindowSize - yClipWindowSize;
  819. // if setting window is smaller than clip window, there should be no
  820. // scroll bar so we should never get here. Check just in case though...
  821. if (ySettingWindowSize < yClipWindowSize)
  822. return;
  823. // get y position of control to be made visible
  824. if (!GetWindowPlacement(hwndCtrl,&wp))
  825. return;
  826. rcCtrl = wp.rcNormalPosition;
  827. rcCtrl.bottom = min ((int) ySettingWindowSize,rcCtrl.bottom + SC_YPAD);
  828. rcCtrl.top = max ((int) 0,rcCtrl.top - SC_YPAD);
  829. // if bottom of control is out of view, scroll the settings window up
  830. if ((float) rcCtrl.bottom >
  831. (float) (yClipWindowSize + ( (float) nPos/(float)iMax) * (ySettingWindowSize -
  832. yClipWindowSize))) {
  833. UINT nNewPos = (UINT)
  834. ( ((float) (nExtra - (ySettingWindowSize - rcCtrl.bottom)) / (float) nExtra) * iMax);
  835. SetScrollPos(hwndClip,SB_VERT,nNewPos,TRUE);
  836. ProcessScrollBar(hwndClip,MAKELPARAM(SB_THUMBTRACK,nNewPos));
  837. return;
  838. }
  839. // if top of control is out of view, scroll the settings window down
  840. if ((float) rcCtrl.top <
  841. (float) ( (float) nPos/(float)iMax) * nExtra) {
  842. UINT nNewPos = (UINT)
  843. ( ((float) rcCtrl.top / (float) nExtra) * iMax);
  844. SetScrollPos(hwndClip,SB_VERT,nNewPos,TRUE);
  845. ProcessScrollBar(hwndClip,MAKELPARAM(SB_THUMBTRACK,nNewPos));
  846. return;
  847. }
  848. }
  849. VOID ProcessScrollBar(HWND hWnd,WPARAM wParam)
  850. {
  851. UINT nPos = GetScrollPos(hWnd,SB_VERT);
  852. RECT rcParent,rcChild;
  853. POLICYDLGINFO * pdi;
  854. // get instance-specific struct from dialog
  855. if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(GetParent(hWnd),DWLP_USER)))
  856. return;
  857. switch (LOWORD(wParam)) {
  858. case SB_THUMBPOSITION:
  859. case SB_THUMBTRACK:
  860. nPos = HIWORD(wParam);
  861. break;
  862. case SB_TOP:
  863. nPos = 0;
  864. break;
  865. case SB_BOTTOM:
  866. nPos = 100;
  867. break;
  868. case SB_LINEUP:
  869. nPos -= 10;
  870. break;
  871. case SB_LINEDOWN:
  872. nPos += 10;
  873. break;
  874. case SB_PAGEUP:
  875. nPos -= 30;
  876. break;
  877. case SB_PAGEDOWN:
  878. nPos += 30;
  879. break;
  880. }
  881. // wsprintf(szDebugOut,"nPos: %lu\r\n",nPos);
  882. // OutputDebugString(szDebugOut);
  883. SetScrollPos(hWnd,SB_VERT,nPos,TRUE);
  884. GetClientRect(hWnd,&rcParent);
  885. GetClientRect(pdi->hwndSettings,&rcChild);
  886. SetWindowPos(pdi->hwndSettings,NULL,0,-(int) ((( (float)
  887. (rcChild.bottom-rcChild.top)-(rcParent.bottom-rcParent.top))
  888. /100.0) * (float) nPos),rcChild.right,rcChild.bottom,SWP_NOZORDER |
  889. SWP_NOSIZE);
  890. }
  891. /*******************************************************************
  892. NAME: ValidateIsNotEmpty
  893. SYNOPSIS: Returns TRUE if specified edit control has text in it,
  894. displays warning popup and returns FALSE if empty
  895. ********************************************************************/
  896. BOOL ValidateIsNotEmpty(HWND hwndCtrl,HWND hDlg,SETTINGS * pSetting,DWORD dwValidate)
  897. {
  898. if (pSetting->dwFlags & DF_REQUIRED) {
  899. CHAR szTextData[MAXSTRLEN];
  900. SendMessage(hwndCtrl,WM_GETTEXT,sizeof(szTextData),(LPARAM) szTextData);
  901. if (!lstrlen(szTextData)) {
  902. if (dwValidate == PSC_VALIDATENOISY) {
  903. MsgBoxParam(hDlg,IDS_ENTRYREQUIRED,GETNAMEPTR(pSetting),
  904. MB_ICONINFORMATION,MB_OK);
  905. SetFocus(hwndCtrl);
  906. }
  907. return FALSE;
  908. }
  909. }
  910. return TRUE;
  911. }
  912. /*******************************************************************
  913. NAME: ObjectTypeHasDataOffset
  914. SYNOPSIS: Returns TRUE if specified object type has a data offset
  915. in SETTINGDATA struct (uOffsetData) rather than a value
  916. (uData).
  917. ********************************************************************/
  918. BOOL ObjectTypeHasDataOffset(DWORD dwType)
  919. {
  920. switch (dwType) {
  921. case STYPE_EDITTEXT:
  922. case STYPE_COMBOBOX:
  923. case STYPE_LISTBOX:
  924. return TRUE;
  925. }
  926. return FALSE;
  927. }
  928. VOID InsertComboboxItems(HWND hwndControl,CHAR * pSuggestionList)
  929. {
  930. while (*pSuggestionList) {
  931. SendMessage(hwndControl,CB_ADDSTRING,0,(LPARAM) pSuggestionList);
  932. pSuggestionList += lstrlen(pSuggestionList) + 1;
  933. }
  934. }
  935. /*******************************************************************
  936. NAME: FindComboboxItemData
  937. SYNOPSIS: Returns the index of item in combobox whose item data
  938. is equal to nData. Returns -1 if no items have data
  939. which matches.
  940. ********************************************************************/
  941. int FindComboboxItemData(HWND hwndControl,UINT nData)
  942. {
  943. UINT nIndex;
  944. for (nIndex=0;nIndex<(UINT) SendMessage(hwndControl,CB_GETCOUNT,0,0L);
  945. nIndex++) {
  946. if ((UINT) SendMessage(hwndControl,CB_GETITEMDATA,nIndex,0L) == nData)
  947. return (int) nIndex;
  948. }
  949. return -1;
  950. }