/* Copyright (c) 1991, Microsoft Corporation, all rights reserved

    ipaddr.c - TCP/IP Address custom control

    November 9, 1992    Greg Strange
*/


#include <windows.h>
#include <stdlib.h>
//#include <lmuitype.h>  <- This defines VOID which causes SHORT to be multiply defined which breaks Mips
#ifdef IPDLL
#include <custcntl.h>
#endif
#include "lmuidbcs.h"
#include "ipaddr.h"             // Global IPAddress definitions
#include "ipadd.h"              // Internal IPAddress definitions

/* global static variables */
HANDLE           hLibInstance = NULL;
#ifdef IPDLL
HANDLE           hLibData;
LPFNSTRTOID      lpfnVerId;
LPFNIDTOSTR      lpfnIdStr;
#endif

/*
    Strings loaded at initialization.
*/
TCHAR szNoMem[MAX_IPNOMEMSTRING];       // Out of memory string
TCHAR szCaption[MAX_IPCAPTION];         // Alert message box caption

#define IPADDRESS_CLASS            TEXT("RassetupIPAddress")


// 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;
    FARPROC     lpfnWndProc;
    BYTE        byLow;  // lowest allowed value for this field.
    BYTE        byHigh; // Highest allowed value for this field.
} FIELD;

typedef struct tagCONTROL {
    HWND        hwndParent;
    UINT        uiFieldWidth;
    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       Children[NUM_FIELDS];
} CONTROL;


// The following macros extract and store the CONTROL structure for a control.
#define    IPADDRESS_EXTRA            sizeof(DWORD)

#define GET_CONTROL_HANDLE(hWnd)        ((HGLOBAL)(GetWindowLong((hWnd), GWL_USERDATA)))
#define SAVE_CONTROL_HANDLE(hWnd,x)     (SetWindowLong((hWnd), GWL_USERDATA, (LONG)x))


/* internal IPAddress function prototypes */
#ifdef IPDLL
BOOL FAR PASCAL IPAddressDlgFn( HWND, WORD, WORD, LONG );
void GetStyleBit(HWND, LPCTLSTYLE, int, DWORD);
#endif
LONG FAR PASCAL IPAddressWndFn( HWND, UINT, WPARAM, LPARAM );
LONG FAR PASCAL IPAddressFieldProc(HWND, UINT, WPARAM, LPARAM);
BOOL SwitchFields(CONTROL FAR *, int, int, WORD, WORD);
void EnterField(FIELD FAR *, WORD, WORD);
BOOL ExitField(CONTROL FAR *, int iField);
int GetFieldValue(FIELD FAR *);
int FAR CDECL IPAlertPrintf(HWND hwndParent, UINT ids, int iCurrent, int iLow, int iHigh);
BOOL IPLoadOem(HINSTANCE hInst, UINT idResource, TCHAR* lpszBuffer, int cchBuffer);



/*
    LibMain() - Called once before anything else.

    call
        hInstance = library instance handle
        wDataSegment = library data segment
        wHeapSize = default heap size
        lpszCmdLine = command line arguements

    When this file is compiled as a DLL, this function is called by Libentry()
    when the library is first loaded.  See the SDK docs for details.
*/
#ifdef IPDLL
int FAR PASCAL LibMain(
   HANDLE      hInstance,
   WORD        wDataSegment,
   WORD        wHeapSize,
   LPSTR       lpszCmdLine )
{
    return IPAddrInit(hInstance);
}
#endif



LOGFONT logfont;

void SetDefaultFont( )
{
    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;

    dc = GetDC(NULL);
    if (NETUI_IsDBCS())
    {
        logfont.lfHeight           = -(10*GetDeviceCaps(dc,LOGPIXELSY)/72);
        logfont.lfCharSet          = SHIFTJIS_CHARSET;
        lstrcpy( logfont.lfFaceName,TEXT("‚l‚r ƒSƒVƒbƒN"));
    }
    else
    {
        logfont.lfHeight           = -(8*GetDeviceCaps(dc,LOGPIXELSY)/72);
        logfont.lfCharSet          = ANSI_CHARSET;
        lstrcpy( logfont.lfFaceName,TEXT("MS Shell Dlg"));
    }

    ReleaseDC(NULL, dc);
}



