/* Copyright (C) 1991, Microsoft Corporation, all rights reserved ipaddr.c - TCP/IP Address custom control November 9, 1992 Greg Strange February 11, 1997 - Marco Chierotti (extend to IPv6 and TTL for DNS snapin) */ #pragma hdrstop #include #include #include "maskctrl.h" // Global IPAddress definitions #define BUFFER_LEN 128 // length for static buffers #define SPACE TEXT(' ') #define BACK_SPACE 8 #define HEAD_ROOM 1 // space at top of control #define LEAD_ROOM 0 // space at front of control // All the information unique to one control is stuffed in one of these // structures in global memory and the handle to the memory is stored in the // Windows extra space. typedef struct tagFIELD { HANDLE hWnd; FARPROC lpfnWndProc; DWORD dwLow; // lowest allowed value for this field. DWORD dwHigh; // Highest allowed value for this field. UINT nChars; // # of chars for the field UINT uiWidth; // width of the field in pixels } FIELD; /* class info struct for different types of control */ typedef struct tagCLS_INFO { TCHAR chFiller; //The character that is displayed between address fields. LPCTSTR lpszFiller; UINT nNumFields; // number of fields in the control void (*lpfnInit)(int, FIELD*); // function to initialize the FIELD structs for a given field BOOL (*lpfnValChar)(TCHAR); // function for field validation DWORD (*lpfnStringToNumber)(LPTSTR, int); // function to change a field string into a number void (*lpfnNumberToString)(LPTSTR, DWORD); // function to change a number into a field string UINT (*lpfnMaxCharWidth)(HDC hDC); // function to get the max width of a char in edit fields } CLS_INFO; typedef struct tagCONTROL { HWND hwndParent; UINT uiMaxCharWidth; UINT uiFillerWidth; BOOL fEnabled; BOOL fPainted; BOOL bControlInFocus; // TRUE if the control is already in focus, dont't send another focus command BOOL bCancelParentNotify; // Don't allow the edit controls to notify parent if TRUE BOOL fInMessageBox; // Set when a message box is displayed so that // we don't send a EN_KILLFOCUS message when // we receive the EN_KILLFOCUS message for the // current field. FIELD* ChildrenArr; // array of structs with info about each field CLS_INFO* pClsInfo; // struct with info about the constrol type int (*lpfnAlert)(HWND, DWORD, DWORD, DWORD); } CONTROL; // The following macros extract and store the CONTROL structure for a control. #define IPADDRESS_EXTRA sizeof(DWORD) #define GET_CONTROL_HANDLE(hWnd) ((HGLOBAL)(GetWindowLongPtr((hWnd), GWLP_USERDATA))) #define SAVE_CONTROL_HANDLE(hWnd,x) (SetWindowLongPtr((hWnd), GWLP_USERDATA, (LONG_PTR)x)) /* internal IPAddress function prototypes */ LRESULT FAR PASCAL IPv4WndFn(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam ); LRESULT FAR PASCAL IPv6WndFn(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam ); LRESULT FAR PASCAL TTLWndFn(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam ); LRESULT FAR PASCAL IPAddressFieldProc(HWND, UINT, WPARAM, LPARAM); BOOL SwitchFields(CONTROL FAR *, LONG_PTR, LONG_PTR, UINT, UINT); void EnterField(FIELD FAR *, UINT, UINT); BOOL ExitField(CONTROL FAR *, LONG_PTR); DWORD GetFieldValue(FIELD*, CLS_INFO*); LOGFONT logfont; BOOL g_bFontInitialized = FALSE; void SetDefaultFont(LPCWSTR lpFontName, int nFontSize) { HDC dc; logfont.lfWidth = 0; logfont.lfEscapement = 0; logfont.lfOrientation = 0; logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; logfont.lfQuality = DEFAULT_QUALITY; logfont.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS; logfont.lfUnderline = 0; logfont.lfStrikeOut = 0; logfont.lfItalic = 0; logfont.lfWeight = FW_NORMAL; if (g_bFontInitialized) return; // do it only once dc = GetDC(NULL); if (dc != NULL) { logfont.lfHeight = -(nFontSize*GetDeviceCaps(dc,LOGPIXELSY)/72); logfont.lfCharSet = ANSI_CHARSET; lstrcpy( logfont.lfFaceName, lpFontName); ReleaseDC(NULL, dc); } g_bFontInitialized = TRUE; } ///////////////////////////////////////////////////////////////////////////// BOOL PASCAL DNS_ControlsInitialize(HANDLE hInstance, LPCWSTR lpFontName, int nFontSize) { return DNS_ControlInit(hInstance, DNS_IP_V4_ADDRESS_CLASS, IPv4WndFn, lpFontName, nFontSize) && DNS_ControlInit(hInstance, DNS_IP_V6_ADDRESS_CLASS, IPv6WndFn, lpFontName, nFontSize) && DNS_ControlInit(hInstance, DNS_TTL_CLASS, TTLWndFn, lpFontName, nFontSize); } /* DNS_ControlInit() - DNS custom controls initialization call hInstance = application instance return TRUE on success, FALSE on failure. This function does all the one time initialization of custom controls. */ BOOL PASCAL DNS_ControlInit(HANDLE hInstance, LPCTSTR lpszClassName, WNDPROC lpfnWndProc, LPCWSTR lpFontName, int nFontSize) { LPWNDCLASS lpClassStruct; BOOL bRes; bRes = FALSE; /* allocate memory for class structure */ lpClassStruct = (LPWNDCLASS)GlobalAlloc(GPTR, (DWORD)sizeof(WNDCLASS)); if (lpClassStruct) { /* define class attributes */ lpClassStruct->lpszClassName = lpszClassName; lpClassStruct->hCursor = LoadCursor(NULL,IDC_IBEAM); lpClassStruct->lpszMenuName = (LPCTSTR)NULL; lpClassStruct->style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;//|CS_GLOBALCLASS; lpClassStruct->lpfnWndProc = lpfnWndProc; lpClassStruct->hInstance = hInstance; lpClassStruct->hIcon = NULL; lpClassStruct->cbWndExtra = IPADDRESS_EXTRA; lpClassStruct->hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 ); /* register window class */ bRes = RegisterClass(lpClassStruct); if (!bRes) { DWORD dwError = GetLastError(); if (HRESULT_CODE(dwError) == ERROR_CLASS_ALREADY_EXISTS) bRes = TRUE; } GlobalFree(lpClassStruct); } SetDefaultFont(lpFontName, nFontSize); return bRes; } /* IPAddressWndFn() - Main window function for an IPAddress control. call hWnd handle to IPAddress window wMsg message number wParam word parameter lParam long parameter */ void FormatIPAddress(LPTSTR pszString, DWORD* dwValue) { static TCHAR szBuf[3+1]; // 3 characters per octet + 1 for the '/0' int nField, nPos; BOOL fFinish = FALSE; dwValue[0] = 0; dwValue[1] = 0; dwValue[2] = 0; dwValue[3] = 0; if (pszString[0] == 0) return; for( nField = 0, nPos = 0; !fFinish; nPos++) { if (( pszString[nPos]TEXT('9'))) { // not a number nField++; fFinish = (nField == 4); } else { dwValue[nField] *= 10; dwValue[nField] += (pszString[nPos]-TEXT('0')); } } } LRESULT FAR PASCAL IPAddressWndFnEx(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam, CLS_INFO* pClsInfo ) { LRESULT lResult; CONTROL *pControl; UINT i; lResult = TRUE; switch( wMsg ) { // use empty string (not NULL) to set to blank case WM_SETTEXT: { /* static TCHAR szBuf[CHARS_PER_FIELD+1]; DWORD dwValue[4]; LPTSTR pszString = (LPTSTR)lParam; FormatIPAddress(pszString, &dwValue[0]); pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); pControl->bCancelParentNotify = TRUE; for (i = 0; i < pClsInfo->nNumFields; ++i) { if (pszString[0] == 0) { szBuf[0] = 0; } else { wsprintf(szBuf, TEXT("%d"), dwValue[i]); } SendMessage(pControl->ChildrenArr[i].hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) szBuf); } pControl->bCancelParentNotify = FALSE; SendMessage(pControl->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd); */ } break; case WM_GETTEXTLENGTH: case WM_GETTEXT: { /* int iFieldValue; int srcPos, desPos; DWORD dwValue[4]; TCHAR pszResult[30]; TCHAR *pszDest = (TCHAR *)lParam; pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); lResult = 0; dwValue[0] = 0; dwValue[1] = 0; dwValue[2] = 0; dwValue[3] = 0; for (i = 0; i < pClsInfo->nNumFields; ++i) { iFieldValue = GetFieldValue(&(pControl->ChildrenArr[i])); if (iFieldValue == -1) iFieldValue = 0; else ++lResult; dwValue[i] = iFieldValue; } wsprintf( pszResult, TEXT("%d.%d.%d.%d"), dwValue[0], dwValue[1], dwValue[2], dwValue[3] ); if ( wMsg == WM_GETTEXTLENGTH ) { lResult = lstrlen( pszResult ); } else { for ( srcPos=0, desPos=0; (srcPos+1<(INT)wParam) && (pszResult[srcPos]!=TEXT('\0')); ) { pszDest[desPos++] = pszResult[srcPos++]; } pszDest[desPos]=TEXT('\0'); lResult = desPos; } */ } break; case WM_GETDLGCODE : lResult = DLGC_WANTCHARS; break; case WM_NCCREATE: SetWindowLong(hWnd, GWL_EXSTYLE, (GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_CLIENTEDGE)); lResult = TRUE; break; case WM_CREATE : /* create pallette window */ { HDC hdc; HMENU id; UINT uiFieldStart; FARPROC lpfnFieldProc; pControl = (CONTROL*)GlobalAlloc(GMEM_FIXED, sizeof(CONTROL)); if (pControl) { HFONT OldFont; RECT rect; LPCREATESTRUCT lpCreateStruct; lpCreateStruct = ((CREATESTRUCT *)lParam); pControl->fEnabled = TRUE; pControl->fPainted = FALSE; pControl->fInMessageBox = FALSE; pControl->hwndParent = lpCreateStruct->hwndParent; pControl->bControlInFocus = FALSE; pControl->bCancelParentNotify = FALSE; pControl->pClsInfo = pClsInfo; pControl->ChildrenArr = (FIELD*)GlobalAlloc(GMEM_FIXED, sizeof(FIELD)*(pClsInfo->nNumFields)); pControl->lpfnAlert = NULL; if (!pControl->ChildrenArr) { GlobalFree(pControl); pControl = 0; DestroyWindow(hWnd); return 0; } for (i = 0; i < pClsInfo->nNumFields; ++i) { (*(pClsInfo->lpfnInit))(i,&(pControl->ChildrenArr[i])); } hdc = GetDC(hWnd); if (hdc != NULL) { GetClientRect(hWnd, &rect); OldFont = SelectObject(hdc, CreateFontIndirect(&logfont)); pControl->uiMaxCharWidth = (*(pClsInfo->lpfnMaxCharWidth))(hdc); GetCharWidth(hdc, pClsInfo->chFiller, pClsInfo->chFiller, (int *)(&pControl->uiFillerWidth)); DeleteObject(SelectObject(hdc, OldFont )); ReleaseDC(hWnd, hdc); uiFieldStart = LEAD_ROOM; lpfnFieldProc = MakeProcInstance((FARPROC)IPAddressFieldProc, LPCS->hInstance); id = (HMENU)GetWindowLongPtr(hWnd, GWLP_ID); for (i = 0; i < pClsInfo->nNumFields; ++i) { pControl->ChildrenArr[i].uiWidth = (pControl->ChildrenArr[i].nChars) * (pControl->uiMaxCharWidth+2); pControl->ChildrenArr[i].hWnd = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE, uiFieldStart, HEAD_ROOM, pControl->ChildrenArr[i].uiWidth, (rect.bottom-rect.top), hWnd, id, lpCreateStruct->hInstance, (LPVOID)NULL); SAVE_CONTROL_HANDLE(pControl->ChildrenArr[i].hWnd, i); SendMessage(pControl->ChildrenArr[i].hWnd, EM_LIMITTEXT, pControl->ChildrenArr[i].nChars, 0L); SendMessage(pControl->ChildrenArr[i].hWnd, WM_SETFONT, (WPARAM)CreateFontIndirect(&logfont), TRUE); // Disable IME support for the ip editor ImmAssociateContext(pControl->ChildrenArr[i].hWnd, NULL); pControl->ChildrenArr[i].lpfnWndProc = (FARPROC) GetWindowLongPtr(pControl->ChildrenArr[i].hWnd, GWLP_WNDPROC); SetWindowLongPtr(pControl->ChildrenArr[i].hWnd, GWLP_WNDPROC, (LONG_PTR)lpfnFieldProc); uiFieldStart += pControl->ChildrenArr[i].uiWidth + pControl->uiFillerWidth; } // for SAVE_CONTROL_HANDLE(hWnd, pControl); // // need to make control wider // uiFieldStart -= pControl->uiFillerWidth; { RECT r; POINT p1; POINT p2; GetWindowRect(hWnd, &r); // screen coords p1.x = r.left; p1.y = r.top; p2.x = r.right; p2.y = r.bottom; ScreenToClient(lpCreateStruct->hwndParent, &p1); ScreenToClient(lpCreateStruct->hwndParent, &p2); p2.x = p1.x + uiFieldStart + 2; MoveWindow(hWnd, p1.x, p1.y, p2.x-p1.x, p2.y-p1.y, FALSE); } } } // if else { DestroyWindow(hWnd); } } lResult = 0; break; case WM_PAINT: /* paint control window */ { PAINTSTRUCT Ps; RECT rect; RECT headRect; /* area before the first edit box */ COLORREF TextColor; COLORREF cRef; HFONT OldFont; HBRUSH hbr; BOOL fPaintAsEnabled; pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); fPaintAsEnabled = pControl->fEnabled; if (fPaintAsEnabled) { /* onlys some of the edit controls might be enabled */ for (i = 0; i < pClsInfo->nNumFields; ++i) { if (!IsWindowEnabled(pControl->ChildrenArr[i].hWnd)) { fPaintAsEnabled = FALSE; /* need disabled background */ break; } // if } // for } // if BeginPaint(hWnd, (LPPAINTSTRUCT)&Ps); OldFont = SelectObject( Ps.hdc, CreateFontIndirect(&logfont)); GetClientRect(hWnd, &rect); if (fPaintAsEnabled) { TextColor = GetSysColor(COLOR_WINDOWTEXT); cRef = GetSysColor(COLOR_WINDOW); } else { TextColor = GetSysColor(COLOR_GRAYTEXT); cRef = GetSysColor(COLOR_3DFACE); } SetBkColor(Ps.hdc, cRef); SetTextColor(Ps.hdc, TextColor); hbr = CreateSolidBrush(cRef); if (hbr != NULL) { FillRect(Ps.hdc, &rect, hbr); DeleteObject(hbr); SetRect(&headRect, 0, HEAD_ROOM, LEAD_ROOM, (rect.bottom-rect.top)); CopyRect(&rect, &headRect); for (i = 0; i < pClsInfo->nNumFields-1; ++i) { rect.left += pControl->ChildrenArr[i].uiWidth; rect.right = rect.left + pControl->uiFillerWidth; if (IsWindowEnabled(pControl->ChildrenArr[i].hWnd)) { TextColor = GetSysColor(COLOR_WINDOWTEXT); cRef = GetSysColor(COLOR_WINDOW); } else { TextColor = GetSysColor(COLOR_GRAYTEXT); cRef = GetSysColor(COLOR_3DFACE); } SetBkColor(Ps.hdc, cRef); SetTextColor(Ps.hdc, TextColor); hbr = CreateSolidBrush(cRef); if (hbr != NULL) { if (i == 0) FillRect(Ps.hdc, &headRect, hbr); FillRect(Ps.hdc, &rect, hbr); DeleteObject(hbr); } ExtTextOut(Ps.hdc, rect.left, HEAD_ROOM, ETO_OPAQUE, &rect, pClsInfo->lpszFiller, 1, NULL); rect.left = rect.right; } } pControl->fPainted = TRUE; DeleteObject(SelectObject(Ps.hdc, OldFont)); EndPaint(hWnd, &Ps); } break; case WM_SETFOCUS : /* get focus - display caret */ pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); /* set the focus on the first enabled field */ for (i = 0; i < pClsInfo->nNumFields; ++i) { if (IsWindowEnabled(pControl->ChildrenArr[i].hWnd)) { EnterField(&(pControl->ChildrenArr[i]), 0, pControl->ChildrenArr[i].nChars); break; } } break; case WM_LBUTTONDOWN : /* left button depressed - fall through */ SetFocus(hWnd); break; case WM_ENABLE: { pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); pControl->fEnabled = (BOOL)wParam; for (i = 0; i < pClsInfo->nNumFields; ++i) { EnableWindow(pControl->ChildrenArr[i].hWnd, (BOOL)wParam); } if (pControl->fPainted) InvalidateRect(hWnd, NULL, FALSE); } break; case WM_DESTROY : pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); if (pControl == NULL) break; // memory already freed (MFC DestroyWindow() call) // Restore all the child window procedures before we delete our memory block. for (i = 0; i < pClsInfo->nNumFields; ++i) { SendMessage(pControl->ChildrenArr[i].hWnd, WM_DESTROY, 0, 0); SetWindowLongPtr(pControl->ChildrenArr[i].hWnd, GWLP_WNDPROC, (LONG_PTR)pControl->ChildrenArr[i].lpfnWndProc); } // free memory and reset window long GlobalFree(pControl->ChildrenArr); GlobalFree(pControl); SAVE_CONTROL_HANDLE(hWnd, NULL); break; case WM_COMMAND: switch (HIWORD(wParam)) { // One of the fields lost the focus, see if it lost the focus to another field // of if we've lost the focus altogether. If its lost altogether, we must send // an EN_KILLFOCUS notification on up the ladder. case EN_KILLFOCUS: { HWND hFocus; pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); if (!pControl->fInMessageBox) { hFocus = GetFocus(); for (i = 0; i < pClsInfo->nNumFields; ++i) if (pControl->ChildrenArr[i].hWnd == hFocus) break; if (i >= pClsInfo->nNumFields) { SendMessage(pControl->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_KILLFOCUS), (LPARAM)hWnd); pControl->bControlInFocus = FALSE; } } } break; case EN_SETFOCUS: { HWND hFocus; pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); if (!pControl->fInMessageBox) { hFocus = (HWND)lParam; for (i = 0; i < pClsInfo->nNumFields; ++i) if (pControl->ChildrenArr[i].hWnd == hFocus) break; // send a focus message when the if (i < pClsInfo->nNumFields && pControl->bControlInFocus == FALSE) { SendMessage(pControl->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_SETFOCUS), (LPARAM)hWnd); pControl->bControlInFocus = TRUE; // only set the focus once } } } break; case EN_CHANGE: pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); if (pControl->bCancelParentNotify == FALSE) { SendMessage(pControl->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd); } break; } break; // Get the value of the control fields. case DNS_MASK_CTRL_GET: { DWORD* dwArr; UINT nArrSize; pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); dwArr = (DWORD*)wParam; nArrSize = (UINT)lParam; lResult = 0; for (i = 0; (i < pClsInfo->nNumFields) && ( i < nArrSize); ++i) { dwArr[i] = GetFieldValue(&(pControl->ChildrenArr[i]), pClsInfo); if (dwArr[i] != FIELD_EMPTY) ++lResult; } } break; // Clear all fields to blanks. case DNS_MASK_CTRL_CLEAR: { pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); pControl->bCancelParentNotify = TRUE; if (wParam == -1) { for (i = 0; i < pClsInfo->nNumFields; ++i) { SendMessage(pControl->ChildrenArr[i].hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) TEXT("")); } } else { SendMessage(pControl->ChildrenArr[wParam].hWnd, WM_SETTEXT, 0, (LPARAM)(LPSTR) TEXT("")); } pControl->bCancelParentNotify = FALSE; SendMessage(pControl->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd); } break; // Set the value of the IP Address. The address is in the lParam with the // first address byte being the high byte, the second being the second byte, // and so on. A lParam value of -1 removes the address. case DNS_MASK_CTRL_SET: { DWORD* dwArr; UINT nArrSize; static TCHAR szBuf[BUFFER_LEN+1]; pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); pControl->bCancelParentNotify = TRUE; dwArr = (DWORD*)wParam; nArrSize = (UINT)lParam; for (i = 0; i < (pClsInfo->nNumFields) && ( i < nArrSize); ++i) { (*(pControl->pClsInfo->lpfnNumberToString))(szBuf,dwArr[i]); SendMessage(pControl->ChildrenArr[i].hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) szBuf); } pControl->bCancelParentNotify = FALSE; SendMessage(pControl->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd); } break; case DNS_MASK_CTRL_SET_LOW_RANGE: case DNS_MASK_CTRL_SET_HI_RANGE: if (wParam < pClsInfo->nNumFields) { pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); if (wMsg == DNS_MASK_CTRL_SET_LOW_RANGE) pControl->ChildrenArr[wParam].dwLow = (DWORD)lParam; else pControl->ChildrenArr[wParam].dwHigh = (DWORD)lParam; } break; // Set the focus to this control. // wParam = the field number to set focus to, or -1 to set the focus to the // first non-blank field. case DNS_MASK_CTRL_SETFOCUS: pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); if (wParam >= pClsInfo->nNumFields) { for (wParam = 0; wParam < pClsInfo->nNumFields; ++wParam) if (GetFieldValue(&(pControl->ChildrenArr[wParam]), pControl->pClsInfo) == FIELD_EMPTY) break; if (wParam >= pClsInfo->nNumFields) wParam = 0; } EnterField(&(pControl->ChildrenArr[wParam]), 0, pControl->ChildrenArr[wParam].nChars); break; // Determine whether all four subfields are blank case DNS_MASK_CTRL_ISBLANK: pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); lResult = TRUE; for (i = 0; i < pClsInfo->nNumFields; ++i) { if (GetFieldValue(&(pControl->ChildrenArr[i]), pControl->pClsInfo) != FIELD_EMPTY) { lResult = FALSE; break; } } break; case DNS_MASK_CTRL_SET_ALERT: { pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); pControl->lpfnAlert = (int (*)(HWND, DWORD, DWORD, DWORD))(wParam); lResult = TRUE; } break; case DNS_MASK_CTRL_ENABLE_FIELD: { pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd); //int nField = (int)wParam; if ( ((int)wParam >= 0) && ((UINT)wParam < pClsInfo->nNumFields) ) { EnableWindow(pControl->ChildrenArr[(int)wParam].hWnd, (BOOL)lParam); } if (pControl->fPainted) InvalidateRect(hWnd, NULL, FALSE); } break; default: lResult = DefWindowProc( hWnd, wMsg, wParam, lParam ); break; } return( lResult ); } /* IPAddressFieldProc() - Edit field window procedure This function sub-classes each edit field. */ LRESULT FAR PASCAL IPAddressFieldProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { CONTROL *pControl; FIELD *pField; HWND hControlWindow; LONG_PTR nChildID; LRESULT lresult; hControlWindow = GetParent(hWnd); if (!hControlWindow) return 0; pControl = (CONTROL *)GET_CONTROL_HANDLE(hControlWindow); nChildID = (LONG_PTR)GET_CONTROL_HANDLE(hWnd); pField = &(pControl->ChildrenArr[nChildID]); if (pField->hWnd != hWnd) return 0; switch (wMsg) { case WM_DESTROY: DeleteObject((HGDIOBJ)SendMessage(hWnd, WM_GETFONT, 0, 0)); return 0; case WM_CHAR: // Typing in the last digit in a field, skips to the next field. //if (wParam >= TEXT('0') && wParam <= TEXT('9')) if ( (*(pControl->pClsInfo->lpfnValChar))((TCHAR)wParam)) { DWORD dwResult; dwResult = (DWORD)CallWindowProc((WNDPROC)pControl->ChildrenArr[nChildID].lpfnWndProc, hWnd, wMsg, wParam, lParam); dwResult = (DWORD)SendMessage(hWnd, EM_GETSEL, 0, 0L); if (dwResult == (DWORD)MAKELPARAM((WORD)(pField->nChars), (WORD)(pField->nChars)) && ExitField(pControl, (UINT)nChildID) && nChildID < (int)pControl->pClsInfo->nNumFields-1) { EnterField(&(pControl->ChildrenArr[nChildID+1]), 0, pField->nChars); } return dwResult; } // spaces and periods fills out the current field and then if possible, // goes to the next field. else if ((TCHAR)wParam == pControl->pClsInfo->chFiller || wParam == SPACE ) { DWORD dwResult; dwResult = (DWORD)SendMessage(hWnd, EM_GETSEL, 0, 0L); if (dwResult != 0L && HIWORD(dwResult) == LOWORD(dwResult) && ExitField(pControl, nChildID)) { if (nChildID >= (int)pControl->pClsInfo->nNumFields-1) MessageBeep((UINT)-1); else { EnterField(&(pControl->ChildrenArr[nChildID+1]), 0, pControl->ChildrenArr[nChildID+1].nChars); } } return 0; } // Backspaces go to the previous field if at the beginning of the current field. // Also, if the focus shifts to the previous field, the backspace must be // processed by that field. else if (wParam == BACK_SPACE) { if (nChildID > 0 && SendMessage(hWnd, EM_GETSEL, 0, 0L) == 0L) { if (SwitchFields(pControl, nChildID, nChildID-1, pControl->ChildrenArr[nChildID-1].nChars, pControl->ChildrenArr[nChildID-1].nChars) && SendMessage(pControl->ChildrenArr[nChildID-1].hWnd, EM_LINELENGTH, (WPARAM)0, (LPARAM)0L) != 0L && IsWindowEnabled(pControl->ChildrenArr[nChildID-1].hWnd)) { SendMessage(pControl->ChildrenArr[nChildID-1].hWnd, wMsg, wParam, lParam); } return 0; } } // Any other printable characters are not allowed. else if (wParam > SPACE) { MessageBeep((UINT)-1); return 0; } break; case WM_KEYDOWN: switch (wParam) { // Arrow keys move between fields when the end of a field is reached. case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: if (GetKeyState(VK_CONTROL) < 0) { if ((wParam == VK_LEFT || wParam == VK_UP) && nChildID > 0) { SwitchFields(pControl, nChildID, nChildID-1, 0, pField->nChars); return 0; } else if ((wParam == VK_RIGHT || wParam == VK_DOWN) && nChildID < (int)pControl->pClsInfo->nNumFields-1) { SwitchFields(pControl, nChildID, nChildID+1, 0, pField->nChars); return 0; } } else { DWORD dwResult; WORD wStart, wEnd; dwResult = (DWORD)SendMessage(hWnd, EM_GETSEL, 0, 0L); wStart = LOWORD(dwResult); wEnd = HIWORD(dwResult); if (wStart == wEnd) { if ((wParam == VK_LEFT || wParam == VK_UP) && wStart == 0 && nChildID > 0) { SwitchFields(pControl, nChildID, nChildID-1, pField->nChars, pField->nChars); return 0; } else if ((wParam == VK_RIGHT || wParam == VK_DOWN) && nChildID < (int)pControl->pClsInfo->nNumFields-1) { dwResult = (DWORD)SendMessage(hWnd, EM_LINELENGTH, 0, 0L); if (wStart >= dwResult) { SwitchFields(pControl, nChildID, nChildID+1, 0, 0); return 0; } } } } break; // Home jumps back to the beginning of the first field. case VK_HOME: if (nChildID > 0) { SwitchFields(pControl, nChildID, 0, 0, 0); return 0; } break; // End scoots to the end of the last field. case VK_END: if (nChildID < (int)pControl->pClsInfo->nNumFields-1) { SwitchFields(pControl, nChildID, (pControl->pClsInfo->nNumFields)-1, pField->nChars, pField->nChars); return 0; } break; } // switch (wParam) break; case WM_KILLFOCUS: if ( !ExitField( pControl, nChildID )) { return 0; } } // switch (wMsg) lresult = CallWindowProc( (WNDPROC)pControl->ChildrenArr[nChildID].lpfnWndProc, hWnd, wMsg, wParam, lParam); return lresult; } /* Switch the focus from one field to another. call pControl = Pointer to the CONTROL structure. iOld = Field we're leaving. iNew = Field we're entering. hNew = Window of field to goto wStart = First character selected wEnd = Last character selected + 1 returns TRUE on success, FALSE on failure. Only switches fields if the current field can be validated. */ BOOL SwitchFields(CONTROL *pControl, LONG_PTR nOld, LONG_PTR nNew, UINT nStart, UINT nEnd) { if (!ExitField(pControl, nOld)) return FALSE; EnterField(&(pControl->ChildrenArr[nNew]), nStart, nEnd); return TRUE; } /* Set the focus to a specific field's window. call pField = pointer to field structure for the field. wStart = First character selected wEnd = Last character selected + 1 */ void EnterField(FIELD *pField, UINT nStart, UINT nEnd) { SetFocus(pField->hWnd); SendMessage(pField->hWnd, EM_SETSEL, (WPARAM)nStart, (LPARAM)nEnd); } /* Exit a field. call pControl = pointer to CONTROL structure. iField = field number being exited. returns TRUE if the user may exit the field. FALSE if he may not. */ BOOL ExitField(CONTROL *pControl, LONG_PTR nField) { HWND hControlWnd; HWND hDialog; WORD wLength; FIELD *pField; static TCHAR szBuf[BUFFER_LEN+1]; DWORD xVal; pField = &(pControl->ChildrenArr[nField]); *(WORD *)szBuf = (sizeof(szBuf)/sizeof(TCHAR)) - 1; wLength = (WORD)SendMessage(pField->hWnd,EM_GETLINE,0,(LPARAM)(LPSTR)szBuf); if (wLength != 0) { szBuf[wLength] = TEXT('\0'); xVal = (*(pControl->pClsInfo->lpfnStringToNumber))(szBuf,(int)wLength); if (xVal < pField->dwLow || xVal > pField->dwHigh) { if ( xVal < pField->dwLow ) { /* too small */ wsprintf(szBuf, TEXT("%d"), pField->dwLow ); } else { /* must be bigger */ wsprintf(szBuf, TEXT("%d"), pField->dwHigh ); } SendMessage(pField->hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) szBuf); if ((hControlWnd = GetParent(pField->hWnd)) != NULL && (hDialog = GetParent(hControlWnd)) != NULL) { pControl->fInMessageBox = TRUE; if (pControl->lpfnAlert != NULL) // call user provided hook { (*(pControl->lpfnAlert))(hDialog, xVal, pField->dwLow, pField->dwHigh); } else { MessageBeep(MB_ICONEXCLAMATION); } pControl->fInMessageBox = FALSE; SendMessage(pField->hWnd, EM_SETSEL, 0, pField->nChars); return FALSE; } } } return TRUE; } /* Get the value stored in a field. call pField = pointer to the FIELD structure for the field. returns The value (0..255) or -1 if the field has not value. */ DWORD GetFieldValue(FIELD *pField, CLS_INFO* pClsInfo) { WORD wLength; static TCHAR szBuf[BUFFER_LEN+1]; *(WORD *)szBuf = (sizeof(szBuf)/sizeof(TCHAR)) - 1; wLength = (WORD)SendMessage(pField->hWnd,EM_GETLINE,0,(LPARAM)(LPSTR)szBuf); if (wLength != 0) { return (*(pClsInfo->lpfnStringToNumber))(szBuf,wLength); } else return FIELD_EMPTY; } //////////////////////////////////////////////////////////////////////////////////////////////// DWORD DecimalStringToNumber(LPCTSTR lpszBuf, int nLen) { DWORD x; int j; for (x=0, j=0;j= TEXT('0') && lpszBuf[j] <= TEXT('9')) digit = lpszBuf[j]-TEXT('0'); else if (lpszBuf[j] >= TEXT('A') && lpszBuf[j] <= TEXT('F')) digit = lpszBuf[j]-TEXT('A') + 10; else// assume 'a' to 'f' digit = lpszBuf[j]-TEXT('a') + 10; x = x*16+digit; } return x; } BOOL ValidateDecimalChar(TCHAR ch) { // allow only digits WCHAR sz[2]; BOOL b; sz[0]=ch; sz[1]=L''; b = (ch >= TEXT('0') && ch <= TEXT('9')); return b; } BOOL ValidateHexChar(TCHAR ch) { // allow only digits return ( (ch >= TEXT('0') && ch <= TEXT('9')) || (ch >= TEXT('a') && ch <= TEXT('f')) || (ch >= TEXT('A') && ch <= TEXT('F')) ); } void InitIPv4Field(int nIndex, FIELD* pField) { nIndex; // Must use formal parameters for /W4 pField->dwLow = 0;//MIN_FIELD_VALUE; pField->dwHigh = 255; //MAX_FIELD_VALUE; pField->nChars = 3; //CHARS_PER_FIELD; } void InitIPv6Field(int nIndex, FIELD* pField) { nIndex; // Must user formal parameters for /W4 pField->dwLow = 0; //MIN_FIELD_VALUE; pField->dwHigh = 0xFFFF; //MAX_FIELD_VALUE; pField->nChars = 4; //CHARS_PER_FIELD; } void InitTTLField(int nIndex, FIELD* pField) { pField->dwLow = 0; switch (nIndex) { case 0: // days pField->dwHigh = 49710; pField->nChars = 5; break; case 1: // hours pField->dwHigh = 23; pField->nChars = 2; break; case 2: // minutes pField->dwHigh = 59; pField->nChars = 2; break; case 3: // seconds pField->dwHigh = 59; pField->nChars = 2; break; default: ; } } UINT _MaxCharWidthHelper(HDC hDC, UINT iFirstChar, UINT iLastChar) { FLOAT fFract[10] = {0}; INT nWidth[10] = {0}; int i; FLOAT maxVal; FLOAT curWidth; UINT retVal; retVal = 8; // good default if we fail if (GetCharWidthFloat(hDC, iFirstChar, iLastChar,fFract) && GetCharWidth(hDC,iFirstChar, iLastChar, nWidth)) { maxVal = 0.0; for (i=0;i<10;i++) { curWidth = fFract[i] + (FLOAT)nWidth[i]; if (curWidth > maxVal) maxVal = curWidth; } if (maxVal > ((FLOAT)((UINT)maxVal))) retVal = (UINT) (maxVal+1); else retVal = (UINT)maxVal; } return retVal; } UINT MaxCharWidthDecimal(HDC hDC) { return _MaxCharWidthHelper(hDC,TEXT('0'), TEXT('9')); } UINT MaxCharWidthHex(HDC hDC) { UINT retVal; UINT nMax1; UINT nMax2; UINT nMax3; retVal = 0; nMax1 = _MaxCharWidthHelper(hDC,TEXT('0'), TEXT('9')); if (nMax1 > retVal) retVal = nMax1; nMax2 = _MaxCharWidthHelper(hDC,TEXT('a'), TEXT('f')); if (nMax2 > retVal) retVal = nMax2; nMax3 = _MaxCharWidthHelper(hDC,TEXT('A'), TEXT('F')); if (nMax3 > retVal) retVal = nMax3; return retVal; } /* class info structs for the various types */ CLS_INFO _IPv4ClsInfo = { TEXT('.'), TEXT("."), 4, InitIPv4Field, ValidateDecimalChar, DecimalStringToNumber, NumberToDecimalString, MaxCharWidthDecimal }; CLS_INFO _IPv6ClsInfo = { TEXT(':'), TEXT(":"), 8, InitIPv6Field, ValidateHexChar, HexStringToNumber, NumberToHexString, MaxCharWidthHex }; CLS_INFO _TTLClsInfo = { TEXT(':'), TEXT(":"), 4, InitTTLField, ValidateDecimalChar, DecimalStringToNumber, NumberToDecimalString, MaxCharWidthDecimal }; LRESULT FAR PASCAL IPv4WndFn(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam ) { return IPAddressWndFnEx( hWnd, wMsg, wParam, lParam , &_IPv4ClsInfo); } LRESULT FAR PASCAL IPv6WndFn(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam ) { return IPAddressWndFnEx( hWnd, wMsg, wParam, lParam , &_IPv6ClsInfo); } LRESULT FAR PASCAL TTLWndFn(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam ) { return IPAddressWndFnEx( hWnd, wMsg, wParam, lParam , &_TTLClsInfo); }