
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: addctrl.c
*
* Contains routines for adding (creating) and deleting controls.
*
* Functions:
*
*    AddNewDialog()
*    DropControl()
*    AddControl()
*    CreateControl()
*    CreateDlgFont()
*    MyGetCharDimensions()
*    AdjustDefaultSizes()
*    DeleteControl()
*    DeleteDialog()
*    DeleteControl2()
*    FreeCTYPE()
*
* Comments:
*
****************************************************************************/

#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"
#include "dialogs.h"

#include <stdlib.h>
#include <string.h>

STATICFN HFONT CreateDlgFont(HWND hwnd, LPTSTR pszFontName,
    INT nPointSize);
STATICFN INT MyGetCharDimensions(HWND hwnd, HFONT hFont,
    PTEXTMETRIC ptm);
STATICFN VOID AdjustDefaultSizes(VOID);
STATICFN VOID DeleteControl2(NPCTYPE npcDel);
STATICFN VOID FreeCTYPE(NPCTYPE npc);



/************************************************************************
* AddNewDialog
*
* High level function to add a new dialog to the current resource.
* Any existing dialog will be saved away in the resource buffer.
* The dialog is created at a default position and size with default
* styles.
*
************************************************************************/

VOID AddNewDialog(VOID)
{
    RECT rc;

    if (gfEditingDlg) {
        if (!SynchDialogResource())
            return;

        DeleteDialog(FALSE);
    }

    /*
     * Now drop a new dialog window.
     */
    SetRect(&rc, DEFDIALOGXPOS, DEFDIALOGYPOS,
            DEFDIALOGXPOS + awcd[W_DIALOG].cxDefault,
            DEFDIALOGYPOS + awcd[W_DIALOG].cyDefault);
    DropControl(&awcd[W_DIALOG], &rc);
}



/************************************************************************
* DropControl
*
* This function drops a new control of Type at the specified
* location.  The default style and text of the control is
* determined from the awcd table based on its type.  The control
* is selected after being dropped.
*
*
* Arguments:
*   PWINDOWCLASSDESC pwcd - Describes the type of new control.
*   PRECT prc             - Rectangle of the new control (in dialog units).
*
************************************************************************/

VOID DropControl(
    PWINDOWCLASSDESC pwcd,
    PRECT prc)
{
    ORDINAL ordIcon;
    ORDINAL ordDlg;
    LPTSTR pszText;
    NPCTYPE npcNew;
    INT idCtrl;
    DIALOGINFO di;

    /*
     * Get the next available id to use for the new control.
     */
    idCtrl = NextID((pwcd->iType == W_DIALOG) ? NEXTID_DIALOG : NEXTID_CONTROL,
            plInclude, 0);

    if (pwcd->iType == W_ICON) {
        /*
         * For icon controls, the text is really an ordinal or name
         * of the icon resource to display.  We get the next available
         * id (skipping the id we just got for the control itself) to
         * use as an ordinal.
         */
        WriteOrd(&ordIcon, NextID(NEXTID_CONTROL, plInclude, idCtrl));
        pszText = (LPTSTR)&ordIcon;
    }
    else {
        pszText = pwcd->pszTextDefault;
    }

    /*
     * Make the control.
     */
    if (pwcd->iType == W_DIALOG) {
        /*
         * Pick a default name for the dialog.
         */
        WriteOrd(&ordDlg, NextID(NEXTID_DIALOG, plInclude, 0));

        di.fResFlags = DEFDLGMEMFLAGS;
	di.wLanguage = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
        di.pszClass = NULL;
        di.pszMenu = NULL;
        di.DataVersion = 0;
        di.Version = 0;
        di.Characteristics = 0;
        di.nPointSize = DEFPOINTSIZE;
        lstrcpy(di.szFontName, ids(IDS_DEFFONTNAME));

        npcNew = AddControl(pwcd, pszText,
                pwcd->flStyles, pwcd->flExtStyle, idCtrl,
                prc->left, prc->top,
                prc->right - prc->left, prc->bottom - prc->top,
                (LPTSTR)&ordDlg, &di);
    }
    else {
        npcNew = AddControl(pwcd, pszText,
                pwcd->flStyles, pwcd->flExtStyle, idCtrl,
                prc->left, prc->top,
                prc->right - prc->left, prc->bottom - prc->top,
                NULL, NULL);
    }

    if (!npcNew)
        return;

    /*
     * If we just dropped a dialog, we need to now show it.
     * It it was some other control, mark the dialog as having
     * been changed.
     */
    if (pwcd->iType == W_DIALOG) {
        ShowWindow(npcNew->hwnd, SW_SHOWNA);
        ToolboxOnTop();
    }
    else {
        gfDlgChanged = TRUE;
    }

    SelectControl(npcNew, FALSE);

    gfResChged = TRUE;
    ShowFileStatus(FALSE);

    /*
     * Now we determine if one of the fields in the status ribbon
     * should be given the focus initially.  The assumption is that
     * there are some things that a user will always want to change
     * when dropping a new control, such as the text in a push
     * button, for example.
     */
    idCtrl = 0;
    switch (pwcd->iType) {
        case W_ICON:
            /*
             * For icons, the first thing the user will
             * probably want to do is to change the name.
             */
            idCtrl = DID_STATUSNAME;
            break;

        default:
            /*
             * If this control has text, they will probably want
             * to change it.  This includes the caption if the
             * control is a dialog.
             */
            if (pwcd->fHasText)
                idCtrl = DID_STATUSTEXT;

            break;
    }

    if (idCtrl) {
        SendDlgItemMessage(hwndStatus, idCtrl,
		 EM_SETSEL, (WPARAM)(0), (LONG)(-1));
        SetFocus(GetDlgItem(hwndStatus, idCtrl));
    }
}



