981 lines
28 KiB
C
981 lines
28 KiB
C
|
/* Copyright (c) 1991, Microsoft Corporation, all rights reserved
|
||
|
|
||
|
ipaddr.c - TCP/IP Address custom control
|
||
|
|
||
|
November 9, 1992 Greg Strange
|
||
|
*/
|
||
|
|
||
|
#include "ctlspriv.h"
|
||
|
|
||
|
|
||
|
// The character that is displayed between address fields.
|
||
|
#define FILLER TEXT('.')
|
||
|
#define SZFILLER TEXT(".")
|
||
|
#define SPACE TEXT(' ')
|
||
|
#define BACK_SPACE 8
|
||
|
|
||
|
/* Min, max values */
|
||
|
#define NUM_FIELDS 4
|
||
|
#define CHARS_PER_FIELD 3
|
||
|
#define HEAD_ROOM 1 // space at top of control
|
||
|
#define LEAD_ROOM 3 // space at front of control
|
||
|
#define MIN_FIELD_VALUE 0 // default minimum allowable field value
|
||
|
#define MAX_FIELD_VALUE 255 // default maximum allowable field value
|
||
|
|
||
|
|
||
|
// 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;
|
||
|
WNDPROC lpfnWndProc;
|
||
|
BYTE byLow; // lowest allowed value for this field.
|
||
|
BYTE byHigh; // Highest allowed value for this field.
|
||
|
} FIELD;
|
||
|
|
||
|
typedef struct tagIPADDR
|
||
|
{
|
||
|
HWND hwndParent;
|
||
|
HWND hwnd;
|
||
|
UINT uiFieldWidth;
|
||
|
UINT uiFillerWidth;
|
||
|
BOOL fEnabled : 1;
|
||
|
BOOL fPainted : 1;
|
||
|
BOOL bControlInFocus : 1; // TRUE if the control is already in focus, dont't send another focus command
|
||
|
BOOL bCancelParentNotify : 1; // Don't allow the edit controls to notify parent if TRUE
|
||
|
BOOL fInMessageBox : 1; // Set when a message box is displayed so that
|
||
|
BOOL fFontCreated :1;
|
||
|
HFONT hfont;
|
||
|
// we don't send a EN_KILLFOCUS message when
|
||
|
// we receive the EN_KILLFOCUS message for the
|
||
|
// current field.
|
||
|
FIELD Children[NUM_FIELDS];
|
||
|
|
||
|
HTHEME hTheme;
|
||
|
} IPADDR;
|
||
|
|
||
|
|
||
|
// The following macros extract and store the CONTROL structure for a control.
|
||
|
#define IPADDRESS_EXTRA sizeof(DWORD)
|
||
|
|
||
|
#define GET_IPADDR_HANDLE(hWnd) ((HGLOBAL)(GetWindowLongPtr((hWnd), GWLP_USERDATA)))
|
||
|
#define SAVE_IPADDR_HANDLE(hWnd,x) (SetWindowLongPtr((hWnd), GWLP_USERDATA, (LONG_PTR)(x)))
|
||
|
|
||
|
|
||
|
/* internal IPAddress function prototypes */
|
||
|
LRESULT IPAddressWndFn( HWND, UINT, WPARAM, LPARAM );
|
||
|
LRESULT IPAddressFieldProc(HWND, UINT, WPARAM, LPARAM);
|
||
|
BOOL SwitchFields(IPADDR *, int, int, WORD, WORD);
|
||
|
void EnterField(FIELD *, WORD, WORD);
|
||
|
BOOL ExitField(IPADDR *, int iField);
|
||
|
int GetFieldValue(FIELD *);
|
||
|
void SetFieldValue(IPADDR *pipa, int iField, int iValue);
|
||
|
BOOL IsDBCS();
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
IPAddrInit() - IPAddress custom control initialization
|
||
|
call
|
||
|
hInstance = library or application instance
|
||
|
return
|
||
|
TRUE on success, FALSE on failure.
|
||
|
|
||
|
This function does all the one time initialization of IPAddress custom
|
||
|
controls. Specifically it creates the IPAddress window class.
|
||
|
*/
|
||
|
int InitIPAddr(HANDLE hInstance)
|
||
|
{
|
||
|
WNDCLASS wc;
|
||
|
|
||
|
wc.lpszClassName = WC_IPADDRESS;
|
||
|
wc.hCursor = LoadCursor(NULL,IDC_IBEAM);
|
||
|
wc.hIcon = NULL;
|
||
|
wc.lpszMenuName = (LPCTSTR)NULL;
|
||
|
wc.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS|CS_GLOBALCLASS;
|
||
|
wc.lpfnWndProc = IPAddressWndFn;
|
||
|
wc.hInstance = hInstance;
|
||
|
wc.hIcon = NULL;
|
||
|
wc.cbWndExtra = IPADDRESS_EXTRA;
|
||
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 );
|
||
|
wc.cbClsExtra = 0;
|
||
|
|
||
|
if (!RegisterClass(&wc) && !GetClassInfo(hInstance, WC_IPADDRESS, &wc))
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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)
|
||
|
{
|
||
|
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('0')) || (pszString[nPos]>TEXT('9')))
|
||
|
{
|
||
|
// not a number
|
||
|
nField++;
|
||
|
fFinish = (nField == 4);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwValue[nField] *= 10;
|
||
|
dwValue[nField] += (pszString[nPos]-TEXT('0'));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IP_OnSetFont(IPADDR* pipa, HFONT hfont, BOOL fRedraw)
|
||
|
{
|
||
|
int i;
|
||
|
RECT rect;
|
||
|
HFONT OldFont;
|
||
|
BOOL fNewFont = FALSE;
|
||
|
UINT uiFieldStart;
|
||
|
HDC hdc;
|
||
|
|
||
|
if (hfont) {
|
||
|
fNewFont = TRUE;
|
||
|
} else {
|
||
|
hfont = (HFONT)SendMessage(pipa->hwnd, WM_GETFONT, 0, 0);
|
||
|
}
|
||
|
|
||
|
hdc = GetDC(pipa->hwnd);
|
||
|
OldFont = SelectObject(hdc, hfont);
|
||
|
GetCharWidth(hdc, FILLER, FILLER,
|
||
|
(int *)(&pipa->uiFillerWidth));
|
||
|
SelectObject(hdc, OldFont);
|
||
|
ReleaseDC(pipa->hwnd, hdc);
|
||
|
|
||
|
GetClientRect(pipa->hwnd, &rect);
|
||
|
pipa->hfont = hfont;
|
||
|
pipa->uiFieldWidth = (RECTWIDTH(rect)
|
||
|
- LEAD_ROOM
|
||
|
- pipa->uiFillerWidth
|
||
|
*(NUM_FIELDS-1))
|
||
|
/ NUM_FIELDS;
|
||
|
|
||
|
|
||
|
uiFieldStart = LEAD_ROOM;
|
||
|
|
||
|
for (i = 0; i < NUM_FIELDS; i++) {
|
||
|
|
||
|
HWND hwnd = pipa->Children[i].hWnd;
|
||
|
|
||
|
if (fNewFont)
|
||
|
SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)fRedraw);
|
||
|
|
||
|
SetWindowPos(hwnd, NULL,
|
||
|
uiFieldStart,
|
||
|
HEAD_ROOM,
|
||
|
pipa->uiFieldWidth,
|
||
|
(rect.bottom-rect.top),
|
||
|
SWP_NOACTIVATE);
|
||
|
|
||
|
uiFieldStart += pipa->uiFieldWidth
|
||
|
+ pipa->uiFillerWidth;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
LRESULT IPAddressWndFn( hWnd, wMsg, wParam, lParam )
|
||
|
HWND hWnd;
|
||
|
UINT wMsg;
|
||
|
WPARAM wParam;
|
||
|
LPARAM lParam;
|
||
|
{
|
||
|
LRESULT lResult;
|
||
|
IPADDR *pipa;
|
||
|
int i;
|
||
|
|
||
|
pipa = (IPADDR *)GET_IPADDR_HANDLE(hWnd);
|
||
|
lResult = TRUE;
|
||
|
|
||
|
switch( wMsg )
|
||
|
{
|
||
|
|
||
|
// use empty string (not NULL) to set to blank
|
||
|
case WM_SETTEXT:
|
||
|
{
|
||
|
TCHAR szBuf[CHARS_PER_FIELD+1];
|
||
|
DWORD dwValue[4];
|
||
|
LPTSTR pszString = (LPTSTR)lParam;
|
||
|
|
||
|
FormatIPAddress(pszString, &dwValue[0]);
|
||
|
pipa->bCancelParentNotify = TRUE;
|
||
|
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
if (pszString[0] == 0)
|
||
|
{
|
||
|
szBuf[0] = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wsprintf(szBuf, TEXT("%d"), dwValue[i]);
|
||
|
}
|
||
|
SendMessage(pipa->Children[i].hWnd, WM_SETTEXT,
|
||
|
0, (LPARAM) (LPSTR) szBuf);
|
||
|
}
|
||
|
|
||
|
pipa->bCancelParentNotify = FALSE;
|
||
|
|
||
|
SendMessage(pipa->hwndParent, WM_COMMAND,
|
||
|
MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_GETTEXTLENGTH:
|
||
|
case WM_GETTEXT:
|
||
|
{
|
||
|
int iFieldValue;
|
||
|
DWORD dwValue[4];
|
||
|
TCHAR pszResult[30];
|
||
|
TCHAR *pszDest = (TCHAR *)lParam;
|
||
|
|
||
|
lResult = 0;
|
||
|
dwValue[0] = 0;
|
||
|
dwValue[1] = 0;
|
||
|
dwValue[2] = 0;
|
||
|
dwValue[3] = 0;
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
iFieldValue = GetFieldValue(&(pipa->Children[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_GETTEXT)
|
||
|
{
|
||
|
StrCpyN(pszDest, pszResult, (int) wParam);
|
||
|
lResult = lstrlen( pszDest );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lResult = lstrlen( pszResult );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_GETDLGCODE :
|
||
|
lResult = DLGC_WANTCHARS;
|
||
|
break;
|
||
|
|
||
|
case WM_NCCREATE:
|
||
|
SetWindowBits(hWnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE);
|
||
|
SetWindowBits(hWnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
|
||
|
|
||
|
lResult = TRUE;
|
||
|
break;
|
||
|
|
||
|
case WM_THEMECHANGED:
|
||
|
{
|
||
|
if (pipa->hTheme)
|
||
|
CloseThemeData(pipa->hTheme);
|
||
|
|
||
|
pipa->hTheme = OpenThemeData(hWnd, L"Combobox");
|
||
|
|
||
|
InvalidateRect(hWnd, NULL, TRUE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
|
||
|
case WM_CREATE : /* create pallette window */
|
||
|
{
|
||
|
LONG id;
|
||
|
|
||
|
pipa = (IPADDR*)LocalAlloc(LPTR, sizeof(IPADDR));
|
||
|
|
||
|
if (pipa)
|
||
|
{
|
||
|
CREATESTRUCT* pcs = ((CREATESTRUCT *)lParam);
|
||
|
SAVE_IPADDR_HANDLE(hWnd, pipa);
|
||
|
|
||
|
pipa->fEnabled = TRUE;
|
||
|
pipa->hwndParent = pcs->hwndParent;
|
||
|
pipa->hwnd = hWnd;
|
||
|
pipa->hTheme = OpenThemeData(hWnd, L"Combobox");
|
||
|
|
||
|
id = GetDlgCtrlID(hWnd);
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
pipa->Children[i].byLow = MIN_FIELD_VALUE;
|
||
|
pipa->Children[i].byHigh = MAX_FIELD_VALUE;
|
||
|
|
||
|
pipa->Children[i].hWnd = CreateWindowEx(0,
|
||
|
TEXT("Edit"),
|
||
|
NULL,
|
||
|
WS_CHILD |
|
||
|
ES_CENTER,
|
||
|
0, 10, 100, 100,
|
||
|
hWnd,
|
||
|
(HMENU)(LONG_PTR)id,
|
||
|
pcs->hInstance,
|
||
|
(LPVOID)NULL);
|
||
|
|
||
|
SAVE_IPADDR_HANDLE(pipa->Children[i].hWnd, i);
|
||
|
SendMessage(pipa->Children[i].hWnd, EM_LIMITTEXT,
|
||
|
CHARS_PER_FIELD, 0L);
|
||
|
|
||
|
pipa->Children[i].lpfnWndProc =
|
||
|
(WNDPROC) GetWindowLongPtr(pipa->Children[i].hWnd,
|
||
|
GWLP_WNDPROC);
|
||
|
|
||
|
SetWindowLongPtr(pipa->Children[i].hWnd,
|
||
|
GWLP_WNDPROC, (LONG_PTR)IPAddressFieldProc);
|
||
|
|
||
|
}
|
||
|
|
||
|
IP_OnSetFont(pipa, NULL, FALSE);
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
ShowWindow(pipa->Children[i].hWnd, SW_SHOW);
|
||
|
|
||
|
|
||
|
#undef pcs
|
||
|
}
|
||
|
else
|
||
|
DestroyWindow(hWnd);
|
||
|
}
|
||
|
lResult = 0;
|
||
|
break;
|
||
|
|
||
|
case WM_PAINT: /* paint IPADDR window */
|
||
|
{
|
||
|
PAINTSTRUCT Ps;
|
||
|
RECT rect;
|
||
|
COLORREF TextColor;
|
||
|
COLORREF cRef;
|
||
|
HFONT OldFont;
|
||
|
|
||
|
BeginPaint(hWnd, (LPPAINTSTRUCT)&Ps);
|
||
|
OldFont = SelectObject( Ps.hdc, pipa->hfont);
|
||
|
GetClientRect(hWnd, &rect);
|
||
|
if (pipa->fEnabled)
|
||
|
{
|
||
|
TextColor = GetSysColor(COLOR_WINDOWTEXT);
|
||
|
cRef = GetSysColor(COLOR_WINDOW);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TextColor = GetSysColor(COLOR_GRAYTEXT);
|
||
|
cRef = GetSysColor(COLOR_3DFACE);
|
||
|
}
|
||
|
|
||
|
FillRectClr(Ps.hdc, &rect, cRef);
|
||
|
SetRect(&rect, 0, HEAD_ROOM, pipa->uiFillerWidth, (rect.bottom-rect.top));
|
||
|
|
||
|
|
||
|
SetBkColor(Ps.hdc, cRef);
|
||
|
SetTextColor(Ps.hdc, TextColor);
|
||
|
|
||
|
for (i = 0; i < NUM_FIELDS-1; ++i)
|
||
|
{
|
||
|
rect.left += pipa->uiFieldWidth + pipa->uiFillerWidth;
|
||
|
rect.right += rect.left + pipa->uiFillerWidth;
|
||
|
ExtTextOut(Ps.hdc, rect.left, HEAD_ROOM, ETO_OPAQUE, &rect, SZFILLER, 1, NULL);
|
||
|
}
|
||
|
|
||
|
pipa->fPainted = TRUE;
|
||
|
|
||
|
SelectObject(Ps.hdc, OldFont);
|
||
|
EndPaint(hWnd, &Ps);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_SETFOCUS : /* get focus - display caret */
|
||
|
EnterField(&(pipa->Children[0]), 0, CHARS_PER_FIELD);
|
||
|
break;
|
||
|
|
||
|
HANDLE_MSG(pipa, WM_SETFONT, IP_OnSetFont);
|
||
|
|
||
|
case WM_LBUTTONDOWN : /* left button depressed - fall through */
|
||
|
SetFocus(hWnd);
|
||
|
break;
|
||
|
|
||
|
case WM_ENABLE:
|
||
|
{
|
||
|
pipa->fEnabled = (BOOL)wParam;
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
EnableWindow(pipa->Children[i].hWnd, (BOOL)wParam);
|
||
|
}
|
||
|
if (pipa->fPainted)
|
||
|
InvalidateRect(hWnd, NULL, FALSE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_NCPAINT:
|
||
|
{
|
||
|
if (pipa->hTheme)
|
||
|
{
|
||
|
HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL;
|
||
|
HBRUSH hbr = (HBRUSH)GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND);
|
||
|
|
||
|
if (CCDrawNonClientTheme(pipa->hTheme, hWnd, hrgn, hbr, 0, CBXS_NORMAL))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
goto DoDefault;
|
||
|
|
||
|
|
||
|
case WM_DESTROY :
|
||
|
// Restore all the child window procedures before we delete our memory block.
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
SendMessage(pipa->Children[i].hWnd, WM_DESTROY, 0, 0);
|
||
|
SetWindowLongPtr(pipa->Children[i].hWnd, GWLP_WNDPROC,
|
||
|
(LONG_PTR)pipa->Children[i].lpfnWndProc);
|
||
|
}
|
||
|
|
||
|
if (pipa->hTheme)
|
||
|
CloseThemeData(pipa->hTheme);
|
||
|
|
||
|
LocalFree(pipa);
|
||
|
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;
|
||
|
|
||
|
if (!pipa->fInMessageBox)
|
||
|
{
|
||
|
hFocus = GetFocus();
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
if (pipa->Children[i].hWnd == hFocus)
|
||
|
break;
|
||
|
|
||
|
if (i >= NUM_FIELDS)
|
||
|
{
|
||
|
SendMessage(pipa->hwndParent, WM_COMMAND,
|
||
|
MAKEWPARAM(GetDlgCtrlID(hWnd),
|
||
|
EN_KILLFOCUS), (LPARAM)hWnd);
|
||
|
pipa->bControlInFocus = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case EN_SETFOCUS:
|
||
|
{
|
||
|
HWND hFocus;
|
||
|
|
||
|
if (!pipa->fInMessageBox)
|
||
|
{
|
||
|
hFocus = (HWND)lParam;
|
||
|
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
if (pipa->Children[i].hWnd == hFocus)
|
||
|
break;
|
||
|
|
||
|
// send a focus message when the
|
||
|
if (i < NUM_FIELDS && pipa->bControlInFocus == FALSE)
|
||
|
{
|
||
|
SendMessage(pipa->hwndParent, WM_COMMAND,
|
||
|
MAKEWPARAM(GetDlgCtrlID(hWnd),
|
||
|
EN_SETFOCUS), (LPARAM)hWnd);
|
||
|
|
||
|
pipa->bControlInFocus = TRUE; // only set the focus once
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case EN_CHANGE:
|
||
|
if (pipa->bCancelParentNotify == FALSE)
|
||
|
{
|
||
|
SendMessage(pipa->hwndParent, WM_COMMAND,
|
||
|
MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// Get the value of the IP Address. The address is placed in the DWORD pointed
|
||
|
// to by lParam and the number of non-blank fields is returned.
|
||
|
case IPM_GETADDRESS:
|
||
|
{
|
||
|
int iFieldValue;
|
||
|
DWORD dwValue;
|
||
|
|
||
|
lResult = 0;
|
||
|
dwValue = 0;
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
iFieldValue = GetFieldValue(&(pipa->Children[i]));
|
||
|
if (iFieldValue == -1)
|
||
|
iFieldValue = 0;
|
||
|
else
|
||
|
++lResult;
|
||
|
dwValue = (dwValue << 8) + iFieldValue;
|
||
|
}
|
||
|
*((DWORD *)lParam) = dwValue;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// Clear all fields to blanks.
|
||
|
case IPM_CLEARADDRESS:
|
||
|
{
|
||
|
pipa->bCancelParentNotify = TRUE;
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
SendMessage(pipa->Children[i].hWnd, WM_SETTEXT,
|
||
|
0, (LPARAM) (LPSTR) TEXT(""));
|
||
|
}
|
||
|
pipa->bCancelParentNotify = FALSE;
|
||
|
SendMessage(pipa->hwndParent, WM_COMMAND,
|
||
|
MAKEWPARAM(GetDlgCtrlID(hWnd), 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 IPM_SETADDRESS:
|
||
|
{
|
||
|
pipa->bCancelParentNotify = TRUE;
|
||
|
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
BYTE bVal = HIBYTE(HIWORD(lParam));
|
||
|
if (pipa->Children[i].byLow <= bVal &&
|
||
|
bVal <= pipa->Children[i].byHigh) {
|
||
|
SetFieldValue(pipa, i, bVal);
|
||
|
|
||
|
} else {
|
||
|
lResult = FALSE;
|
||
|
}
|
||
|
|
||
|
lParam <<= 8;
|
||
|
}
|
||
|
|
||
|
pipa->bCancelParentNotify = FALSE;
|
||
|
|
||
|
SendMessage(pipa->hwndParent, WM_COMMAND,
|
||
|
MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IPM_SETRANGE:
|
||
|
if (wParam < NUM_FIELDS && LOBYTE(LOWORD(lParam)) <= HIBYTE(LOWORD(lParam)))
|
||
|
{
|
||
|
lResult = MAKEIPRANGE(pipa->Children[wParam].byLow, pipa->Children[wParam].byHigh);
|
||
|
pipa->Children[wParam].byLow = LOBYTE(LOWORD(lParam));
|
||
|
pipa->Children[wParam].byHigh = HIBYTE(LOWORD(lParam));
|
||
|
break;
|
||
|
}
|
||
|
lResult = 0;
|
||
|
break;
|
||
|
|
||
|
// Set the focus to this IPADDR.
|
||
|
// wParam = the field number to set focus to, or -1 to set the focus to the
|
||
|
// first non-blank field.
|
||
|
case IPM_SETFOCUS:
|
||
|
|
||
|
if (wParam >= NUM_FIELDS)
|
||
|
{
|
||
|
for (wParam = 0; wParam < NUM_FIELDS; ++wParam)
|
||
|
if (GetFieldValue(&(pipa->Children[wParam])) == -1) break;
|
||
|
if (wParam >= NUM_FIELDS) wParam = 0;
|
||
|
}
|
||
|
EnterField(&(pipa->Children[wParam]), 0, CHARS_PER_FIELD);
|
||
|
break;
|
||
|
|
||
|
// Determine whether all four subfields are blank
|
||
|
case IPM_ISBLANK:
|
||
|
|
||
|
lResult = TRUE;
|
||
|
for (i = 0; i < NUM_FIELDS; ++i)
|
||
|
{
|
||
|
if (GetFieldValue(&(pipa->Children[i])) != -1)
|
||
|
{
|
||
|
lResult = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
DoDefault:
|
||
|
lResult = DefWindowProc( hWnd, wMsg, wParam, lParam );
|
||
|
break;
|
||
|
}
|
||
|
return( lResult );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
IPAddressFieldProc() - Edit field window procedure
|
||
|
|
||
|
This function sub-classes each edit field.
|
||
|
*/
|
||
|
LRESULT IPAddressFieldProc(HWND hWnd,
|
||
|
UINT wMsg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam)
|
||
|
{
|
||
|
IPADDR *pipa;
|
||
|
FIELD *pField;
|
||
|
HWND hIPADDRWindow;
|
||
|
WORD wChildID;
|
||
|
LRESULT lresult;
|
||
|
|
||
|
if (!(hIPADDRWindow = GetParent(hWnd)))
|
||
|
return 0;
|
||
|
|
||
|
pipa = (IPADDR *)GET_IPADDR_HANDLE(hIPADDRWindow);
|
||
|
if (!pipa)
|
||
|
return 0;
|
||
|
|
||
|
wChildID = (WORD)GET_IPADDR_HANDLE(hWnd);
|
||
|
pField = &(pipa->Children[wChildID]);
|
||
|
|
||
|
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'))
|
||
|
{
|
||
|
LRESULT lResult;
|
||
|
|
||
|
lResult = CallWindowProc(pipa->Children[wChildID].lpfnWndProc,
|
||
|
hWnd, wMsg, wParam, lParam);
|
||
|
lResult = SendMessage(hWnd, EM_GETSEL, 0, 0L);
|
||
|
|
||
|
if (lResult == MAKELPARAM(CHARS_PER_FIELD, CHARS_PER_FIELD)
|
||
|
&& ExitField(pipa, wChildID)
|
||
|
&& wChildID < NUM_FIELDS-1)
|
||
|
{
|
||
|
EnterField(&(pipa->Children[wChildID+1]),
|
||
|
0, CHARS_PER_FIELD);
|
||
|
}
|
||
|
return lResult;
|
||
|
}
|
||
|
|
||
|
// spaces and periods fills out the current field and then if possible,
|
||
|
// goes to the next field.
|
||
|
else if (wParam == FILLER || wParam == SPACE )
|
||
|
{
|
||
|
LRESULT lResult;
|
||
|
lResult = SendMessage(hWnd, EM_GETSEL, 0, 0L);
|
||
|
if (lResult != 0L && HIWORD(lResult) == LOWORD(lResult)
|
||
|
&& ExitField(pipa, wChildID))
|
||
|
{
|
||
|
if (wChildID >= NUM_FIELDS-1)
|
||
|
MessageBeep((UINT)-1);
|
||
|
else
|
||
|
{
|
||
|
EnterField(&(pipa->Children[wChildID+1]),
|
||
|
0, CHARS_PER_FIELD);
|
||
|
}
|
||
|
}
|
||
|
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 (wChildID > 0 && SendMessage(hWnd, EM_GETSEL, 0, 0L) == 0L)
|
||
|
{
|
||
|
if (SwitchFields(pipa, wChildID, wChildID-1,
|
||
|
CHARS_PER_FIELD, CHARS_PER_FIELD)
|
||
|
&& SendMessage(pipa->Children[wChildID-1].hWnd,
|
||
|
EM_LINELENGTH, 0, 0L) != 0L)
|
||
|
{
|
||
|
SendMessage(pipa->Children[wChildID-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) && wChildID > 0)
|
||
|
{
|
||
|
SwitchFields(pipa, wChildID, wChildID-1,
|
||
|
0, CHARS_PER_FIELD);
|
||
|
return 0;
|
||
|
}
|
||
|
else if ((wParam == VK_RIGHT || wParam == VK_DOWN)
|
||
|
&& wChildID < NUM_FIELDS-1)
|
||
|
{
|
||
|
SwitchFields(pipa, wChildID, wChildID+1,
|
||
|
0, CHARS_PER_FIELD);
|
||
|
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
|
||
|
&& wChildID > 0)
|
||
|
{
|
||
|
SwitchFields(pipa, wChildID, wChildID-1,
|
||
|
CHARS_PER_FIELD, CHARS_PER_FIELD);
|
||
|
return 0;
|
||
|
}
|
||
|
else if ((wParam == VK_RIGHT || wParam == VK_DOWN)
|
||
|
&& wChildID < NUM_FIELDS-1)
|
||
|
{
|
||
|
dwResult = (DWORD)SendMessage(hWnd, EM_LINELENGTH, 0, 0L);
|
||
|
if (wStart >= dwResult)
|
||
|
{
|
||
|
SwitchFields(pipa, wChildID, wChildID+1, 0, 0);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// Home jumps back to the beginning of the first field.
|
||
|
case VK_HOME:
|
||
|
if (wChildID > 0)
|
||
|
{
|
||
|
SwitchFields(pipa, wChildID, 0, 0, 0);
|
||
|
return 0;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// End scoots to the end of the last field.
|
||
|
case VK_END:
|
||
|
if (wChildID < NUM_FIELDS-1)
|
||
|
{
|
||
|
SwitchFields(pipa, wChildID, NUM_FIELDS-1,
|
||
|
CHARS_PER_FIELD, CHARS_PER_FIELD);
|
||
|
return 0;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
|
||
|
} // switch (wParam)
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WM_KILLFOCUS:
|
||
|
if ( !ExitField( pipa, wChildID ))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
} // switch (wMsg)
|
||
|
|
||
|
lresult = CallWindowProc( pipa->Children[wChildID].lpfnWndProc,
|
||
|
hWnd, wMsg, wParam, lParam);
|
||
|
return lresult;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
Switch the focus from one field to another.
|
||
|
call
|
||
|
pipa = Pointer to the IPADDR 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(IPADDR *pipa, int iOld, int iNew, WORD wStart, WORD wEnd)
|
||
|
{
|
||
|
if (!ExitField(pipa, iOld)) return FALSE;
|
||
|
EnterField(&(pipa->Children[iNew]), wStart, wEnd);
|
||
|
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, WORD wStart, WORD wEnd)
|
||
|
{
|
||
|
SetFocus(pField->hWnd);
|
||
|
SendMessage(pField->hWnd, EM_SETSEL, wStart, wEnd);
|
||
|
}
|
||
|
|
||
|
void SetFieldValue(IPADDR *pipa, int iField, int iValue)
|
||
|
{
|
||
|
TCHAR szBuf[CHARS_PER_FIELD+1];
|
||
|
FIELD* pField = &(pipa->Children[iField]);
|
||
|
|
||
|
wsprintf(szBuf, TEXT("%d"), iValue);
|
||
|
SendMessage(pField->hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) szBuf);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Exit a field.
|
||
|
call
|
||
|
pipa = pointer to IPADDR structure.
|
||
|
iField = field number being exited.
|
||
|
returns
|
||
|
TRUE if the user may exit the field.
|
||
|
FALSE if he may not.
|
||
|
*/
|
||
|
BOOL ExitField(IPADDR *pipa, int iField)
|
||
|
{
|
||
|
FIELD *pField;
|
||
|
int i;
|
||
|
NMIPADDRESS nm;
|
||
|
int iOldValue;
|
||
|
|
||
|
pField = &(pipa->Children[iField]);
|
||
|
i = GetFieldValue(pField);
|
||
|
iOldValue = i;
|
||
|
|
||
|
nm.iField = iField;
|
||
|
nm.iValue = i;
|
||
|
|
||
|
SendNotifyEx(pipa->hwndParent, pipa->hwnd, IPN_FIELDCHANGED, &nm.hdr, FALSE);
|
||
|
i = nm.iValue;
|
||
|
|
||
|
if (i != -1) {
|
||
|
|
||
|
if (i < (int)(UINT)pField->byLow || i > (int)(UINT)pField->byHigh)
|
||
|
{
|
||
|
|
||
|
if ( i < (int)(UINT) pField->byLow )
|
||
|
{
|
||
|
/* too small */
|
||
|
i = (int)(UINT)pField->byLow;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* must be bigger */
|
||
|
i = (int)(UINT)pField->byHigh;
|
||
|
}
|
||
|
SetFieldValue(pipa, iField, i);
|
||
|
// FEATURE: send notify up
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (iOldValue != i) {
|
||
|
SetFieldValue(pipa, iField, i);
|
||
|
}
|
||
|
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.
|
||
|
*/
|
||
|
int GetFieldValue(FIELD *pField)
|
||
|
{
|
||
|
WORD wLength;
|
||
|
TCHAR szBuf[CHARS_PER_FIELD+1];
|
||
|
INT i;
|
||
|
|
||
|
*(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');
|
||
|
i = StrToInt(szBuf);
|
||
|
return i;
|
||
|
}
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL IsDBCS()
|
||
|
{
|
||
|
LANGID langid;
|
||
|
langid = PRIMARYLANGID(GetThreadLocale());
|
||
|
if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
|
||
|
{
|
||
|
return (TRUE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (FALSE);
|
||
|
}
|
||
|
}
|