/*
    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 FAR PASCAL IPAddrInit(HANDLE hInstance)
{
    LPWNDCLASS        lpClassStruct;

    /* register IPAddress window if necessary */
    if (hLibInstance == NULL)
    {
        /* allocate memory for class structure */
        lpClassStruct = (LPWNDCLASS)GlobalAlloc(GPTR, (DWORD)sizeof(WNDCLASS));
        if (lpClassStruct)
        {
            /* define class attributes */
            lpClassStruct->lpszClassName = IPADDRESS_CLASS;
            lpClassStruct->hCursor =       LoadCursor(NULL,IDC_IBEAM);
            lpClassStruct->lpszMenuName =  (LPCTSTR)NULL;
            lpClassStruct->style =         CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS|CS_GLOBALCLASS;
            lpClassStruct->lpfnWndProc =   IPAddressWndFn;
            lpClassStruct->hInstance =     hInstance;
            lpClassStruct->hIcon =         NULL;
            lpClassStruct->cbWndExtra =    IPADDRESS_EXTRA;
            lpClassStruct->hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 );

            /* register IPAddress window class */
            hLibInstance = (RegisterClass(lpClassStruct)) ? hInstance : NULL;

            if (hLibInstance)
            {
                /* Load caption and out of memory string before we're
                   out of memory. */
                if (!IPLoadOem(hLibInstance, IDS_IPNOMEM, szNoMem,
                            sizeof(szNoMem)/sizeof(TCHAR))
                    || !IPLoadOem(hLibInstance, IDS_IPMBCAPTION, szCaption,
                            sizeof(szCaption)/sizeof(TCHAR)))
                    return FALSE;
            }
            GlobalFree(lpClassStruct);
        }
    }

    SetDefaultFont();
    return(hLibInstance? 1:0);
}

/*
void FAR PASCAL IPAddrUnInit(HANDLE hInstance)
    call
        hInstance = library or application instance
    return
        None

    This function deregisters the window class registered by IPAddrInit
*/
void FAR PASCAL IPAddrUnInit(HANDLE hInstance)
{
    UnregisterClass( IPADDRESS_CLASS, hInstance);
}

/*
    WEP - Last function called before DLL is unloaded.
*/
#ifdef IPDLL

VOID FAR PASCAL WEP (bSystemExit)
int  bSystemExit;
{
    return;
}

#endif


/*
    IPAddressInfo() - Returns various bits of information about the control.

    returns
        A handle for a CtlInfo structure.

    This function is only included in the DLL and is used by the dialog
    editor.
*/
#ifdef IPDLL
HANDLE FAR PASCAL IPAddressInfo()
{
    LPCTLINFO    lpCtlInfo;

    /* allocate space for information structure */
    lpCtlInfo = (LPCTLINFO)GlobalAlloc(GPTR, (DWORD)sizeof(CTLINFO));
    if (lpCtlInfo)
    {
        /* define the fixed portion of the structure */
        lpCtlInfo->wVersion = 100;
        lpCtlInfo->wCtlTypes = 1;
        lstrcpy( lpCtlInfo->szClass, IPADDRESS_CLASS );
        lstrcpy( lpCtlInfo->szTitle, TEXT("TCP/IP IP Address") );

        /* define the variable portion of the structure */
        lpCtlInfo->Type[0].wWidth = NUM_FIELDS*(CHARS_PER_FIELD+1) * 4 + 4;
        lpCtlInfo->Type[0].wHeight = 13;
        lpCtlInfo->Type[0].dwStyle = WS_CHILD | WS_TABSTOP;
        lstrcpy( lpCtlInfo->Type[0].szDescr, TEXT("IPAddress") );
        }
    }

    /* return result */
    return(hCtlInfo);
}
#endif