/************************************************************************
* AddControl
*
* This function is used to add a new control.  CreateControl() does
* half the work.
*
* Arguments:
*   PWINDOWCLASSDESC pwcd - Window class structure.  Describes the
*                           type of control to add.
*   LPTSTR pszText        - Text for the new control.
*   DWORD style           - Style of the new control.
*   DWORD flExtStyle      - Extended style of the new control.
*   INT id                - ID for the new control.
*   INT x                 - X location of the new control.
*   INT y                 - Y location of the new control.
*   INT cx                - Width of the new control.
*   INT cy                - Height of the new control.
*   LPTSTR pszDlgName     - For dialogs, has dialog name.
*   PDIALOGINFO pdi       - Ptr to additional dialog info (NULL for controls).
*
* Returns:
*     A pointer to the CTYPE structure for the new control.
*     NULL if it couldn't create the control.
*
************************************************************************/

NPCTYPE AddControl(
    PWINDOWCLASSDESC pwcd,
    LPTSTR pszText,
    DWORD style,
    DWORD flExtStyle,
    INT id,
    INT x,
    INT y,
    INT cx,
    INT cy,
    LPTSTR pszDlgName,
    PDIALOGINFO pdi)
{
    NPCTYPE npcNew;
    NPCTYPE npcT;
    NPCTYPE *npnpcLast;
    HWND hwndBehind;

    if (!(npcNew = (NPCTYPE)MyAlloc(sizeof(CTYPE))))
        return NULL;

    /*
     * These are checked later if a failure occurs,
     * so we null them out now.
     */
    npcNew->hwnd = NULL;
    npcNew->hwndDrag = NULL;
    npcNew->text = NULL;

    /*
     * Set up some fields and create the control.
     */
    npcNew->npcNext = NULL;
    npcNew->pwcd = pwcd;
    npcNew->fSelected = FALSE;
    SetRect(&npcNew->rc, x, y, x + cx, y + cy);

    if (pwcd->iType == W_DIALOG)
        hwndBehind = (HWND)NULL;
    else
        hwndBehind = (HWND)1;

    if (!CreateControl(npcNew, pszText, style, flExtStyle, id, &npcNew->rc,
            hwndBehind, pdi))
        goto CreateFailed;

    /*
     * Create the drag window, unless this is the dialog.
     */
    if (pwcd->iType != W_DIALOG) {
        npcNew->hwndDrag = CreateWindow(
                szDragClass,
                NULL,
                WS_CHILD,
                0, 0, 0, 0,
                gcd.npc->hwnd,
                NULL,
                ghInst,
                NULL);

        /*
         * Store the CTYPE pointer into the control's drag window.
         * This will be used by PCFROMHWND later.
         */
        SETPCINTOHWND(npcNew->hwndDrag, npcNew);

        /*
         * Move the drag window to the top of the Z-Order.
         */
        SetWindowPos(npcNew->hwndDrag, NULL, 0, 0, 0, 0,
                SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW);

        SizeDragToControl(npcNew);
    }

    /*
     * Did we just create a dialog?
     */
    if (pwcd->iType == W_DIALOG) {
        /*
         * First, copy the new name (it can be an ordinal!).
         */
        if (!(gcd.pszDlgName = MyAlloc(NameOrdLen(pszDlgName))))
            goto CreateFailed;

        NameOrdCpy(gcd.pszDlgName, pszDlgName);

        /*
         * Now, setup some other globals.  We clear the gcd.prl pointer,
         * because we are assuming that this dialog was not created
         * from a res link (it was dropped instead).  The routines
         * that call AddControl when creating a dialog from a res
         * link are  responsible for setting this global later.
         */
        gcd.prl = NULL;
        gcd.npc = npcNew;
        gfEditingDlg = TRUE;
    }
    else {
        /*
         * Search for the last control in the list.
         */
        npnpcLast = &npcHead;
        for (npcT = npcHead; npcT; npcT = npcT->npcNext)
            npnpcLast = &npcT->npcNext;

        /*
         * Link in the new control at the end of the list.
         */
        *npnpcLast = npcNew;
        cWindows++;
    }

    return npcNew;

CreateFailed:
    FreeCTYPE(npcNew);
    return NULL;
}



