//********************************************************************* //* Microsoft Windows ** //* Copyright(c) Microsoft Corp., 1994 ** //********************************************************************* #include "admincfg.h" #include "settings.h" HFONT hfontDlg=NULL; BOOL SetHwndBkColor(HWND hWnd,COLORREF color); BOOL GetTextSize(HWND hWnd,CHAR * szText,SIZE * pSize); BOOL AdjustWindowToText(HWND hWnd,CHAR * szText,UINT xStart,UINT yStart,UINT yPad, UINT * pnWidth,UINT * pnHeight); int AddControlHwnd(POLICYDLGINFO * pdi,POLICYCTRLINFO * pPolicyCtrlInfo); BOOL SetWindowData(POLICYDLGINFO * pdi,HWND hwndControl,DWORD dwType, UINT uDataIndex,SETTINGS * pSetting); VOID ProcessScrollBar(HWND hWnd,WPARAM wParam); VOID ProcessCommand(HWND hWnd,HWND hwndCtrl); HWND CreateSetting(POLICYDLGINFO * pdi,CHAR * pszClassName,CHAR * pszWindowName, DWORD dwExStyle,DWORD dwStyle,int x,int y,int cx,int cy,DWORD dwType,UINT uIndex, SETTINGS * pSetting); VOID InsertComboboxItems(HWND hwndControl,CHAR *pSuggestionList); BOOL ValidateIsNotEmpty(HWND hwndCtrl,HWND hDlg,SETTINGS * pSetting,DWORD dwValidate); BOOL ObjectTypeHasDataOffset(DWORD dwType); VOID EnsureSettingControlVisible(HWND hDlg,HWND hwndCtrl); int FindComboboxItemData(HWND hwndControl,UINT nData); /******************************************************************* NAME: ClipWndProc SYNOPSIS: Window proc for ClipClass window ********************************************************************/ LRESULT CALLBACK ClipWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { switch (message) { case WM_CREATE: if (!((CREATESTRUCT *) lParam)->lpCreateParams) { // this is the clip window in the dialog box. SetScrollRange(hWnd,SB_VERT,0,0,TRUE); hfontDlg = (HFONT) SendMessage(GetParent(hWnd),WM_GETFONT,0,0L); } else { // this is the container window // store away the dialog box HWND (the grandparent of this // window) because the pointer to instance data we need lives // in the dialog's window data SetWindowLong(hWnd,0,WT_SETTINGS); } break; case WM_USER: { HWND hwndParent = GetParent(hWnd); POLICYDLGINFO * pdi; if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hwndParent, DWLP_USER))) return FALSE; // make a container window that is clipped by this windows if (!(pdi->hwndSettings=CreateWindow("ClipClass",(CHAR *) szNull, WS_CHILD | WS_VISIBLE,0,0,400,400,hWnd,NULL,ghInst, (LPVOID) hWnd))) return FALSE; SetWindowLong(hWnd,0,WT_CLIP); return TRUE; } break; case WM_VSCROLL: if (GetWindowLong(hWnd,0) == WT_CLIP) ProcessScrollBar(hWnd,wParam); else goto defproc; return 0; break; case WM_COMMAND: if (GetWindowLong(hWnd,0) == WT_SETTINGS) ProcessCommand(hWnd,(HWND) lParam); break; case WM_GETDLGCODE: if (GetWindowLong(hWnd,0) == WT_CLIP) { SetWindowLongPtr(GetParent(hWnd),DWLP_MSGRESULT,DLGC_WANTTAB | DLGC_WANTALLKEYS); return DLGC_WANTTAB | DLGC_WANTALLKEYS; } break; case WM_SETFOCUS: // if clip window gains keyboard focus, transfer focus to first // control owned by settings window if (GetWindowLong(hWnd,0) == WT_CLIP) { HWND hwndParent = GetParent(hWnd); POLICYDLGINFO * pdi; INT nIndex; BOOL fForward=TRUE; HWND hwndOK = GetDlgItem(GetParent(hwndParent),IDOK); int iDelta; if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hwndParent, DWLP_USER))) return FALSE; // if OK button lost focus, then we're going backwards // in tab order; otherwise we're going forwards if ( (HWND) wParam == hwndOK) fForward = FALSE; // find the first control that has a data index (e.g. is // not static text) and give it focus if (pdi->nControls) { if (fForward) { // search from start of table forwards nIndex = 0; iDelta = 1; } else { // search from end of table backwards nIndex = pdi->nControls-1; iDelta = -1; } for (;nIndex>=0 && nIndex<(int)pdi->nControls;nIndex += iDelta) { if (pdi->pControlTable[nIndex].uDataIndex != NO_DATA_INDEX && IsWindowEnabled(pdi->pControlTable[nIndex].hwnd)) { SetFocus(pdi->pControlTable[nIndex].hwnd); EnsureSettingControlVisible(hwndParent, pdi->pControlTable[nIndex].hwnd); return FALSE; } } } // only get here if there are no setting windows that can // receive keyboard focus. Give keyboard focus to the // next guy in line. This is the "OK" button, unless we // shift-tabbed to get here from the "OK" button in which // case the tree window is the next guy in line if (fForward) SetFocus(hwndOK); else SetFocus(GetDlgItem(hwndParent,IDD_TVPOLICIES)); return FALSE; } break; default: defproc: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (0); } /******************************************************************* NAME: ProcessCommand SYNOPSIS: WM_COMMAND handler for ClipClass window ********************************************************************/ VOID ProcessCommand(HWND hWnd,HWND hwndCtrl) { // get instance-specific struct from dialog UINT uID = GetWindowLong(hwndCtrl,GWL_ID); POLICYDLGINFO * pdi; if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(GetParent(GetParent(hWnd)), DWLP_USER))) return; if ( (uID >= IDD_SETTINGCTRL) && (uID < IDD_SETTINGCTRL+pdi->nControls)) { POLICYCTRLINFO * pPolicyCtrlInfo= &pdi->pControlTable[uID - IDD_SETTINGCTRL]; switch (pPolicyCtrlInfo->dwType) { case STYPE_CHECKBOX: SendMessage(hwndCtrl,BM_SETCHECK, !(SendMessage(hwndCtrl,BM_GETCHECK,0,0)),0); break; case STYPE_LISTBOX: ShowListbox(hwndCtrl,pdi->hUser,pPolicyCtrlInfo->pSetting, pPolicyCtrlInfo->uDataIndex); break; default: // nothing to do break; } } } /******************************************************************* NAME: CreateSettingsControls SYNOPSIS: Creates controls in settings window NOTES: Looks at a table of SETTINGS structs to determine type of control to create and type-specific information. For some types, more than one control can be created (for instance, edit fields get a static control with the title followed by an edit field control). ENTRY: hDlg - owner dialog hTable - table of SETTINGS structs containing setting control information ********************************************************************/ BOOL CreateSettingsControls(HWND hDlg,SETTINGS * pSetting,BOOL fEnable) { CHAR * pObjectData; POLICYDLGINFO * pdi; UINT xMax=0,yStart=SC_YSPACING,nHeight,nWidth,yMax,xWindowMax; HWND hwndControl,hwndBuddy,hwndParent; RECT rcParent; DWORD dwType, dwStyle; UINT uEnable = (fEnable ? 0 : WS_DISABLED); WINDOWPLACEMENT wp; // get instance-specific struct from dialog if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER))) return FALSE; wp.length = sizeof(wp); if (!GetWindowPlacement(GetDlgItem(hDlg,IDD_TVPOLICIES),&wp)) return FALSE; xWindowMax = wp.rcNormalPosition.right - wp.rcNormalPosition.left; pdi->pCurrentSettings = pSetting; while (pSetting) { pObjectData = GETOBJECTDATAPTR(pSetting); dwType = pSetting->dwType & STYPE_MASK; nWidth = 0; switch (dwType) { case STYPE_TEXT: // create static text control if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC, (CHAR *) (GETNAMEPTR(pSetting)),0,SSTYLE_STATIC | uEnable,0, yStart,0,15,STYPE_TEXT,NO_DATA_INDEX,0))) return FALSE; AdjustWindowToText(hwndControl,(CHAR *) (GETNAMEPTR(pSetting)) ,SC_XSPACING,yStart,0,&nWidth,&nHeight); yStart += nHeight + SC_YSPACING; nWidth += SC_XSPACING; break; case STYPE_CHECKBOX: // create checkbox control if (!(hwndControl = CreateSetting(pdi,(CHAR *) szBUTTON, (CHAR *) (GETNAMEPTR(pSetting)),0,SSTYLE_CHECKBOX | uEnable, 0,yStart,200,nHeight,STYPE_CHECKBOX,pSetting->uDataIndex, pSetting))) return FALSE; nWidth = 20; AdjustWindowToText(hwndControl,(CHAR *) (GETNAMEPTR(pSetting)) ,SC_XSPACING,yStart,0,&nWidth,&nHeight); yStart += nHeight + SC_YSPACING; nWidth += SC_XSPACING; break; case STYPE_EDITTEXT: case STYPE_COMBOBOX: // create static text with setting name if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC, GETNAMEPTR(pSetting),0,SSTYLE_STATIC | uEnable,0,0,0,0, STYPE_TEXT,NO_DATA_INDEX,0))) return FALSE; AdjustWindowToText(hwndControl, GETNAMEPTR(pSetting),SC_XSPACING,yStart,SC_YPAD, &nWidth,&nHeight); nWidth += SC_XSPACING + 5; if (nWidth + SC_EDITWIDTH> xWindowMax) { // if next control will stick out of settings window, // put it on the next line if (nWidth > xMax) xMax = nWidth; yStart += nHeight + SC_YSPACING - SC_YPAD; nWidth = SC_XINDENT; } // create edit field or combo box control if (dwType == STYPE_EDITTEXT) { hwndControl = CreateSetting(pdi,(CHAR *) szEDIT,(CHAR *) szNull, WS_EX_CLIENTEDGE,SSTYLE_EDITTEXT | uEnable,nWidth,yStart,SC_EDITWIDTH,nHeight, STYPE_EDITTEXT,pSetting->uDataIndex,pSetting); } else { dwStyle = SSTYLE_COMBOBOX | uEnable; if (pSetting->dwFlags & DF_NOSORT) { dwStyle &= ~CBS_SORT; } hwndControl = CreateSetting(pdi,(CHAR *) szCOMBOBOX,(CHAR *)szNull, WS_EX_CLIENTEDGE,dwStyle,nWidth,yStart,SC_EDITWIDTH,nHeight*3, STYPE_COMBOBOX,pSetting->uDataIndex,pSetting); } if (!hwndControl) return FALSE; // limit the text length appropriately SendMessage(hwndControl,EM_SETLIMITTEXT, (WPARAM) ((EDITTEXTINFO *) pObjectData)->nMaxLen,0L); if (dwType == STYPE_COMBOBOX && ((POLICYCOMBOBOXINFO *) pObjectData)->uOffsetSuggestions) InsertComboboxItems(hwndControl,(CHAR *) pSetting + ((POLICYCOMBOBOXINFO *) pObjectData)->uOffsetSuggestions); yStart += (UINT) ((float) nHeight*1.3) + SC_YSPACING; nWidth += SC_EDITWIDTH; break; case STYPE_NUMERIC: // create static text for setting if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC, GETNAMEPTR(pSetting),0, SSTYLE_STATIC | uEnable,0,0,0,0,STYPE_TEXT,NO_DATA_INDEX,0))) return FALSE; AdjustWindowToText(hwndControl, GETNAMEPTR(pSetting),SC_XSPACING,yStart,SC_YPAD, &nWidth,&nHeight); nWidth += SC_XSPACING + 5; // create edit field if (!(hwndBuddy = CreateSetting(pdi,(CHAR *) szEDIT, (CHAR *) szNull,WS_EX_CLIENTEDGE,SSTYLE_EDITTEXT | uEnable,nWidth,yStart,SC_UPDOWNWIDTH, nHeight,STYPE_NUMERIC,pSetting->uDataIndex,pSetting))) return FALSE; // SendMessage(hwndBuddy,EM_LIMITTEXT,4,0); nWidth += SC_UPDOWNWIDTH + SC_XLEADING; // create spin (up-down) control if specifed if (((NUMERICINFO *)pObjectData)->uSpinIncrement) { UDACCEL udAccel = {0,0}; UINT nMax,nMin; if (!(hwndControl = CreateSetting(pdi,(CHAR *) szUPDOWN, (CHAR *) szNull,0,SSTYLE_UPDOWN | UDS_SETBUDDYINT | uEnable,nWidth,yStart,SC_UPDOWNWIDTH2, nHeight,STYPE_TEXT,NO_DATA_INDEX,0))) return FALSE; nWidth += SC_UPDOWNWIDTH2; nMax = ((NUMERICINFO *) pObjectData)->uMaxValue; nMin = ((NUMERICINFO *) pObjectData)->uMinValue; udAccel.nInc = ((NUMERICINFO *) pObjectData)->uSpinIncrement; SendMessage(hwndControl,UDM_SETBUDDY,(WPARAM) hwndBuddy,0L); SendMessage(hwndControl,UDM_SETRANGE,0,MAKELONG(nMax,nMin)); SendMessage(hwndControl,UDM_SETACCEL,1,(LPARAM) &udAccel); } yStart += nHeight + SC_YSPACING; break; case STYPE_DROPDOWNLIST: // create text description if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC, GETNAMEPTR(pSetting),0, SSTYLE_STATIC | uEnable,0,0,0,0,STYPE_TEXT,NO_DATA_INDEX,0))) return FALSE; AdjustWindowToText(hwndControl, GETNAMEPTR(pSetting),SC_XSPACING,yStart,SC_YPAD,&nWidth,&nHeight); nWidth += SC_XLEADING; if (nWidth + SC_EDITWIDTH> xWindowMax) { // if next control will stick out of settings window, // put it on the next line if (nWidth > xMax) xMax = nWidth; yStart += nHeight + SC_YSPACING - SC_YPAD; nWidth = SC_XINDENT; } dwStyle = SSTYLE_DROPDOWNLIST | uEnable; if (pSetting->dwFlags & DF_NOSORT) { dwStyle &= ~CBS_SORT; } // create drop down listbox hwndControl = CreateSetting(pdi,(CHAR *) szCOMBOBOX,(CHAR *) szNull, WS_EX_CLIENTEDGE,dwStyle,nWidth,yStart,SC_EDITWIDTH,nHeight*3, STYPE_DROPDOWNLIST,pSetting->uDataIndex,pSetting); if (!hwndControl) return FALSE; nWidth += SC_EDITWIDTH; { // insert dropdown list items into control UINT uOffset = pSetting->uOffsetObjectData,nIndex=0; DROPDOWNINFO * pddi; int iSel; while (uOffset) { pddi = (DROPDOWNINFO *) ( (CHAR *) pSetting + uOffset); iSel=(int)SendMessage(hwndControl,CB_ADDSTRING,0,(LPARAM) ((CHAR *) pSetting + pddi->uOffsetItemName)); if (iSel<0) return FALSE; SendMessage(hwndControl,CB_SETITEMDATA,iSel,nIndex); nIndex++; uOffset = pddi->uOffsetNextDropdowninfo; } } yStart += (UINT) ((float) nHeight*1.3) + 1; break; case STYPE_LISTBOX: // create static text with description if (!(hwndControl = CreateSetting(pdi,(CHAR *) szSTATIC, GETNAMEPTR(pSetting),0, SSTYLE_STATIC | uEnable,0,0,0,0,STYPE_TEXT,NO_DATA_INDEX,0))) return FALSE; AdjustWindowToText(hwndControl,GETNAMEPTR(pSetting),SC_XSPACING,yStart, SC_YPAD,&nWidth,&nHeight); nWidth += SC_XLEADING; if (nWidth + LISTBOX_BTN_WIDTH> xWindowMax) { // if next control will stick out of settings window, // put it on the next line if (nWidth > xMax) xMax = nWidth; yStart += nHeight + SC_YSPACING - SC_YPAD; nWidth = SC_XINDENT; } // create pushbutton to show listbox contents hwndControl = CreateSetting(pdi,(CHAR *) szBUTTON,LoadSz(IDS_LISTBOX_SHOW, szSmallBuf,sizeof(szSmallBuf)),0, SSTYLE_LBBUTTON | uEnable,nWidth+5,yStart, LISTBOX_BTN_WIDTH,nHeight,STYPE_LISTBOX, pSetting->uDataIndex,pSetting); if (!hwndControl) return FALSE; nWidth += LISTBOX_BTN_WIDTH + SC_XLEADING; yStart += nHeight+1; } if (nWidth > xMax) xMax = nWidth; pSetting = (SETTINGS *) pSetting->pNext; } yMax = yStart - 1; SetWindowPos(pdi->hwndSettings,NULL,0,0,xMax,yMax,SWP_NOZORDER); hwndParent = GetParent(pdi->hwndSettings); GetClientRect(hwndParent,&rcParent); if (yMax > (UINT) rcParent.bottom-rcParent.top) { SetScrollRange(hwndParent,SB_VERT,0,100,TRUE); SetScrollPos(hwndParent,SB_VERT,0,TRUE); } else SetScrollRange(hwndParent,SB_VERT,0,0,TRUE); return TRUE; } /******************************************************************* NAME: CreateSettings SYNOPSIS: Creates a control and add it to the table of settings controls ********************************************************************/ HWND CreateSetting(POLICYDLGINFO * pdi,CHAR * pszClassName,CHAR * pszWindowName, DWORD dwExStyle,DWORD dwStyle,int x,int y,int cx,int cy,DWORD dwType,UINT uIndex, SETTINGS * pSetting) { HWND hwndControl; if (!(hwndControl = CreateWindowEx(WS_EX_NOPARENTNOTIFY | dwExStyle, pszClassName,pszWindowName,dwStyle,x,y,cx,cy,pdi->hwndSettings,NULL, ghInst,NULL))) return NULL; if (!SetWindowData(pdi,hwndControl,dwType,uIndex,pSetting)) { DestroyWindow(hwndControl); return NULL; } SendMessage(hwndControl,WM_SETFONT,(WPARAM) hfontDlg,MAKELPARAM(TRUE,0)); return hwndControl; } /******************************************************************* NAME: ProcessSettingsControls SYNOPSIS: For each settings control, takes currently entered data from control and saves data in user's buffer. ********************************************************************/ BOOL ProcessSettingsControls(HWND hDlg,DWORD dwValidate) { UINT nIndex; USERDATA * pUserData; POLICYDLGINFO * pdi; SETTINGS * pSetting; CHAR szTextData[MAXSTRLEN]; BOOL fRet=TRUE; // get instance-specific struct from dialog // from instance-specific struct, get user's data buffer and setting info if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)) || !(pUserData = (USERDATA *) GlobalLock(pdi->hUser))) { MsgBox(hDlg,IDS_ErrOUTOFMEMORY,MB_ICONEXCLAMATION,MB_OK); fRet=FALSE; goto cleanup; } pSetting = pdi->pCurrentSettings; for (nIndex=0;nIndexnControls;nIndex++) { pSetting = pdi->pControlTable[nIndex].pSetting; if (pdi->pControlTable[nIndex].uDataIndex != NO_DATA_INDEX) { switch (pdi->pControlTable[nIndex].dwType) { case STYPE_CHECKBOX: pUserData->SettingData[pdi->pControlTable[nIndex].uDataIndex].uData = (UINT)SendMessage(pdi->pControlTable[nIndex].hwnd,BM_GETCHECK, 0,0L); break; case STYPE_EDITTEXT: case STYPE_COMBOBOX: SendMessage(pdi->pControlTable[nIndex].hwnd,WM_GETTEXT, sizeof(szTextData),(LPARAM) szTextData); // don't allow empty text if this is required field if (dwValidate && !ValidateIsNotEmpty(pdi->pControlTable[nIndex].hwnd, hDlg,pSetting,dwValidate)) { fRet = FALSE; goto cleanup; } // add this text to buffer. Need to unlock handle before // call and relock afterwards, as buffer may resize and move GlobalUnlock(pdi->hUser); SetVariableLengthData(pdi->hUser,pdi->pControlTable[nIndex].uDataIndex, szTextData,lstrlen(szTextData)+1); if (!(pUserData = (USERDATA *) GlobalLock(pdi->hUser))) return FALSE; break; case STYPE_NUMERIC: { BOOL fTranslated; UINT uRet; uRet=GetDlgItemInt(pdi->hwndSettings,nIndex+IDD_SETTINGCTRL, &fTranslated,FALSE); if (dwValidate) { NUMERICINFO * pNumericInfo; // don't allow empty text if this is required field if (!ValidateIsNotEmpty(pdi->pControlTable[nIndex].hwnd, hDlg,pSetting,dwValidate)) { fRet = FALSE; goto cleanup; } // check for non-numeric in edit ctrl if (!fTranslated) { if (dwValidate == PSC_VALIDATENOISY) { MsgBoxParam(hDlg,IDS_INVALIDNUM, GETNAMEPTR(pSetting),MB_ICONINFORMATION, MB_OK); SetFocus(pdi->pControlTable[nIndex].hwnd); SendMessage(pdi->pControlTable[nIndex].hwnd, EM_SETSEL,0,-1); } fRet = FALSE; goto cleanup; } // validate for max and min pNumericInfo = (NUMERICINFO *) GETOBJECTDATAPTR(pSetting); if (uRet < pNumericInfo->uMinValue) uRet = pNumericInfo->uMinValue; if (uRet > pNumericInfo->uMaxValue) uRet = pNumericInfo->uMaxValue; } pUserData->SettingData[pdi->pControlTable[nIndex].uDataIndex].uData = (fTranslated ? uRet : NO_VALUE); } break; case STYPE_DROPDOWNLIST: { int iSel; iSel = (int)SendMessage(pdi->pControlTable[nIndex].hwnd, CB_GETCURSEL,0,0L); iSel = (int)SendMessage(pdi->pControlTable[nIndex].hwnd, CB_GETITEMDATA,iSel,0L); if (dwValidate && iSel < 0) { if (dwValidate == PSC_VALIDATENOISY) { MsgBoxParam(hDlg,IDS_ENTRYREQUIRED,GETNAMEPTR(pSetting), MB_ICONINFORMATION,MB_OK); SetFocus(pdi->pControlTable[nIndex].hwnd); } return FALSE; } pUserData->SettingData[pdi->pControlTable[nIndex].uDataIndex].uData = (UINT) iSel; } break; } } } cleanup: GlobalUnlock(pdi->hUser); return fRet; } /******************************************************************* NAME: FreeSettingsControls SYNOPSIS: Frees all settings controls ********************************************************************/ VOID FreeSettingsControls(HWND hDlg) { UINT nIndex; POLICYDLGINFO * pdi; // get instance-specific struct from dialog if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER))) return; for (nIndex=0;nIndexnControls;nIndex++) { DestroyWindow(pdi->pControlTable[nIndex].hwnd); } pdi->pCurrentSettings = NULL; pdi->nControls = 0; SetScrollRange(pdi->hwndSettings,SB_VERT,0,0,TRUE); } /******************************************************************* NAME: SetVariableLengthData SYNOPSIS: Adds variable length data to user's data buffer, reallocs if necessary. CAVEATS: Since this call may realloc the user's data buffer (hUser), it must always be unlocked before calling. NOTES: For variable-length data (edit-text, listboxes), the SettingData struct for a particular setting contains an offset to the actual data. (The offset is from the beginning of the USERDATA struct.) If this offset is zero, it means no data has been entered and there is no allocation for that setting yet. The offset points to a STRDATA struct, which contains a size entry followed by data. If an allocation currently exists but is insufficient for new data (e.g., user previously entered a string value, now goes back and changes it and the new one is longer), then the buffer is realloced, the end of the buffer is memcopy'd to enlarge the region of allocation, and offsets into the section that moved are fixed up. ********************************************************************/ #define SHRINK_THRESHOLD 64 BOOL SetVariableLengthData(HGLOBAL hUser,UINT nDataIndex,TCHAR * pchData, DWORD cbData) { USERDATA * pUserData; STRDATA * pStrData = NULL; DWORD dwCurrentSize,dwStrSize; UINT uOffsetData; if (!(pUserData = (USERDATA *) GlobalLock(hUser))) return FALSE; uOffsetData = pUserData->SettingData[nDataIndex].uOffsetData; // if no existing data and no new data, nothing to do if ((!uOffsetData || uOffsetData == INIT_WITH_DEFAULT) && cbData<1) { GlobalUnlock(hUser); return TRUE; } if (!uOffsetData || uOffsetData == INIT_WITH_DEFAULT) { // no data for this setting yet, allocate extra room and stick // this data at the end of the buffer pUserData->SettingData[nDataIndex].uOffsetData = pUserData->dwSize; dwStrSize = 0; } else { // there's already data for this setting, make sure there's enough // room to put the new data in the buffer pStrData = (STRDATA *) ( (LPBYTE) pUserData + pUserData->SettingData[nDataIndex].uOffsetData); dwStrSize = pStrData->dwSize; } // if the current string buffer is not large enough, or is larger than // we need by a lot (SHRINK_THRESHOLD), resize that buffer area to just // fit the current data (rounded up to DWORD alignment). // add sizeof(DWORD) to make room for length header in STRDATA struct if ((dwStrSize < cbData + sizeof(DWORD)) || (dwStrSize > cbData+sizeof(DWORD)+SHRINK_THRESHOLD)) { int nAdditional = RoundToDWord(cbData - (dwStrSize - sizeof(DWORD))); DWORD dwOldSize; dwOldSize = dwCurrentSize = pUserData->dwSize; // resize the buffer to make more room if (!(pUserData = (USERDATA *) ResizeBuffer((CHAR *) pUserData, hUser,dwCurrentSize+nAdditional,&dwCurrentSize))) return FALSE; pUserData->dwSize = dwCurrentSize; pStrData = (STRDATA *) ( (CHAR *) pUserData + pUserData->SettingData[nDataIndex].uOffsetData); pStrData->dwSize = dwStrSize; // we may be expanding the middle of the buffer-- copy the rest of // the buffer "down" to make room { TCHAR * pchFrom,* pchTo; UINT cbMove; UINT uOffsetMoved; UINT nIndex; pchFrom = (TCHAR *) pStrData + pStrData->dwSize; pchTo = pchFrom + nAdditional; uOffsetMoved = (UINT)(pchFrom - (CHAR *) pUserData); cbMove = dwOldSize - uOffsetMoved; pStrData->dwSize += nAdditional; if (cbMove) { memmove(pchTo,pchFrom,cbMove * sizeof(TCHAR)); // now we've moved a chunk of the buffer, need to fix up // data offsets that point into the chunk that moved. for (nIndex = 0;nIndexnSettings;nIndex++) { if (ObjectTypeHasDataOffset(pUserData->SettingData[nIndex].dwType & STYPE_MASK)) { if (pUserData->SettingData[nIndex].uOffsetData != INIT_WITH_DEFAULT) { if (pUserData->SettingData[nIndex].uOffsetData >= uOffsetMoved) { pUserData->SettingData[nIndex].uOffsetData += nAdditional; } } } } } } } if (pStrData) memcpy(pStrData->szData,pchData, cbData * sizeof(TCHAR)); GlobalUnlock(hUser); return TRUE; } /******************************************************************* NAME: EnableSettingsControls SYNOPSIS: Enables or disables all setting controls NOTES: If disabling, "clears" control as appropriate to the control type (e.g. for edit fields, clears text); if enabling, restores control contents from user's data buffer ********************************************************************/ BOOL EnableSettingsControls(HWND hDlg,BOOL fEnable) { UINT nIndex; USERDATA * pUserData; UINT nDataIndex; HWND hwndControl; POLICYDLGINFO * pdi; SETTINGS * pSetting; // get instance-specific struct from dialog if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER)) || !pdi->pCurrentSettings) return FALSE; if (!(pUserData = (USERDATA *) GlobalLock(pdi->hUser))) return FALSE; pSetting = pdi->pCurrentSettings; for (nIndex=0;nIndexnControls;nIndex++) { nDataIndex = pdi->pControlTable[nIndex].uDataIndex; hwndControl = pdi->pControlTable[nIndex].hwnd; switch (pdi->pControlTable[nIndex].dwType) { case STYPE_CHECKBOX: SendMessage(hwndControl,BM_SETCHECK,(fEnable ? pUserData->SettingData[nDataIndex].uData : 0),0L); break; case STYPE_EDITTEXT: case STYPE_COMBOBOX: // for edit fields, clear text from edit field if disabled; // replace text if enabled // if this is the first time this setting is enabled for this // user and there is a default value to use, set that in // the user's data buffer if (fEnable && pUserData->SettingData[nDataIndex].uOffsetData == INIT_WITH_DEFAULT) { CHAR * pszDefaultText; pszDefaultText = (CHAR *) pdi->pControlTable[nIndex].pSetting + ((EDITTEXTINFO *) GETOBJECTDATAPTR(pdi->pControlTable[nIndex]. pSetting))->uOffsetDefText; // add this text to buffer. Need to unlock handle before // call and relock afterwards, as buffer may resize and move GlobalUnlock(pdi->hUser); SetVariableLengthData(pdi->hUser,pdi->pControlTable[nIndex].uDataIndex, pszDefaultText,lstrlen(pszDefaultText)+1); if (!(pUserData = (USERDATA *) GlobalLock(pdi->hUser))) return FALSE; } if (fEnable) { // set the text appropriately if (pUserData->SettingData[nDataIndex].uOffsetData) { CHAR * pchText; pchText = ((STRDATA *) ((CHAR *) pUserData + pUserData->SettingData[nDataIndex].uOffsetData)) ->szData; SendMessage(hwndControl,WM_SETTEXT,0,(LPARAM) pchText); } } else { // clear the text SendMessage(hwndControl,WM_SETTEXT,0,(LPARAM) szNull); } break; case STYPE_NUMERIC: if (fEnable) { UINT uData = pUserData->SettingData[nDataIndex].uData; if (uData != NO_VALUE) SetDlgItemInt(pdi->hwndSettings,nIndex+IDD_SETTINGCTRL, uData,FALSE); } else { // clear the number SendMessage(hwndControl,WM_SETTEXT,0,(LPARAM) szNull); } break; case STYPE_DROPDOWNLIST: if (fEnable) { SendMessage(hwndControl,CB_SETCURSEL, FindComboboxItemData(hwndControl,pUserData->SettingData[nDataIndex].uData), 0L); } else { SendMessage(hwndControl,CB_SETCURSEL,(UINT) -1,0L); } break; case STYPE_LISTBOX: // nothing to do break; } EnableWindow(pdi->pControlTable[nIndex].hwnd,fEnable); } GlobalUnlock(pdi->hUser); return TRUE; } BOOL SetWindowData(POLICYDLGINFO * pdi,HWND hwndControl,DWORD dwType, UINT uDataIndex,SETTINGS * pSetting) { POLICYCTRLINFO PolicyCtrlInfo; int iCtrl; PolicyCtrlInfo.hwnd = hwndControl; PolicyCtrlInfo.dwType = dwType; PolicyCtrlInfo.uDataIndex = uDataIndex; PolicyCtrlInfo.pSetting = pSetting; iCtrl = AddControlHwnd(pdi,&PolicyCtrlInfo); if (iCtrl < 0) return FALSE; SetWindowLong(hwndControl,GWL_ID,iCtrl + IDD_SETTINGCTRL); return TRUE; } BOOL AdjustWindowToText(HWND hWnd,CHAR * szText,UINT xStart,UINT yStart, UINT yPad,UINT * pnWidth,UINT * pnHeight) { SIZE size; if (GetTextSize(hWnd,szText,&size)) { *pnHeight =size.cy + yPad; *pnWidth += size.cx; SetWindowPos(hWnd,NULL,xStart,yStart,*pnWidth,*pnHeight,SWP_NOZORDER); } return FALSE; } BOOL SetHwndBkColor(HWND hWnd,COLORREF color) { HDC hDC; BOOL fRet; if (!(hDC = GetDC(hWnd))) return FALSE; fRet=(SetBkColor(hDC,color) != CLR_INVALID); ReleaseDC(hWnd,hDC); return fRet; } BOOL GetTextSize(HWND hWnd,CHAR * szText,SIZE * pSize) { HDC hDC; BOOL fRet; if (!(hDC = GetDC(hWnd))) return FALSE; SelectObject(hDC, hfontDlg); fRet=GetTextExtentPoint(hDC,szText,lstrlen(szText),pSize); ReleaseDC(hWnd,hDC); return fRet; } int AddControlHwnd(POLICYDLGINFO * pdi,POLICYCTRLINFO * pPolicyCtrlInfo) { int iRet; DWORD dwNeeded; POLICYCTRLINFO * pTemp; // grow table if necessary dwNeeded = (pdi->nControls+1) * sizeof(POLICYCTRLINFO); if (dwNeeded > pdi->dwControlTableSize) { pTemp = (POLICYCTRLINFO *) GlobalReAlloc(pdi->pControlTable, dwNeeded,GMEM_ZEROINIT | GMEM_MOVEABLE); if (!pTemp) return (-1); pdi->pControlTable = pTemp; pdi->dwControlTableSize = dwNeeded; } pdi->pControlTable[pdi->nControls] = *pPolicyCtrlInfo; iRet = (int) pdi->nControls; (pdi->nControls)++; return iRet; } // scrolls the control window into view if it's not visible VOID EnsureSettingControlVisible(HWND hDlg,HWND hwndCtrl) { // get the clip window, which owns the scroll bar HWND hwndClip = GetDlgItem(hDlg,IDD_TVSETTINGS); POLICYDLGINFO * pdi; UINT nPos = GetScrollPos(hwndClip,SB_VERT),ySettingWindowSize,yClipWindowSize; UINT nExtra; int iMin,iMax=0; WINDOWPLACEMENT wp; RECT rcCtrl; if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(hDlg,DWLP_USER))) return; // find the scroll range GetScrollRange(hwndClip,SB_VERT,&iMin,&iMax); if (!iMax) // no scroll bar, nothing to do return; // find the y size of the settings window that contains the settings controls // (this is clipped by the clip window in the dialog, scroll bar moves the // setting window up and down behind the clip window) wp.length = sizeof(wp); if (!GetWindowPlacement(pdi->hwndSettings,&wp)) return; // unlikely to fail, but just bag out if it does rather than do something wacky ySettingWindowSize=wp.rcNormalPosition.bottom-wp.rcNormalPosition.top; // find y size of clip window if (!GetWindowPlacement(hwndClip,&wp)) return; // unlikely to fail, but just bag out if it does rather than do something wacky yClipWindowSize=wp.rcNormalPosition.bottom-wp.rcNormalPosition.top; nExtra = ySettingWindowSize - yClipWindowSize; // if setting window is smaller than clip window, there should be no // scroll bar so we should never get here. Check just in case though... if (ySettingWindowSize < yClipWindowSize) return; // get y position of control to be made visible if (!GetWindowPlacement(hwndCtrl,&wp)) return; rcCtrl = wp.rcNormalPosition; rcCtrl.bottom = min ((int) ySettingWindowSize,rcCtrl.bottom + SC_YPAD); rcCtrl.top = max ((int) 0,rcCtrl.top - SC_YPAD); // if bottom of control is out of view, scroll the settings window up if ((float) rcCtrl.bottom > (float) (yClipWindowSize + ( (float) nPos/(float)iMax) * (ySettingWindowSize - yClipWindowSize))) { UINT nNewPos = (UINT) ( ((float) (nExtra - (ySettingWindowSize - rcCtrl.bottom)) / (float) nExtra) * iMax); SetScrollPos(hwndClip,SB_VERT,nNewPos,TRUE); ProcessScrollBar(hwndClip,MAKELPARAM(SB_THUMBTRACK,nNewPos)); return; } // if top of control is out of view, scroll the settings window down if ((float) rcCtrl.top < (float) ( (float) nPos/(float)iMax) * nExtra) { UINT nNewPos = (UINT) ( ((float) rcCtrl.top / (float) nExtra) * iMax); SetScrollPos(hwndClip,SB_VERT,nNewPos,TRUE); ProcessScrollBar(hwndClip,MAKELPARAM(SB_THUMBTRACK,nNewPos)); return; } } VOID ProcessScrollBar(HWND hWnd,WPARAM wParam) { UINT nPos = GetScrollPos(hWnd,SB_VERT); RECT rcParent,rcChild; POLICYDLGINFO * pdi; // get instance-specific struct from dialog if (!(pdi = (POLICYDLGINFO *) GetWindowLongPtr(GetParent(hWnd),DWLP_USER))) return; switch (LOWORD(wParam)) { case SB_THUMBPOSITION: case SB_THUMBTRACK: nPos = HIWORD(wParam); break; case SB_TOP: nPos = 0; break; case SB_BOTTOM: nPos = 100; break; case SB_LINEUP: nPos -= 10; break; case SB_LINEDOWN: nPos += 10; break; case SB_PAGEUP: nPos -= 30; break; case SB_PAGEDOWN: nPos += 30; break; } // wsprintf(szDebugOut,"nPos: %lu\r\n",nPos); // OutputDebugString(szDebugOut); SetScrollPos(hWnd,SB_VERT,nPos,TRUE); GetClientRect(hWnd,&rcParent); GetClientRect(pdi->hwndSettings,&rcChild); SetWindowPos(pdi->hwndSettings,NULL,0,-(int) ((( (float) (rcChild.bottom-rcChild.top)-(rcParent.bottom-rcParent.top)) /100.0) * (float) nPos),rcChild.right,rcChild.bottom,SWP_NOZORDER | SWP_NOSIZE); } /******************************************************************* NAME: ValidateIsNotEmpty SYNOPSIS: Returns TRUE if specified edit control has text in it, displays warning popup and returns FALSE if empty ********************************************************************/ BOOL ValidateIsNotEmpty(HWND hwndCtrl,HWND hDlg,SETTINGS * pSetting,DWORD dwValidate) { if (pSetting->dwFlags & DF_REQUIRED) { CHAR szTextData[MAXSTRLEN]; SendMessage(hwndCtrl,WM_GETTEXT,sizeof(szTextData),(LPARAM) szTextData); if (!lstrlen(szTextData)) { if (dwValidate == PSC_VALIDATENOISY) { MsgBoxParam(hDlg,IDS_ENTRYREQUIRED,GETNAMEPTR(pSetting), MB_ICONINFORMATION,MB_OK); SetFocus(hwndCtrl); } return FALSE; } } return TRUE; } /******************************************************************* NAME: ObjectTypeHasDataOffset SYNOPSIS: Returns TRUE if specified object type has a data offset in SETTINGDATA struct (uOffsetData) rather than a value (uData). ********************************************************************/ BOOL ObjectTypeHasDataOffset(DWORD dwType) { switch (dwType) { case STYPE_EDITTEXT: case STYPE_COMBOBOX: case STYPE_LISTBOX: return TRUE; } return FALSE; } VOID InsertComboboxItems(HWND hwndControl,CHAR * pSuggestionList) { while (*pSuggestionList) { SendMessage(hwndControl,CB_ADDSTRING,0,(LPARAM) pSuggestionList); pSuggestionList += lstrlen(pSuggestionList) + 1; } } /******************************************************************* NAME: FindComboboxItemData SYNOPSIS: Returns the index of item in combobox whose item data is equal to nData. Returns -1 if no items have data which matches. ********************************************************************/ int FindComboboxItemData(HWND hwndControl,UINT nData) { UINT nIndex; for (nIndex=0;nIndex<(UINT) SendMessage(hwndControl,CB_GETCOUNT,0,0L); nIndex++) { if ((UINT) SendMessage(hwndControl,CB_GETITEMDATA,nIndex,0L) == nData) return (int) nIndex; } return -1; }