/*
    IPAddressStyle()
    call
        hWnd            handle of parent window
        hCtlStyle       handle to control style info
        lpfnVerifyId    pointer to the VerifyId function from dialog editor
        lpfnGetIDStr    pointer to the GetIdStr function from dialog editor

    This function is called by the dialog editor when the user double clicks
    on the custom control.  Or when the user chooses to edit the control's
    styles.
*/
#ifdef IPDLL
BOOL FAR PASCAL IPAddressStyle(
    HWND        hWnd,
    HANDLE      hCtlStyle,
    LPFNSTRTOID    lpfnVerifyId,
    LPFNIDTOSTR    lpfnGetIdStr )
{
    FARPROC       lpDlgFn;
    HANDLE        hNewCtlStyle;

    /* initialization */
    hLibData = hCtlStyle;
    lpfnVerId = lpfnVerifyId;
    lpfnIdStr = lpfnGetIdStr;

    /* display dialog box */
    lpDlgFn = MakeProcInstance( (FARPROC)IPAddressDlgFn, hLibInstance );
    hNewCtlStyle = ( DialogBox(hLibInstance,TEXT("IPAddressStyle"),hWnd,lpDlgFn) ) ? hLibData : NULL;
    FreeProcInstance( lpDlgFn );

    /* return updated data block */
    return( hNewCtlStyle );
}
#endif




/*
    IPAddressDlgFn() - Dialog editor style dialog

    hDlg        styles dialog box handle
    wMessage    window message
    wParam      word parameter
    lParam      long parameter

    This is the dialog function for the styles dialog that is displayed when
    the user wants to edit an IPAddress control's style from the dialog editor.
*/
#ifdef IPDLL
BOOL FAR PASCAL IPAddressDlgFn(
    HWND        hDlg,
    WORD        wMessage,
    WORD        wParam,
    LONG        lParam )
{
    BOOL            bResult;

    /* initialization */
    bResult = TRUE;

    /* process message */
    switch( wMessage )
    {
    case WM_INITDIALOG :
        {
            HANDLE        hCtlStyle;
            LPCTLSTYLE    lpCtlStyle;

            /* disable Ok button & save dialog data handle */
            hCtlStyle = hLibData;

            /* retrieve & display style parameters */
            if ( hCtlStyle ) {

                /* add handle to property list */
                SetProp( hDlg, MAKEINTRESOURCE(1), hCtlStyle );

                /* update dialog box fields */
                lpCtlStyle = (LPCTLSTYLE)GlobalLock( hCtlStyle );

                lstrcpy( lpCtlStyle->szClass, IPADDRESS_CLASS );
                SendDlgItemMessage(hDlg, ID_VISIBLE, BM_SETCHECK,
                        (WPARAM)((lpCtlStyle->dwStyle & WS_VISIBLE) != 0), 0L);
                SendDlgItemMessage(hDlg, ID_GROUP, BM_SETCHECK,
                        (WPARAM)((lpCtlStyle->dwStyle & WS_GROUP) != 0), 0L);
                SendDlgItemMessage(hDlg, ID_DISABLED, BM_SETCHECK,
                        (WPARAM)((lpCtlStyle->dwStyle & WS_DISABLED) != 0), 0L);
                SendDlgItemMessage(hDlg, ID_TABSTOP, BM_SETCHECK,
                        (WPARAM)((lpCtlStyle->dwStyle & WS_TABSTOP) != 0), 0L);
                GlobalUnlock( hCtlStyle );

            } else
                EndDialog( hDlg, FALSE );
        }
        break;

    case WM_COMMAND :

        switch( wParam )
        {
        case IDCANCEL:
            RemoveProp( hDlg, MAKEINTRESOURCE(1) );
            EndDialog( hDlg, FALSE );
            break;

        case IDOK:
            {
                HANDLE        hCtlStyle;
                LPCTLSTYLE    lpCtlStyle;

                hCtlStyle = GetProp( hDlg, MAKEINTRESOURCE(1) );
                lpCtlStyle = (LPCTLSTYLE)GlobalLock( hCtlStyle );

                GetStyleBit(hDlg, lpCtlStyle, ID_VISIBLE,  WS_VISIBLE);
                GetStyleBit(hDlg, lpCtlStyle, ID_DISABLED, WS_DISABLED);
                GetStyleBit(hDlg, lpCtlStyle, ID_GROUP,    WS_GROUP);
                GetStyleBit(hDlg, lpCtlStyle, ID_TABSTOP,  WS_TABSTOP);

                GlobalUnlock( hCtlStyle );

                RemoveProp( hDlg, MAKEINTRESOURCE(1) );

                hLibData = hCtlStyle;
                EndDialog( hDlg, TRUE );
            }
            break;

        default :
            bResult = FALSE;
            break;
        }
        break;

    default :
        bResult = FALSE;
        break;
    }
    return( bResult );
}
#endif