/************************************************************************
* CreateControl
*
* Creates a control.  Some styles may be masked off of the actual
* control created.  This function can also create the dialog box.
*
* If the control created is the dialog box, it will not be made visible.
* This must be done by the caller.  This allows the caller to first add
* all the controls to the dialog before showing it.
*
* The x, y, cx and cy coordinates are all in dialog units.  For a
* type of W_DIALOG, this will be relative to the apps client.  For a
* control, this will be relative to the "client" area of the dialog.
*
* Arguments:
*   NPCTYPE npc       - The CTYPE pointer to the new control.  The hwnd
*                       fields of the npc will be set.
*   LPTSTR pszText    - The window text.
*   DWORD flStyle     - The style to use.
*   DWORD flExtStyle  - Extended style of the new control.
*   INT id            - ID for the control.
*   PRECT prc         - The size and location of the new control.
*   HWND hwndBehind   - Put new control behind this hwnd in Z-order.
*   PDIALOGINFO pdi   - Pointer to additional dialog info (NULL for controls).
*
* Returns:
*     Handle of the control created.
*     NULL if control was not created.
*
************************************************************************/

HWND CreateControl(
    NPCTYPE npc,
    LPTSTR pszText,
    DWORD flStyle,
    DWORD flExtStyle,
    INT id,
    PRECT prc,
    HWND hwndBehind,
    PDIALOGINFO pdi)
{
    HWND hwnd;
    HWND hwndChild;
    WNDPROC lpfnChild;
    RECT rcT;
    TEXTMETRIC tm;
    LPTSTR pszCreateClass;
    LPTSTR pszTextOld;
    INT iType = npc->pwcd->iType;

    /*
     * Set the text field.  Remember that it can be an ordinal
     * (for icon controls).
     */
    pszTextOld = npc->text;
    if (pszText && *pszText) {
        if (!(npc->text = MyAlloc(NameOrdLen(pszText))))
            return NULL;

        NameOrdCpy(npc->text, pszText);
    }
    else {
        npc->text = NULL;
    }

    /*
     * If there was text before on this control, free it now.
     * This should be done after the new text is allocated and
     * copied, because it is common to pass this routine the same
     * pointer, and we don't want to free the text before we have
     * copied it!
     */
    if (pszTextOld)
        MyFree(pszTextOld);

    /*
     * Also set some other values in the CTYPE structure.
     */
    npc->id = id;
    npc->flStyle = flStyle;
    npc->flExtStyle = flExtStyle;

    /*
     * If this is a dialog and it has the WS_CHILD style, remove
     * it and make it WS_POPUP instead.  This prevents some problems
     * when editing the dialog.
     */
    if (iType == W_DIALOG && (flStyle & WS_CHILD)) {
        flStyle &= ~WS_CHILD;
        flStyle |= WS_POPUP;
    }

    /*
     * If this is an emulated custom control, we always make it with the
     * default styles no matter what the user has specified.  If not,
     * remove any styles that can cause problems for a control of this
     * type, such as OWNERDRAW styles.
     */
    if (npc->pwcd->fEmulated)
        flStyle = awcd[W_CUSTOM].flStyles;
    else
        flStyle &= ~npc->pwcd->flStylesBad;

    if (iType == W_DIALOG) {
        /*
         * If the style includes the DS_MODALFRAME bit, set the appropriate
         * extended style bit.
         */
        if (flStyle & DS_MODALFRAME)
            flExtStyle |= WS_EX_DLGMODALFRAME;

        /*
         * Create the dialog, but don't show it yet.
         */
        hwnd = CreateWindowEx(
                flExtStyle,
                MAKEINTRESOURCE(DIALOGCLASS),
                npc->text,
                flStyle & ~WS_VISIBLE,
                0, 0, 0, 0,
                ghwndSubClient,
                0,
                ghInst,
                NULL);
    }
    else {
        /*
         * Get the size of the control.
         */
        rcT = *prc;

        /*
         * Map the dialog unit rectangle to window coords.
         */
        DUToWinRect(&rcT);

        /*
         * If this is an icon, the text field is actually going
         * to be an ordinal that has the resource id.  We must map this to
         * an id of our own or when we create the control it will cause a
         * "Resource not found" error.
         */
        if (iType == W_ICON)
            pszText = (LPTSTR)&gordIcon;
        else
            pszText = npc->text;

        /*
         * Get the class name to use.  In the case of custom controls,
         * if the control is emulated, use the special emulator class.
         * Otherwise, it is an installed custom control, and we can use
         * it's real class string.
         */
        if (iType == W_CUSTOM) {
            if (npc->pwcd->fEmulated)
                pszCreateClass = szCustomClass;
            else
                pszCreateClass = npc->pwcd->pszClass;
        }
        else {
            pszCreateClass = ids(acsd[awcd[iType].iClass].idsClass);
        }

        /*
         * Create the control.  We always create it visible in work mode,
         * even if the style says it isn't.
         */
        hwnd = CreateWindowEx(
                flExtStyle,
                pszCreateClass,
                pszText,
                flStyle | WS_VISIBLE,
                rcT.left, rcT.top,
                rcT.right - rcT.left,
                rcT.bottom - rcT.top,
                gcd.npc->hwnd,
                0,
                ghInst,
                NULL);
    }

    if (!hwnd) {
        Message(MSG_CREATECTRLERROR, pszCreateClass);
        return NULL;
    }

    if (iType == W_DIALOG) {
        /*
         * Did they specify a font?
         */
        if (*pdi->szFontName) {
            gcd.hFont = CreateDlgFont(hwnd, pdi->szFontName, pdi->nPointSize);
            lstrcpy(gcd.di.szFontName, pdi->szFontName);
            gcd.di.nPointSize = pdi->nPointSize;
            gcd.fFontSpecified = TRUE;
        }
        else {
            gcd.hFont = NULL;
            *gcd.di.szFontName = CHAR_NULL;
            gcd.di.nPointSize = 0;
            gcd.fFontSpecified = FALSE;
        }

        /*
         * Get the dimensions of the font.  It is a possible case that
         * they specified a font but it could not be created, so in
         * that case we use the system font as well as if they didn't
         * specify a font at all.
         */
        if (gcd.hFont) {
            gcd.cxChar = MyGetCharDimensions(hwnd, gcd.hFont, &tm);
            gcd.cyChar = tm.tmHeight;
        }
        else {
            gcd.cxChar = gcxSysChar;
            gcd.cyChar = gcySysChar;
        }

        /*
         * Now that we know what font we are using, adjust some entries
         * in the awcd (array of window class data) table for default 
         * sizing of controls.
         */
        AdjustDefaultSizes();
    }

    /*
     * If there is a valid user specified font, inform the control
     * of it.  Once the font has been set into the dialog, it
     * must not be destroyed, because the dialog window will
     * clean it up when it is destroyed.
     */
    if (gcd.hFont)
        SendMessage(hwnd, WM_SETFONT, (WPARAM)gcd.hFont, 0L);

    /*
     * Move the window into the requested Z-Order.
     */
    SetWindowPos(hwnd, hwndBehind, 0, 0, 0, 0,
            SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW);

    /*
     * Store the CTYPE pointer into the control's hwnd.
     * This will be used by PCFROMHWND later.
     */
    SETPCINTOHWND(hwnd, npc);

    /*
     * Subclass the control.
     */
    npc->pwcd->pfnOldWndProc =
            (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC,
            (iType == W_DIALOG) ? (DWORD)DialogCtrlWndProc :
            (DWORD)CtrlWndProc);

    /*
     * Be sure double-clicks are enabled for this control.
     */
    (UINT)SetClassLong((hwnd), GCL_STYLE, (LONG)((UINT)GetClassLong((hwnd), GCL_STYLE) | CS_DBLCLKS));

    /*
     * Subclass any children this control may have.
     */
    for (hwndChild = GetTopWindow(hwnd); hwndChild;
            hwndChild = GetNextWindow(hwndChild, GW_HWNDNEXT)) {
        lpfnChild = (WNDPROC)SetWindowLong(hwndChild,
                GWL_WNDPROC, (DWORD)ChildWndProc);
        SETCHILDPROC(hwndChild, lpfnChild);
    }

    /*
     * Did we just create a dialog?
     */
    if (iType == W_DIALOG) {
        /*
         * Now that the dialog is created, we can figure out how to
         * size it.  We start by mapping the dialog units to window
         * coordinates,
         */
        rcT = *prc;
        DUToWinRect(&rcT);

        /*
         * We now have the rectangle for the client area.  Expand it
         * to account for the frame controls.
         */
        AdjustWindowRectEx(&rcT, flStyle, FALSE, flExtStyle);

        /*
         * Now we can map the rect from the apps client area
         * to the desktop, then we set the dialogs position.
         */
        ClientToScreenRect(ghwndSubClient, &rcT);
        SetWindowPos(hwnd, NULL,
                rcT.left, rcT.top,
                rcT.right - rcT.left, rcT.bottom - rcT.top,
                SWP_NOZORDER | SWP_NOACTIVATE);

        SaveDlgClientRect(hwnd);

        /*
         * Save the class name, if any.
         */
        if (pdi->pszClass && *pdi->pszClass) {
            if (!(gcd.di.pszClass = MyAlloc(NameOrdLen(pdi->pszClass)))) {
                DestroyWindow(hwnd);
                return (HWND)NULL;
            }

            NameOrdCpy(gcd.di.pszClass, pdi->pszClass);
        }
        else {
            gcd.di.pszClass = NULL;
        }

        /*
         * Save the menu name, if any.
         */
        if (pdi->pszMenu && *pdi->pszMenu) {
            if (!(gcd.di.pszMenu = MyAlloc(NameOrdLen(pdi->pszMenu)))) {
                DestroyWindow(hwnd);
                return (HWND)NULL;
            }

            NameOrdCpy(gcd.di.pszMenu, pdi->pszMenu);
        }
        else {
            gcd.di.pszMenu = NULL;
        }

        /*
         * Set some other fields in the additional dialog info structure.
         */
        gcd.di.fResFlags = pdi->fResFlags;
        gcd.di.wLanguage = pdi->wLanguage;
        gcd.di.DataVersion = pdi->DataVersion;
        gcd.di.Version = pdi->Version;
        gcd.di.Characteristics = pdi->Characteristics;
    }

    npc->hwnd = hwnd;
    return hwnd;
}



