/* * IP Address control * * Copyright 1998, 1999 Eric Kohl * Copyright 1998 Alex Priem * * NOTES * * TODO: * -Check ranges when changing field-focus. * -Check all notifications/behavior. * -Optimization: include lpipsi in IPADDRESS_INFO. * -CurrentFocus: field that has focus at moment of processing. * -connect Rect32 rcClient. * -handle right and left arrows correctly. Boring. * -split GotoNextField in CheckField and GotoNextField. * -check ipaddress.cpp for missing features. * -refresh: draw '.' instead of setpixel. * -handle VK_ESCAPE. */ #include #include #include "winbase.h" #include "commctrl.h" #include "ipaddress.h" #include "heap.h" #include "debug.h" DEFAULT_DEBUG_CHANNEL(ipaddress) #define IPADDRESS_GetInfoPtr(hwnd) ((IPADDRESS_INFO *)GetWindowLongA (hwnd, 0)) static BOOL IPADDRESS_SendNotify (HWND hwnd, UINT command); static BOOL IPADDRESS_SendIPAddressNotify (HWND hwnd, UINT field, BYTE newValue); /* property name of tooltip window handle */ #define IP_SUBCLASS_PROP "CCIP32SubclassInfo" static LRESULT CALLBACK IPADDRESS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static VOID IPADDRESS_Refresh (HWND hwnd, HDC hdc) { RECT rcClient; HBRUSH hbr; COLORREF clr=GetSysColor (COLOR_3DDKSHADOW); int i,x,fieldsize; GetClientRect (hwnd, &rcClient); hbr = CreateSolidBrush (RGB(255,255,255)); DrawEdge (hdc, &rcClient, EDGE_SUNKEN, BF_RECT | BF_ADJUST); FillRect (hdc, &rcClient, hbr); DeleteObject (hbr); x=rcClient.left; fieldsize=(rcClient.right-rcClient.left) /4; for (i=0; i<3; i++) { /* Should draw text "." here */ x+=fieldsize; SetPixel (hdc, x, 13, clr); SetPixel (hdc, x, 14, clr); SetPixel (hdc, x+1, 13, clr); SetPixel (hdc, x+1, 14, clr); } } static LRESULT IPADDRESS_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) { IPADDRESS_INFO *infoPtr; RECT rcClient, edit; int i,fieldsize; LPIP_SUBCLASS_INFO lpipsi; infoPtr = (IPADDRESS_INFO *)COMCTL32_Alloc (sizeof(IPADDRESS_INFO)); SetWindowLongA (hwnd, 0, (DWORD)infoPtr); GetClientRect (hwnd, &rcClient); fieldsize=(rcClient.right-rcClient.left) /4; edit.top =rcClient.top+2; edit.bottom=rcClient.bottom-2; lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP); if (lpipsi == NULL) { lpipsi= (LPIP_SUBCLASS_INFO) COMCTL32_Alloc (sizeof(IP_SUBCLASS_INFO)); lpipsi->hwnd = hwnd; lpipsi->uRefCount++; SetPropA ((HWND)hwnd, IP_SUBCLASS_PROP, (HANDLE)lpipsi); /* infoPtr->lpipsi= lpipsi; */ } else WARN (ipaddress,"IP-create called twice\n"); for (i=0; i<=3; i++) { infoPtr->LowerLimit[i]=0; infoPtr->UpperLimit[i]=255; edit.left=rcClient.left+i*fieldsize+3; edit.right=rcClient.left+(i+1)*fieldsize-2; lpipsi->hwndIP[i]= CreateWindowA ("edit", NULL, WS_CHILD | WS_VISIBLE | ES_LEFT, edit.left, edit.top, edit.right-edit.left, edit.bottom-edit.top, hwnd, (HMENU) 1, GetWindowLongA (hwnd, GWL_HINSTANCE), NULL); lpipsi->wpOrigProc[i]= (WNDPROC) SetWindowLongA (lpipsi->hwndIP[i],GWL_WNDPROC, (LONG) IPADDRESS_SubclassProc); SetPropA ((HWND)lpipsi->hwndIP[i], IP_SUBCLASS_PROP, (HANDLE)lpipsi); } lpipsi->infoPtr= infoPtr; return 0; } static LRESULT IPADDRESS_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam) { int i; IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP); for (i=0; i<=3; i++) { SetWindowLongA ((HWND)lpipsi->hwndIP[i], GWL_WNDPROC, (LONG)lpipsi->wpOrigProc[i]); } COMCTL32_Free (infoPtr); return 0; } static LRESULT IPADDRESS_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam) { HDC hdc; TRACE (ipaddress,"\n"); hdc = GetDC (hwnd); IPADDRESS_Refresh (hwnd, hdc); ReleaseDC (hwnd, hdc); IPADDRESS_SendIPAddressNotify (hwnd, 0, 0); /* FIXME: should use -1 */ IPADDRESS_SendNotify (hwnd, EN_KILLFOCUS); InvalidateRect (hwnd, NULL, TRUE); return 0; } static LRESULT IPADDRESS_Paint (HWND hwnd, WPARAM wParam) { HDC hdc; PAINTSTRUCT ps; hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam; IPADDRESS_Refresh (hwnd, hdc); if(!wParam) EndPaint (hwnd, &ps); return 0; } static LRESULT IPADDRESS_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam) { HDC hdc; TRACE (ipaddress,"\n"); hdc = GetDC (hwnd); IPADDRESS_Refresh (hwnd, hdc); ReleaseDC (hwnd, hdc); return 0; } static LRESULT IPADDRESS_Size (HWND hwnd, WPARAM wParam, LPARAM lParam) { /* IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); */ TRACE (ipaddress,"\n"); return 0; } static BOOL IPADDRESS_SendNotify (HWND hwnd, UINT command) { TRACE (ipaddress, "%x\n",command); return (BOOL)SendMessageA (GetParent (hwnd), WM_COMMAND, MAKEWPARAM (GetWindowLongA (hwnd, GWL_ID),command), (LPARAM)hwnd); } static BOOL IPADDRESS_SendIPAddressNotify (HWND hwnd, UINT field, BYTE newValue) { NMIPADDRESS nmip; TRACE (ipaddress, "%x %x\n",field,newValue); nmip.hdr.hwndFrom = hwnd; nmip.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID); nmip.hdr.code = IPN_FIELDCHANGED; nmip.iField=field; nmip.iValue=newValue; return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY, (WPARAM)nmip.hdr.idFrom, (LPARAM)&nmip); } static LRESULT IPADDRESS_ClearAddress (HWND hwnd, WPARAM wParam, LPARAM lParam) { int i; HDC hdc; char buf[1]; LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd,IP_SUBCLASS_PROP); TRACE (ipaddress,"\n"); buf[0]=0; for (i=0; i<=3; i++) SetWindowTextA (lpipsi->hwndIP[i],buf); hdc = GetDC (hwnd); IPADDRESS_Refresh (hwnd, hdc); ReleaseDC (hwnd, hdc); return 0; } static LRESULT IPADDRESS_IsBlank (HWND hwnd, WPARAM wParam, LPARAM lParam) { int i; char buf[20]; LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP); TRACE (ipaddress,"\n"); for (i=0; i<=3; i++) { GetWindowTextA (lpipsi->hwndIP[i],buf,5); if (buf[0]) return 0; } return 1; } static LRESULT IPADDRESS_GetAddress (HWND hwnd, WPARAM wParam, LPARAM lParam) { char field[20]; int i,valid,fieldvalue; DWORD ip_addr; IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP); TRACE (ipaddress,"\n"); valid=0; ip_addr=0; for (i=0; i<=3; i++) { GetWindowTextA (lpipsi->hwndIP[i],field,4); ip_addr*=256; if (field[0]) { field[3]=0; fieldvalue=atoi(field); if (fieldvalueLowerLimit[i]) fieldvalue=infoPtr->LowerLimit[i]; if (fieldvalue>infoPtr->UpperLimit[i]) fieldvalue=infoPtr->UpperLimit[i]; ip_addr+=fieldvalue; valid++; } } *(LPDWORD) lParam=ip_addr; return valid; } static LRESULT IPADDRESS_SetRange (HWND hwnd, WPARAM wParam, LPARAM lParam) { IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); INT index; TRACE (ipaddress,"\n"); index=(INT) wParam; if ((index<0) || (index>3)) return 0; infoPtr->LowerLimit[index]=lParam & 0xff; infoPtr->UpperLimit[index]=(lParam >>8) & 0xff; return 1; } static LRESULT IPADDRESS_SetAddress (HWND hwnd, WPARAM wParam, LPARAM lParam) { IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); HDC hdc; LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP); int i,ip_address,value; char buf[20]; TRACE (ipaddress,"\n"); ip_address=(DWORD) lParam; for (i=3; i>=0; i--) { value=ip_address & 0xff; if ((value>=infoPtr->LowerLimit[i]) && (value<=infoPtr->UpperLimit[i])) { sprintf (buf,"%d",value); SetWindowTextA (lpipsi->hwndIP[i],buf); IPADDRESS_SendNotify (hwnd, EN_CHANGE); } ip_address/=256; } hdc = GetDC (hwnd); /* & send notifications */ IPADDRESS_Refresh (hwnd, hdc); ReleaseDC (hwnd, hdc); return TRUE; } static LRESULT IPADDRESS_SetFocusToField (HWND hwnd, WPARAM wParam, LPARAM lParam) { /* IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); */ LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP); INT index; index=(INT) wParam; TRACE (ipaddress," %d\n", index); if ((index<0) || (index>3)) return 0; SetFocus (lpipsi->hwndIP[index]); return 1; } static LRESULT IPADDRESS_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam) { TRACE (ipaddress, "\n"); SetFocus (hwnd); IPADDRESS_SendNotify (hwnd, EN_SETFOCUS); IPADDRESS_SetFocusToField (hwnd, 0, 0); return TRUE; } /* tab/shift-tab: IPN_FIELDCHANGED, lose focus. dot, space,right arrow: set focus to next child edit. numerics (0..9), control characters: forward to default edit control other characters: dropped */ static int IPADDRESS_GotoNextField (LPIP_SUBCLASS_INFO lpipsi, int currentfield) { int newField,fieldvalue; char field[20]; IPADDRESS_INFO *infoPtr=lpipsi->infoPtr; TRACE (ipaddress,"\n"); GetWindowTextA (lpipsi->hwndIP[currentfield],field,4); if (field[0]) { field[3]=0; newField=-1; fieldvalue=atoi(field); if (fieldvalueLowerLimit[currentfield]) newField=infoPtr->LowerLimit[currentfield]; if (fieldvalue>infoPtr->UpperLimit[currentfield]) newField=infoPtr->UpperLimit[currentfield]; if (newField>=0) { sprintf (field,"%d",newField); SetWindowTextA (lpipsi->hwndIP[currentfield], field); return 1; } } if (currentfield<3) SetFocus (lpipsi->hwndIP[currentfield+1]); return 0; } LRESULT CALLBACK IPADDRESS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int i,l,index; IPADDRESS_INFO *infoPtr; LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO) GetPropA ((HWND)hwnd,IP_SUBCLASS_PROP); infoPtr = lpipsi->infoPtr; index=0; /* FIXME */ for (i=0; i<=3; i++) if (lpipsi->hwndIP[i]==hwnd) index=i; switch (uMsg) { case WM_CHAR: break; case WM_KEYDOWN: { char c=(char) wParam; if (c==VK_TAB) { HWND pwnd; int shift; shift = GetKeyState(VK_SHIFT) & 0x8000; if (shift) pwnd=GetNextDlgTabItem (GetParent (hwnd), 0, TRUE); else pwnd=GetNextDlgTabItem (GetParent (hwnd), 0, FALSE); if (pwnd) SetFocus (pwnd); break; } if ((c==' ') || (c=='.') || (c==VK_RIGHT)) { IPADDRESS_GotoNextField (lpipsi,index); wParam=0; lParam=0; break; } if (c==VK_LEFT) { } if (c==VK_RETURN) { } if (((c>='0') && (c<='9')) || (iscntrl(c))) { l=GetWindowTextLengthA (lpipsi->hwndIP[index]); if (l==3) if (IPADDRESS_GotoNextField (lpipsi,index)) { wParam=0; lParam=0; } break; } wParam=0; lParam=0; break; } } return CallWindowProcA (lpipsi->wpOrigProc[index], hwnd, uMsg, wParam, lParam); } LRESULT WINAPI IPADDRESS_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case IPM_CLEARADDRESS: return IPADDRESS_ClearAddress (hwnd, wParam, lParam); case IPM_SETADDRESS: return IPADDRESS_SetAddress (hwnd, wParam, lParam); case IPM_GETADDRESS: return IPADDRESS_GetAddress (hwnd, wParam, lParam); case IPM_SETRANGE: return IPADDRESS_SetRange (hwnd, wParam, lParam); case IPM_SETFOCUS: return IPADDRESS_SetFocusToField (hwnd, wParam, lParam); case IPM_ISBLANK: return IPADDRESS_IsBlank (hwnd, wParam, lParam); case WM_CREATE: return IPADDRESS_Create (hwnd, wParam, lParam); case WM_DESTROY: return IPADDRESS_Destroy (hwnd, wParam, lParam); case WM_GETDLGCODE: return DLGC_WANTARROWS | DLGC_WANTCHARS; case WM_KILLFOCUS: return IPADDRESS_KillFocus (hwnd, wParam, lParam); case WM_LBUTTONDOWN: return IPADDRESS_LButtonDown (hwnd, wParam, lParam); case WM_PAINT: return IPADDRESS_Paint (hwnd, wParam); case WM_SETFOCUS: return IPADDRESS_SetFocus (hwnd, wParam, lParam); case WM_SIZE: return IPADDRESS_Size (hwnd, wParam, lParam); default: if (uMsg >= WM_USER) ERR (ipaddress, "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam); return DefWindowProcA (hwnd, uMsg, wParam, lParam); } return 0; } void IPADDRESS_Register (void) { WNDCLASSA wndClass; if (GlobalFindAtomA (WC_IPADDRESSA)) return; ZeroMemory (&wndClass, sizeof(WNDCLASSA)); wndClass.style = CS_GLOBALCLASS; wndClass.lpfnWndProc = (WNDPROC)IPADDRESS_WindowProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = sizeof(IPADDRESS_INFO *); wndClass.hCursor = LoadCursorA (0, IDC_ARROWA); wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wndClass.lpszClassName = WC_IPADDRESSA; RegisterClassA (&wndClass); } VOID IPADDRESS_Unregister (VOID) { if (GlobalFindAtomA (WC_IPADDRESSA)) UnregisterClassA (WC_IPADDRESSA, (HINSTANCE)NULL); }