/*
    Get the value of a check box and set the appropriate style bit.
*/
#ifdef IPDLL
void GetStyleBit(HWND hDlg, LPCTLSTYLE lpCtlStyle, int iControl, DWORD dwStyle)
{
    if (SendDlgItemMessage(hDlg, iControl, BM_GETSTATE, 0, 0L))
        lpCtlStyle->dwStyle |= dwStyle;
    else
        lpCtlStyle->dwStyle &= ~dwStyle;
}
#endif


/*
    IPAddressFlags()

    call
        wFlags          class style flags
        lpszString      class style string
        wMaxString      maximum size of class style string

  This function translates the class style flags provided into a
  corresponding text string for output to an RC file.  The general
  windows flags (contained in the low byte) are not interpreted,
  only those in the high byte.

  The value returned by this function is the library instance
  handle when sucessful, and NULL otherwise.
*/
#ifdef IPDLL
WORD FAR PASCAL IPAddressFlags(
    WORD        wFlags,
    LPSTR       lpszString,
    WORD        wMaxString )
{
    lpszString[0] = NULL;
    return( 0 );
}
#endif

/*
    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[CHARS_PER_FIELD+1];
    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'));
        }
    }
}

LONG FAR PASCAL IPAddressWndFn( hWnd, wMsg, wParam, lParam )
    HWND            hWnd;
    UINT            wMsg;
    WPARAM            wParam;
    LPARAM            lParam;
{
    LONG lResult;
    CONTROL *pControl;
    int 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 < NUM_FIELDS; ++i)
            {
                if (pszString[0] == 0)
                {
                    szBuf[0] = 0;
                }
                else
                {
                    wsprintf(szBuf, TEXT("%d"), dwValue[i]);
                }
                SendMessage(pControl->Children[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 < NUM_FIELDS; ++i)
            {
                iFieldValue = GetFieldValue(&(pControl->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_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;
            UINT uiFieldStart;
            FARPROC lpfnFieldProc;

            pControl = (CONTROL*)GlobalAlloc(GMEM_FIXED, sizeof(CONTROL));
            if (pControl)
            {
                HFONT OldFont;
                RECT rect;

                #define LPCS    ((CREATESTRUCT *)lParam)

                pControl->fEnabled = TRUE;
                pControl->fPainted = FALSE;
                pControl->fInMessageBox = FALSE;
                pControl->hwndParent = LPCS->hwndParent;
                pControl->bControlInFocus = FALSE;
                pControl->bCancelParentNotify = FALSE;


                hdc = GetDC(hWnd);
                GetClientRect(hWnd, &rect);

                OldFont = SelectObject(hdc, CreateFontIndirect(&logfont));
                GetCharWidth(hdc, FILLER, FILLER,
                                        (int *)(&pControl->uiFillerWidth));
                DeleteObject(SelectObject(hdc, OldFont ));
                ReleaseDC(hWnd, hdc);

                pControl->uiFieldWidth = (LPCS->cx
                                          - LEAD_ROOM
                                          - pControl->uiFillerWidth
                                              *(NUM_FIELDS-1))
                                                  / NUM_FIELDS;
                uiFieldStart = LEAD_ROOM;

                lpfnFieldProc = MakeProcInstance((FARPROC)IPAddressFieldProc,
                                                 LPCS->hInstance);

                for (i = 0; i < NUM_FIELDS; ++i)
                {
                    pControl->Children[i].byLow = MIN_FIELD_VALUE;
                    pControl->Children[i].byHigh = MAX_FIELD_VALUE;

                    pControl->Children[i].hWnd = CreateWindowEx(0,
                                        TEXT("Edit"),
                                        NULL,
                                        WS_CHILD | WS_VISIBLE |
                                        ES_CENTER,
                                        uiFieldStart,
                                        HEAD_ROOM,
                                        pControl->uiFieldWidth,
                                        (rect.bottom-rect.top),
                                        hWnd,
                                        (HMENU)i,
                                        LPCS->hInstance,
                                        (LPVOID)NULL);

                    SendMessage(pControl->Children[i].hWnd, EM_LIMITTEXT,
                                CHARS_PER_FIELD, 0L);

                    SendMessage(pControl->Children[i].hWnd, WM_SETFONT,
                                (WPARAM)CreateFontIndirect(&logfont), TRUE);

                    pControl->Children[i].lpfnWndProc =
                        (FARPROC) GetWindowLong(pControl->Children[i].hWnd,
                                                GWL_WNDPROC);

                    SetWindowLong(pControl->Children[i].hWnd,
                                  GWL_WNDPROC, (LONG)lpfnFieldProc);

                    uiFieldStart += pControl->uiFieldWidth
                                    + pControl->uiFillerWidth;
                }

                SAVE_CONTROL_HANDLE(hWnd, pControl);

                #undef LPCS
            }
            else
                DestroyWindow(hWnd);
        }
        lResult = 0;
        break;

    case WM_PAINT: /* paint control window */
        {
            PAINTSTRUCT Ps;
            RECT rect;
            UINT uiFieldStart;
            COLORREF TextColor;
            COLORREF cRef;
            HFONT OldFont;
            HBRUSH hbr;

            BeginPaint(hWnd, (LPPAINTSTRUCT)&Ps);
            OldFont = SelectObject( Ps.hdc, CreateFontIndirect(&logfont));
            GetClientRect(hWnd, &rect);
            pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);

            if (pControl->fEnabled)
            {
                TextColor = GetSysColor(COLOR_WINDOWTEXT);
                cRef = GetSysColor(COLOR_WINDOW);
                SetBkColor(Ps.hdc, cRef);

            }
            else
            {
                TextColor = GetSysColor(COLOR_GRAYTEXT);
                cRef = GetSysColor(COLOR_3DFACE);
                SetBkColor(Ps.hdc, cRef);
            }

            if (TextColor)
                SetTextColor(Ps.hdc, TextColor);


            hbr = CreateSolidBrush(cRef);
            FillRect(Ps.hdc, &rect, hbr);
            DeleteObject(hbr);

            SetRect(&rect, 0, HEAD_ROOM, pControl->uiFillerWidth, (rect.bottom-rect.top));
            ExtTextOut(Ps.hdc, rect.left, HEAD_ROOM, ETO_OPAQUE, &rect, L" ", 1, NULL);

            for (i = 0; i < NUM_FIELDS-1; ++i)
            {
                rect.left += pControl->uiFieldWidth + pControl->uiFillerWidth;
                rect.right += rect.left + pControl->uiFillerWidth;
                ExtTextOut(Ps.hdc, rect.left, HEAD_ROOM, ETO_OPAQUE, &rect, SZFILLER, 1, NULL);
            }

            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);
        EnterField(&(pControl->Children[0]), 0, CHARS_PER_FIELD);
        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 < NUM_FIELDS; ++i)
            {
                EnableWindow(pControl->Children[i].hWnd, (BOOL)wParam);
            }
            if (pControl->fPainted)
                InvalidateRect(hWnd, NULL, FALSE);
        }
        break;

    case WM_DESTROY :
        pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);

        // Restore all the child window procedures before we delete our memory block.
        for (i = 0; i < NUM_FIELDS; ++i)
        {
            SendMessage(pControl->Children[i].hWnd, WM_DESTROY, 0, 0);
            SetWindowLong(pControl->Children[i].hWnd, GWL_WNDPROC,
                          (LONG)pControl->Children[i].lpfnWndProc);
        }

        GlobalFree(pControl);
        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 < NUM_FIELDS; ++i)
                        if (pControl->Children[i].hWnd == hFocus)
                            break;

                    if (i >= NUM_FIELDS)
                    {
                        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 < NUM_FIELDS; ++i)
                        if (pControl->Children[i].hWnd == hFocus)
                            break;

                    // send a focus message when the
                    if (i < NUM_FIELDS && 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 IP Address.  The address is placed in the DWORD pointed
// to by lParam and the number of non-blank fields is returned.
    case IP_GETADDRESS:
        {
            int iFieldValue;
            DWORD dwValue;
            pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);

            lResult = 0;
            dwValue = 0;
            for (i = 0; i < NUM_FIELDS; ++i)
            {
                iFieldValue = GetFieldValue(&(pControl->Children[i]));
                if (iFieldValue == -1)
                    iFieldValue = 0;
                else
                    ++lResult;
                dwValue = (dwValue << 8) + iFieldValue;
            }
            *((DWORD *)lParam) = dwValue;
        }
        break;