/************************************************************************
* CreateDlgFont
*
* This function creates a font with the given face name and point size
* and returns a handle to it.
*
* Arguments:
*   HWND hwnd           - Dialog window handle.
*   LPTSTR pszFontName  - Name of the font (for example: "Helv").
*   INT nPointSize      - Point size of the font (for example: 8 or 12).
*
* Returns:
*   A handle to the created font, or NULL if it could not be created.
*
************************************************************************/

STATICFN HFONT CreateDlgFont(
    HWND hwnd,
    LPTSTR pszFontName,
    INT nPointSize)
{
    HFONT hFont;
    HFONT hFontOld;
    HDC hDC;
    LOGFONT lf;
    TCHAR szFaceName[LF_FACESIZE];

    /*
     * Initialize the logical font structure.  Note that filling the
     * structure with zeros gives it all the default settings.
     */
    memset(&lf, 0, sizeof(LOGFONT));
    lf.lfHeight = (SHORT)-PointSizeToPixels(nPointSize);
    lf.lfWeight = FW_BOLD;
    lstrcpy(lf.lfFaceName, pszFontName);

    if (!(hFont = CreateFontIndirect(&lf)))
        return NULL;

    /*
     * If we didn't get the face name that was requested, delete the
     * new font and return NULL.  This will effectively select the
     * system font for this dialog.
     */
    hDC = GetDC(hwnd);
    if (hFontOld = SelectObject(hDC, hFont)) {
        GetTextFace(hDC, LF_FACESIZE, szFaceName);
        SelectObject(hDC, hFontOld);

        if (lstrcmpi(szFaceName, pszFontName) != 0) {
            DeleteObject(hFont);
            hFont = NULL;
        }
    }
    else {
        DeleteObject(hFont);
        hFont = NULL;
    }

    ReleaseDC(hwnd, hDC);

    return hFont;
}



/************************************************************************
* MyGetCharDimensions
*
* This function calculates the average character width of the given
* font for the given window.  This must be used instead of
* simply using the tmAveCharWidth field of the text metrics, because
* this value is not correct for proportional fonts.  
*
* Arguments:
*   HWND hwnd       - The window handle.
*   HFONT hFont     - The font handle.
*   PTEXTMETRIC ptm - Where to return the text metrics.
*
* Returns:
*   The average character width.  The text metrics are returned in
*   the TEXTMETRIC structure pointed to by ptm.
*
************************************************************************/

STATICFN INT MyGetCharDimensions(
    HWND hwnd,
    HFONT hFont,
    PTEXTMETRIC ptm)
{
    register INT i;
    HDC hDC;
    SIZE size;
    INT iWidth;
    TCHAR szAveCharWidth[52];
    HFONT hFontOld;

    hDC = GetDC(hwnd);
    hFontOld = SelectObject(hDC, hFont);

    GetTextMetrics(hDC, ptm);

    /*
     * Is this a variable pitch font?
     */
    if (ptm->tmPitchAndFamily & 0x01) {
        for (i = 0; i < 26; i++)
            szAveCharWidth[i] = (TCHAR)(i + CHAR_A);

        for (i = 0; i < 26; i++)
            szAveCharWidth[i + 26] = (TCHAR)(i + CHAR_CAP_A);

        GetTextExtentPoint(hDC, szAveCharWidth, 52, &size);
        iWidth = (INT)size.cx / 26;

        //
        // Round it up.
        //
        iWidth = (iWidth + 1) / 2;
    }
    else {
        iWidth = ptm->tmAveCharWidth;
    }

    SelectObject(hDC, hFontOld);
    ReleaseDC(hwnd, hDC);

    return iWidth;
}