// Clear all fields to blanks.
    case IP_CLEARADDRESS:
        {
            pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);
            pControl->bCancelParentNotify = TRUE;
            for (i = 0; i < NUM_FIELDS; ++i)
            {
                SendMessage(pControl->Children[i].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 IP_SETADDRESS:
        {
            static TCHAR szBuf[CHARS_PER_FIELD+1];
            pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);
            pControl->bCancelParentNotify = TRUE;

            for (i = 0; i < NUM_FIELDS; ++i)
            {
                wsprintf(szBuf, TEXT("%d"), HIBYTE(HIWORD(lParam)));
                SendMessage(pControl->Children[i].hWnd, WM_SETTEXT,
                                0, (LPARAM) (LPSTR) szBuf);
                lParam <<= 8;
            }

            pControl->bCancelParentNotify = FALSE;

            SendMessage(pControl->hwndParent, WM_COMMAND,
                MAKEWPARAM(GetWindowLong(hWnd, GWL_ID), EN_CHANGE), (LPARAM)hWnd);
        }
        break;

    case IP_SETRANGE:
        if (wParam < NUM_FIELDS)
        {
            pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);
            pControl->Children[wParam].byLow = LOBYTE(LOWORD(lParam));
            pControl->Children[wParam].byHigh = HIBYTE(LOWORD(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 IP_SETFOCUS:
        pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);

        if (wParam >= NUM_FIELDS)
        {
            for (wParam = 0; wParam < NUM_FIELDS; ++wParam)
                if (GetFieldValue(&(pControl->Children[wParam])) == -1)   break;
            if (wParam >= NUM_FIELDS)    wParam = 0;
        }
        EnterField(&(pControl->Children[wParam]), 0, CHARS_PER_FIELD);
        break;

// Determine whether all four subfields are blank
    case IP_ISBLANK:
        pControl = (CONTROL *)GET_CONTROL_HANDLE(hWnd);

        lResult = TRUE;
        for (i = 0; i < NUM_FIELDS; ++i)
        {
            if (GetFieldValue(&(pControl->Children[i])) != -1)
            {
                lResult = FALSE;
                break;
            }
        }
        break;

    default:
        lResult = DefWindowProc( hWnd, wMsg, wParam, lParam );
        break;
    }
    return( lResult );
}




/*
    IPAddressFieldProc() - Edit field window procedure

    This function sub-classes each edit field.
*/
LONG FAR PASCAL IPAddressFieldProc(HWND hWnd,
                                   UINT wMsg,
                                   WPARAM wParam,
                                   LPARAM lParam)
{
    CONTROL *pControl;
    FIELD *pField;
    HWND hControlWindow;
    WORD wChildID;
    LRESULT lresult;

    if (!(hControlWindow = GetParent(hWnd)))
        return 0;

    pControl = (CONTROL *)GET_CONTROL_HANDLE(hControlWindow);
    wChildID = (WORD)GetWindowLong(hWnd, GWL_ID);
    pField = &(pControl->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'))
        {
            DWORD dwResult;

            dwResult = CallWindowProc((WNDPROC)pControl->Children[wChildID].lpfnWndProc,
                                      hWnd, wMsg, wParam, lParam);
            dwResult = SendMessage(hWnd, EM_GETSEL, 0, 0L);

            if (dwResult == MAKELPARAM(CHARS_PER_FIELD, CHARS_PER_FIELD)
                && ExitField(pControl, wChildID)
                && wChildID < NUM_FIELDS-1)
            {
                EnterField(&(pControl->Children[wChildID+1]),
                                0, CHARS_PER_FIELD);
            }
            return dwResult;
        }

// spaces and periods fills out the current field and then if possible,
// goes to the next field.
        else if (wParam == FILLER || wParam == SPACE )
        {
            DWORD dwResult;
            dwResult = SendMessage(hWnd, EM_GETSEL, 0, 0L);
            if (dwResult != 0L && HIWORD(dwResult) == LOWORD(dwResult)
                && ExitField(pControl, wChildID))
            {
                if (wChildID >= NUM_FIELDS-1)
                    MessageBeep((UINT)-1);
                else
                {
                    EnterField(&(pControl->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(pControl, wChildID, wChildID-1,
                              CHARS_PER_FIELD, CHARS_PER_FIELD)
                    && SendMessage(pControl->Children[wChildID-1].hWnd,
                        EM_LINELENGTH, 0, 0L) != 0L)
                {
                    SendMessage(pControl->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(pControl, wChildID, wChildID-1,
                                  0, CHARS_PER_FIELD);
                    return 0;
                }
                else if ((wParam == VK_RIGHT || wParam == VK_DOWN)
                         && wChildID < NUM_FIELDS-1)
                {
                    SwitchFields(pControl, wChildID, wChildID+1,
                                      0, CHARS_PER_FIELD);
                    return 0;
                }
            }
            else
            {
                DWORD dwResult;
                WORD wStart, wEnd;

                dwResult = 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(pControl, wChildID, wChildID-1,
                                          CHARS_PER_FIELD, CHARS_PER_FIELD);
                        return 0;
                    }
                    else if ((wParam == VK_RIGHT || wParam == VK_DOWN)
                             && wChildID < NUM_FIELDS-1)
                    {
                        dwResult = SendMessage(hWnd, EM_LINELENGTH, 0, 0L);
                        if (wStart >= dwResult)
                        {
                            SwitchFields(pControl, 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(pControl, 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(pControl, wChildID, NUM_FIELDS-1,
                                CHARS_PER_FIELD, CHARS_PER_FIELD);
                return 0;
            }
            break;


        } // switch (wParam)

        break;

    case WM_KILLFOCUS:
        if ( !ExitField( pControl, wChildID ))
        {
            return 0;
        }

    } // switch (wMsg)

    lresult = CallWindowProc( (WNDPROC)pControl->Children[wChildID].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, int iOld, int iNew, WORD wStart, WORD wEnd)
{
    if (!ExitField(pControl, iOld))    return FALSE;
    EnterField(&(pControl->Children[iNew]), wStart, wEnd);
}



/*
    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);
}


/*
    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, int iField)
{
    HWND hControlWnd;
    HWND hDialog;
    WORD wLength;
    FIELD *pField;
    static TCHAR szBuf[CHARS_PER_FIELD+1];
    int i,j;

    pField = &(pControl->Children[iField]);
    *(WORD *)szBuf = (sizeof(szBuf)/sizeof(TCHAR)) - 1;
    wLength = (WORD)SendMessage(pField->hWnd,EM_GETLINE,0,(DWORD)(LPSTR)szBuf);
    if (wLength != 0)
    {
        szBuf[wLength] = TEXT('\0');
        for (j=0,i=0;j<(INT)wLength;j++)
        {
            i=i*10+szBuf[j]-TEXT('0');
        }
        if (i < (int)(UINT)pField->byLow || i > (int)(UINT)pField->byHigh)
        {
            if ( i < (int)(UINT) pField->byLow )
            {
                /* too small */
                wsprintf(szBuf, TEXT("%d"), (int)(UINT)pField->byLow );
            }
            else
            {
                /* must be bigger */
                wsprintf(szBuf, TEXT("%d"), (int)(UINT)pField->byHigh );
            }
            SendMessage(pField->hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) szBuf);
            if ((hControlWnd = GetParent(pField->hWnd)) != NULL
                && (hDialog = GetParent(hControlWnd)) != NULL)
            {
                pControl->fInMessageBox = TRUE;
                IPAlertPrintf(hDialog, IDS_IPBAD_FIELD_VALUE, i,
                            pField->byLow, pField->byHigh);
                pControl->fInMessageBox = FALSE;
                SendMessage(pField->hWnd, EM_SETSEL, 0, CHARS_PER_FIELD);
                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.
*/
int GetFieldValue(FIELD *pField)
{
    WORD wLength;
    static TCHAR szBuf[CHARS_PER_FIELD+1];
    INT i,j;

    *(WORD *)szBuf = (sizeof(szBuf)/sizeof(TCHAR)) - 1;
    wLength = (WORD)SendMessage(pField->hWnd,EM_GETLINE,0,(DWORD)(LPSTR)szBuf);
    if (wLength != 0)
    {
        szBuf[wLength] = TEXT('\0');
        for (j=0,i=0;j<(INT)wLength;j++)
        {
            i=i*10+szBuf[j]-TEXT('0');
        }
        return i;
    }
    else
        return -1;
}



/*
    IPAlertPrintf() - Does a printf to a message box.
*/
int FAR CDECL IPAlertPrintf(HWND hwndParent, UINT ids, int iCurrent, int iLow, int iHigh)
{
    static TCHAR szBuf[MAX_IPRES];
    static TCHAR szFormat[MAX_IPRES];
    TCHAR * psz;

    if (ids != IDS_IPNOMEM &&
        IPLoadOem(hLibInstance, ids, szFormat, sizeof(szFormat)/sizeof(TCHAR)))
    {
        wsprintf(szBuf, szFormat, iCurrent, iLow, iHigh);
        psz = szBuf;
    }
    else
        psz = szNoMem;

    MessageBeep(MB_ICONEXCLAMATION);
    return MessageBox(hwndParent, psz, szCaption, MB_ICONEXCLAMATION);
}



/*
    Load an OEM string and convert it to ANSI.
    call
        hInst = This instance
        idResource = The ID of the string to load
        lpszBuffer = Pointer to buffer to load string into.
        cchBuffer = Length of the buffer.
    returns
        TRUE if the string is loaded, FALSE if it is not.
*/
BOOL IPLoadOem(HINSTANCE hInst, UINT idResource, TCHAR* lpszBuffer, int cchBuffer)
{
    if (LoadString(hInst, idResource, lpszBuffer, cchBuffer))
    {
//fix kksuzuka: #2240
//Why call OemToAnsi?
// BUGBUG FloydR Why, indeed?  LoadString loads from Unicode and converts
// to ANSI - never to OEM.
//lpszBuffer points Unicode string.
        if ( !NETUI_IsDBCS() ) {
            OemToAnsi((LPCSTR)lpszBuffer, (LPSTR)lpszBuffer);
        }
        return TRUE;
    }
    else
    {
        lpszBuffer[0] = 0;
        return FALSE;
    }
}