/************************************************************************
* AdjustDefaultSizes
*
* This functions adjusts some default size entries in the awcd (array of
* window class data) table.
* This must be done at run time, because the actual values depend on the
* system that dlgedit is being run on and the font of the current dialog.
*
* This function should be called any time that a dialog is created,
* or its font is changed.
*
************************************************************************/

STATICFN VOID AdjustDefaultSizes(VOID)
{
    awcd[W_ICON].cxDefault =
            (((GetSystemMetrics(SM_CXICON) * 4) * 2) + gcd.cxChar)
            / (gcd.cxChar * 2);

    awcd[W_ICON].cyDefault =
            (((GetSystemMetrics(SM_CYICON) * 8) * 2) + gcd.cyChar)
            / (gcd.cyChar * 2);

    awcd[W_VERTSCROLL].cxDefault =
            (((GetSystemMetrics(SM_CXVSCROLL) * 4) * 2) + gcd.cxChar)
            / (gcd.cxChar * 2);

    awcd[W_HORZSCROLL].cyDefault =
            (((GetSystemMetrics(SM_CYHSCROLL) * 8) * 2) + gcd.cyChar)
            / (gcd.cyChar * 2);
}



/************************************************************************
* DeleteControl
*
* This deletes all selected controls from the dialog being edited
* (or the dialog itself, if it is selected).
*
************************************************************************/

VOID DeleteControl(VOID)
{
    if (gfDlgSelected) {
        if (Message(MSG_DELETEDIALOG) == IDYES)
            /*
             * Delete the dialog, including the resource for it.
             */
            DeleteDialog(TRUE);
    }
    else {
        while (gnpcSel)
            DeleteControl2(gnpcSel);

        gfDlgChanged = TRUE;
    }

    gfResChged = TRUE;
    ShowFileStatus(FALSE);
    StatusUpdate();
    StatusSetEnable();
}



/************************************************************************
* DeleteControl2
*
* This deletes a control by destroying its window and removing it
* from the linked list of controls associated with the dialog box.
* The control is destroyed and the window is unlinked and the CTYPE free'd.
*
************************************************************************/

STATICFN VOID DeleteControl2(
    NPCTYPE npcDel)
{
    register NPCTYPE npcT;
    register NPCTYPE *npnpcLast;

    UnSelectControl(npcDel);

    /*
     * Search for the control, unlink it from the list and free it.
     */
    npcT = npcHead;
    npnpcLast = &npcHead;
    while (npcT) {
        if (npcT == npcDel) {
            *npnpcLast = npcT->npcNext;
            FreeCTYPE(npcT);
            cWindows--;
            break;
        }

        npnpcLast = &npcT->npcNext;
        npcT = npcT->npcNext;
    }
}



/************************************************************************
* DeleteDialog
*
* This deletes the dialog box being worked on and sets globals
* and the Status Window appropriately.  All CTYPEs are freed, the
* status window is updated and the dialog window is destroyed.
*
* Arguments:
*   BOOL fResAlso - If TRUE, delete the dialog resource also.
*
************************************************************************/

VOID DeleteDialog(
    BOOL fResAlso)
{
    register NPCTYPE npcT;
    register NPCTYPE npcNext;

    CancelSelection(FALSE);

    /*
     * If they requested that the dialog resource be deleted also,
     * do it first while some globals are still set.
     */
    if (fResAlso)
        DeleteDialogResource();

    /*
     * Hide the window for better painting speed.
     */
    ShowWindow(gcd.npc->hwnd, SW_HIDE);

    /*
     * Free all the controls.
     */
    npcT = npcHead;
    while (npcT) {
        npcNext = npcT->npcNext;
        FreeCTYPE(npcT);
        npcT = npcNext;
    }
    npcHead = NULL;
    cWindows = 0;

    /*
     * Free the dialog itself.
     */
    FreeCTYPE(gcd.npc);

    if (gcd.pszDlgName) {
        MyFree(gcd.pszDlgName);
        gcd.pszDlgName = NULL;
    }

    if (gcd.di.pszClass) {
        MyFree(gcd.di.pszClass);
        gcd.di.pszClass = NULL;
    }

    if (gcd.di.pszMenu) {
        MyFree(gcd.di.pszMenu);
        gcd.di.pszMenu = NULL;
    }

    if (gcd.hFont) {
        DeleteObject(gcd.hFont);
        gcd.hFont = NULL;
    }

    gcd.fFontSpecified = FALSE;
    *gcd.di.szFontName = CHAR_NULL;

    /*
     * Set these globals back to the system font values so that
     * workers like WinToDUPoint will still work.
     */
    gcd.cxChar = gcxSysChar;
    gcd.cyChar = gcySysChar;

    gcd.prl = NULL;

    gcd.npc = NULL;
    gfEditingDlg = FALSE;
    gfDlgChanged = FALSE;

    ToolboxSelectTool(W_NOTHING, FALSE);
    StatusUpdate();
    StatusSetEnable();
}



/************************************************************************
* FreeCTYPE
*
* This function frees an allocated CTYPE.  The associated control or
* dialog window is destroyed, and memory for the text and/or class
* is freed, followed by freeing the actual CTYPE structure itself.
*
* If the hwnd in the CTYPE is NULL, only the text (if not NULL) is
* assumed to be valid and will be freed, followed by the CTYPE structure
* itself.  This allows FreeCTYPE to be called when the CTYPE is only
* partially initialized.  This is a little dependant on the order that
* a CTYPE is allocated and initialized in AddControl().
*
* Arguments:
*     NPCTYPE npc = The CTYPE to free.
*
************************************************************************/

STATICFN VOID FreeCTYPE(
    NPCTYPE npc)
{
    if (npc->hwnd)
        DestroyWindow(npc->hwnd);

    if (npc->hwndDrag)
        DestroyWindow(npc->hwndDrag);

    if (npc->text)
        MyFree(npc->text);

    MyFree(npc);
}
