
/******************************************************************************\
*       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: status.c
*
* Support for the Status window.
*
* Functions:
*
*    StatusDlgProc()
*    StatusApplyChanges()
*    StatusFillSymbolList()
*    StatusSetCoords()
*    StatusUpdate()
*    StatusSetEnable()
*    StatusInit()
*    StatusProcessCommand()
*    ValidateNewName()
*    ValidateNewID()
*    ApplyNewName()
*    ApplyNewID()
*    ApplyNewText()
*    StatusSetText()
*    StatusSetTextLabels()
*    StatusSetID()
*    StatusSetName()
*    StatusSetNameID()
*    StatusClearID()
*    StatusClearName()
*    StatusClear()
*    StatusShowFields()
*
* Comments:
*
****************************************************************************/

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


STATICFN BOOL StatusInit(HWND hwnd);
STATICFN VOID StatusProcessCommand(HWND hwnd, INT idCtrl,
    INT NotifyCode);
STATICFN BOOL ValidateNewName(NPCTYPE npc, LPTSTR pszName,
    LPTSTR pszID, LPTSTR pszSym, PINT pidNew, BOOL *pfAddLabel);
STATICFN BOOL ValidateNewID(NPCTYPE npc, LPTSTR pszSym, LPTSTR pszID,
    PINT pidNew, BOOL *pfAddLabel);
STATICFN BOOL ApplyNewName(NPCTYPE npc, LPTSTR pszName,
    BOOL fAddLabel, LPTSTR pszSym, INT idNew);
STATICFN BOOL ApplyNewID(NPCTYPE npc, INT idNew, BOOL fAddLabel,
    LPTSTR pszSym);
STATICFN BOOL ApplyNewText(NPCTYPE npc, LPTSTR pszNewText);
STATICFN VOID StatusSetText(LPTSTR pszText, INT Type);
STATICFN VOID StatusSetTextLabels(INT Type, BOOL fIsOrd);
STATICFN VOID StatusSetID(INT id, BOOL fSymAlso);
STATICFN VOID StatusSetName(LPTSTR pszName, INT Type);
STATICFN VOID StatusSetNameID(INT id, INT Type);
STATICFN VOID StatusClearID(VOID);
STATICFN VOID StatusClearName(VOID);
STATICFN VOID StatusClear(VOID);
STATICFN VOID StatusShowFields(INT Type);

/*
 * TRUE if the user has changed the entry fields in the status window.
 * This will be reset to FALSE whenever new information is placed
 * into the fields.
 */
static BOOL gfStatusChanged = FALSE;

/*
 * These globals save the window positions of the upper and lower
 * combo boxes and edit controls.  This is used to position them
 * depending on what type of control is selected.
 */
static RECT grcTopCombo;
static RECT grcBottomCombo;
static RECT grcTopEdit;
static RECT grcBottomEdit;



/************************************************************************
* StatusDlgProc
*
* This is the dialog procedure for the Status ribbon window.
*
************************************************************************/

DIALOGPROC StatusDlgProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (msg) {
        case WM_INITDIALOG:
            return StatusInit(hwnd);

        case WM_PAINT:
            {
                HDC hdc;
                RECT rc;
                PAINTSTRUCT ps;
                HPEN hpenWindowFrame;

                /*
                 * Draw our border lines.
                 */
                GetClientRect(hwnd, &rc);
                hdc = BeginPaint(hwnd, &ps);

                SelectObject(hdc, GetStockObject(WHITE_PEN));
                MoveToEx(hdc, rc.left, rc.top, NULL);
                LineTo(hdc, rc.right, rc.top);

                SelectObject(hdc, hpenDarkGray);
                MoveToEx(hdc, rc.left, (rc.top + gcyStatus) - gcyBorder - 1, NULL);
                LineTo(hdc, rc.right, (rc.top + gcyStatus) - gcyBorder - 1);

                hpenWindowFrame = CreatePen(PS_SOLID, gcyBorder,
                        GetSysColor(COLOR_WINDOWFRAME));
                SelectObject(hdc, hpenWindowFrame);
                MoveToEx(hdc, rc.left, (rc.top + gcyStatus) - gcyBorder, NULL);
                LineTo(hdc, rc.right, (rc.top + gcyStatus) - gcyBorder);

                EndPaint(hwnd, &ps);
                DeleteObject(hpenWindowFrame);
            }

            break;

        case WM_CTLCOLORDLG:
        case WM_CTLCOLORLISTBOX:
        case WM_CTLCOLORSTATIC:
            switch ((WORD)(msg - WM_CTLCOLORMSGBOX)) {
                case CTLCOLOR_DLG:
                case CTLCOLOR_LISTBOX:
                    return (BOOL)GetStockObject(LTGRAY_BRUSH);

                case CTLCOLOR_STATIC:
                    SetBkColor((HDC)(wParam),
                            LIGHTGRAY);
                    return (BOOL)GetStockObject(LTGRAY_BRUSH);
            }

            return (BOOL)NULL;

        case WM_COMMAND:
            StatusProcessCommand(hwnd,
                    LOWORD(wParam),
                    HIWORD(wParam));
            break;

        default:
            return FALSE;
    }

    return FALSE;
}



/************************************************************************
* StatusInit
*
* Initializes the Status ribbon window.
*
* Arguments:
*     HWND hwnd = The window handle.
*
* Returns:
*     
* TRUE 
*
************************************************************************/

STATICFN BOOL StatusInit(
    HWND hwnd)
{
    /*
     * Set this global right away.  Other routines that will be called
     * before CreateDialog returns depend on this global.
     */
    hwndStatus = hwnd;

    GetChildRect(GetDlgItem(hwnd, DID_STATUSSYM), &grcTopCombo);
    GetChildRect(GetDlgItem(hwnd, DID_STATUSSYMID), &grcTopEdit);
    GetChildRect(GetDlgItem(hwnd, DID_STATUSNAME), &grcBottomCombo);
    GetChildRect(GetDlgItem(hwnd, DID_STATUSNAMEID), &grcBottomEdit);

    SendDlgItemMessage(hwnd, DID_STATUSSYM, EM_LIMITTEXT, CCHTEXTMAX, 0L);
    SendDlgItemMessage(hwnd, DID_STATUSNAME, EM_LIMITTEXT, CCHTEXTMAX, 0L);
    SendDlgItemMessage(hwnd, DID_STATUSSYMID, EM_LIMITTEXT, CCHIDMAX, 0L);
    SendDlgItemMessage(hwnd, DID_STATUSNAMEID, EM_LIMITTEXT, CCHIDMAX, 0L);
    SendDlgItemMessage(hwnd, DID_STATUSTEXT, EM_LIMITTEXT, CCHTEXTMAX, 0L);

    StatusFillSymbolList(plInclude);
    StatusUpdate();
    StatusSetEnable();

    /*
     * Return TRUE so that the dialog manager does NOT set the focus
     * for me.  This prevents the status window from initially having
     * the focus when the editor is started.
     */
    return TRUE;
}



/************************************************************************
* StatusProcessCommand
*
* Function that processes commands sent to the status window.
*
* Arguments:
*   HWND hwnd        - The window handle.
*   INT idCtrl       - The id of the control the WM_COMMAND is for.
*   INT NotifyCode   - The control's notification code.
*
*
************************************************************************/

STATICFN VOID StatusProcessCommand(
    HWND hwnd,
    INT idCtrl,
    INT NotifyCode)
{
    TCHAR szSym[CCHTEXTMAX];
    TCHAR szID[CCHIDMAX + 1];
    NPLABEL npLabel;
    INT id;
    INT nIndex;
    LPTSTR pszOldName;

    switch (idCtrl) {
        case DID_STATUSSYM:
            /*
             * Did the symbol edit field change and is
             * something selected?
             */
            if (gnpcSel && (NotifyCode == CBN_EDITCHANGE ||
                    NotifyCode == CBN_SELCHANGE)) {
                /*
                 * Get the symbol and begin looking for it.
                 */
                if (NotifyCode == CBN_EDITCHANGE) {
                    /*
                     * The edit field was typed into.  Get the
                     * new text from there.
                     */
                    GetDlgItemText(hwnd, DID_STATUSSYM, szSym, CCHTEXTMAX);
                }
                else {
                    /*
                     * A new string was selected from the list
                     * box.  Get it from the list box, because
                     * at this point the new text is not yet set
                     * into the edit control!
                     */
                    nIndex = (INT)SendDlgItemMessage(hwnd,
                            DID_STATUSSYM, CB_GETCURSEL, 0, 0L);

                    if (nIndex != CB_ERR)
                        SendDlgItemMessage(hwnd, DID_STATUSSYM, CB_GETLBTEXT,
                                nIndex, (DWORD)szSym);
                    else
                        *szSym = CHAR_NULL;
                }

                /*
                 * Convert the symbol field to the associated id value,
                 * taking into account the special IDOK values, etc.
                 */
                if (!LabelToID(szSym, &id)) {
                    /*
                     * The symbol was not found.
                     * If the symbol is not blank, and the
                     * id of the control is already taken
                     * by another label, fill the id field
                     * with the next available id.  Otherwise,
                     * fill the id field with the controls
                     * id value.  It is assumed here that the
                     * dialog cannot be selected if the symbol
                     * field was able to be changed.
                     */
                    if (*szSym && FindID(gnpcSel->id, plInclude))
                        id = NextID(NEXTID_CONTROL, plInclude, 0);
                    else
                        id = gnpcSel->id;
                }

                StatusSetID(id, FALSE);

                gfStatusChanged = TRUE;
            }

            break;

        case DID_STATUSNAME:
            /*
             * Did the name edit field change and is
             * something selected?
             */
            if (gnpcSel && (NotifyCode == CBN_EDITCHANGE ||
                    NotifyCode == CBN_SELCHANGE)) {
                /*
                 * Get the symbol and begin looking for it.
                 */
                if (NotifyCode == CBN_EDITCHANGE) {
                    /*
                     * The edit field was typed into.  Get the
                     * new text from there.
                     */
                    GetDlgItemText(hwnd, DID_STATUSNAME, szSym, CCHTEXTMAX);
                }
                else {
                    /*
                     * A new string was selected from the list
                     * box.  Get it from the list box, because
                     * at this point the new text is not yet set
                     * into the edit control!
                     */
                    nIndex = (INT)SendDlgItemMessage(hwnd,
                            DID_STATUSNAME, CB_GETCURSEL, 0, 0L);

                    if (nIndex != CB_ERR)
                        SendDlgItemMessage(hwnd, DID_STATUSNAME, CB_GETLBTEXT,
                                nIndex, (DWORD)szSym);
                    else
                        *szSym = CHAR_NULL;
                }

                /*
                 * Try and convert the name to an ordinal.
                 */
                StrToNameOrd(szSym, (gnpcSel->pwcd->iType == W_DIALOG) ?
                        TRUE : FALSE);

                /*
                 * Was the name converted to an ordinal?
                 */
                if (IsOrd(szSym)) {
                    id = OrdID(szSym);
                }
                /*
                 * Is it an existing label?
                 */
                else if (npLabel = FindLabel(szSym, plInclude)) {
                    id = npLabel->id;
                }
                else {
                    /*
                     * Get a pointer to the original name.
                     */
                    pszOldName = (gnpcSel->pwcd->iType == W_DIALOG) ?
                            gcd.pszDlgName : gnpcSel->text;

                    /*
                     * If the old name was originally an ordinal, and
                     * there was no corresponding label for it,
                     * assume that the user is trying to enter a
                     * define for it and leave it alone.  Otherwise,
                     * pick the next available id.  But if the user
                     * completely blanks out the field, leave it
                     * alone also (this is a benign case).
                     */
                    if (IsOrd(pszOldName) &&
                            (!*szSym || !FindID(OrdID(pszOldName), plInclude)))
                        id = OrdID(pszOldName);
                    else
                        id = NextID((gnpcSel->pwcd->iType == W_DIALOG) ?
                                NEXTID_DIALOG : NEXTID_CONTROL, plInclude, 0);
                }

                StatusSetNameID(id, gnpcSel->pwcd->iType);

                /*
                 * Change the labels to reflect that the entered name
                 * is an ID instead of a name.
                 */
                StatusSetTextLabels(gnpcSel->pwcd->iType, TRUE);

                gfStatusChanged = TRUE;
            }

            break;

        case DID_STATUSSYMID:
        case DID_STATUSNAMEID:
        case DID_STATUSTEXT:
            if (!gnpcSel)
                break;

            if (NotifyCode == EN_CHANGE)
                gfStatusChanged = TRUE;

            if (idCtrl == DID_STATUSNAMEID) {
                GetDlgItemText(hwnd, DID_STATUSNAME, szSym, CCHTEXTMAX);
                GetDlgItemText(hwnd, DID_STATUSNAMEID, szID, CCHIDMAX + 1);
                StrToNameOrd(szSym, (gnpcSel->pwcd->iType == W_DIALOG) ?
                        TRUE : FALSE);

                /*
                 * Change the labels to reflect whether the entered name
                 * is an ID or a name.  It is considered an id if the
                 * edit field has something in it, or if the name field
                 * is a valid label, or if the name field represents an
                 * ordinal (it is numeric).
                 */
                StatusSetTextLabels(gnpcSel->pwcd->iType,
                        (IsOrd(szSym) || *szID ||
                        FindLabel(szSym, plInclude)) ? TRUE : FALSE);
            }

            break;

        case IDOK:
            if (StatusApplyChanges())
                SetFocus(ghwndMain);

            break;

        case IDCANCEL:
            StatusUpdate();
            SetFocus(ghwndMain);
            break;
    }
}



/************************************************************************
* StatusApplyChanges
*
* Processes the Enter command from the "Status" bar window to apply
* the current value of the fields to the current control.
*
* Returns:
*
* TRUE is successful; otherwise, FALSE is returned.
*
************************************************************************/

BOOL StatusApplyChanges(VOID)
{
    TCHAR szText[CCHTEXTMAX];
    TCHAR szSym[CCHTEXTMAX];
    TCHAR szName[CCHTEXTMAX];
    TCHAR szSymID[CCHIDMAX + 1];
    TCHAR szNameID[CCHIDMAX + 1];
    TCHAR szNameSym[CCHTEXTMAX];
    INT idSymNew;
    INT idNameNew;
    BOOL fAddSymLabel;
    BOOL fAddNameLabel;
    BOOL fSuccess = FALSE;

    /*
     * Quit if nothing is selected, or if nothing was changed.
     */
    if (!gnpcSel || !gfStatusChanged)
        return TRUE;

    idSymNew = gnpcSel->id;

    switch (gnpcSel->pwcd->iType) {
        case W_DIALOG:
            GetDlgItemText(hwndStatus, DID_STATUSNAME, szName, CCHTEXTMAX);
            GetDlgItemText(hwndStatus, DID_STATUSNAMEID, szNameID, CCHIDMAX + 1);
            GetDlgItemText(hwndStatus, DID_STATUSTEXT, szText, CCHTEXTMAX);
            if (!ValidateNewName(gnpcSel, szName, szNameID, szNameSym,
                    &idNameNew, &fAddNameLabel))
                return FALSE;

            if (ApplyNewName(gnpcSel, szName, fAddNameLabel,
                    szNameSym, idNameNew) &&
                    ApplyNewText(gnpcSel, szText))
                fSuccess = TRUE;

            break;

        case W_ICON:
            GetDlgItemText(hwndStatus, DID_STATUSNAME, szName, CCHTEXTMAX);
            GetDlgItemText(hwndStatus, DID_STATUSNAMEID, szNameID, CCHIDMAX + 1);
            GetDlgItemText(hwndStatus, DID_STATUSSYM, szSym, CCHTEXTMAX);
            GetDlgItemText(hwndStatus, DID_STATUSSYMID, szSymID, CCHIDMAX + 1);
            if (!ValidateNewName(gnpcSel, szName, szNameID, szNameSym,
                    &idNameNew, &fAddNameLabel) ||
                    !ValidateNewID(gnpcSel, szSym, szSymID,
                    &idSymNew, &fAddSymLabel))
                return FALSE;

            if (ApplyNewID(gnpcSel, idSymNew, fAddSymLabel, szSym) &&
                    ApplyNewName(gnpcSel, szName, fAddNameLabel,
                    szNameSym, idNameNew))
                fSuccess = TRUE;

            break;

        default:
            GetDlgItemText(hwndStatus, DID_STATUSSYM, szSym, CCHTEXTMAX);
            GetDlgItemText(hwndStatus, DID_STATUSSYMID, szSymID, CCHIDMAX + 1);
            GetDlgItemText(hwndStatus, DID_STATUSTEXT, szText, CCHTEXTMAX);
            if (!ValidateNewID(gnpcSel, szSym, szSymID, &idSymNew, &fAddSymLabel))
                return FALSE;

            if (ApplyNewID(gnpcSel, idSymNew, fAddSymLabel, szSym) &&
                    ApplyNewText(gnpcSel, szText))
                fSuccess = TRUE;

            break;
    }

    if (fSuccess) {
        ShowFileStatus(FALSE);
        StatusUpdate();
    }

    return fSuccess;
}



/************************************************************************
* ValidateNewName
*
* Validates the new name from the processing of the OK command
* from the Status ribbon window.
*
* The name is considered valid if it does not have any blanks in it,
* it is not already used by another dialog in the resource file, and
* it is not an empty string.
*
* Arguments:
*   NPCTYPE npc      - Pointer to the control.
*   LPTSTR pszName   - The new name.
*   LPTSTR pszID     - The ID.
*   LPTSTR pszSym    - The symbol.
*   PINT pidNew      - Where to return the new ID if successful.
*   BOOL *pfAddLabel - Set to TRUE if this symbol/id should be added
*                      to the include list.  Not touched otherwise.
*
* Returns:
*   TRUE if the new name is valid, FALSE otherwise.
*
************************************************************************/

STATICFN BOOL ValidateNewName(
    NPCTYPE npc,
    LPTSTR pszName,
    LPTSTR pszID,
    LPTSTR pszSym,
    PINT pidNew,
    BOOL *pfAddLabel)
{
    NPLABEL npLabel;
    BOOL fIDEmpty = FALSE;
    BOOL fNameEmpty = FALSE;
    BOOL fAddLabel = FALSE;
    INT idNew;
    TCHAR szIDTemp[CCHIDMAX + 1];

    /*
     * Start by assuming that the label will NOT be added.
     */
    *pfAddLabel = FALSE;

    /*
     * Is the ID field non-blank?
     */
    if (*pszID) {
        /*
         * Is the id valid?
         */
        if (!IsValue(pszID)) {
            Message(MSG_BADSYMBOLID);
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSNAMEID));
            return FALSE;
        }

        idNew = valtoi(pszID);
    }
    else {
        /*
         * The id field is blank.
         */
        fIDEmpty = TRUE;
    }

    /*
     * Is the name field blank?
     */
    if (!(*pszName))
        fNameEmpty = TRUE;

    if (fNameEmpty) {
        if (fIDEmpty) {
            Message((npc->pwcd->iType == W_DIALOG) ?
                    MSG_NODLGNAME : MSG_NOICONNAME);
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSNAME));
            return FALSE;
        }
        else {
            WriteOrd((PORDINAL)pszName, idNew);
        }
    }
    else {
        /*
         * Error if there are imbedded blanks.
         */
        if (HasBlanks(pszName)) {
            Message((npc->pwcd->iType == W_DIALOG) ?
                    MSG_DLGNAMEHASBLANKS : MSG_ICONNAMEHASBLANKS);
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSNAME));
            return FALSE;
        }

        /*
         * Convert the name to an ordinal, if appropriate.
         */
        StrToNameOrd(pszName, (npc->pwcd->iType == W_DIALOG) ? TRUE : FALSE);

        /*
         * If the name was translated to an ordinal, we are done.
         * Otherwise, keep trying to figure out whether it is a
         * name or a define.
         */
        if (!IsOrd(pszName)) {
            /*
             * Is the name a symbol in the current include list?
             */
            if (npLabel = FindLabel(pszName, plInclude)) {
                /*
                 * Yes.  If the id field was blank, just assume they
                 * wanted the corresponding id.
                 */
                if (fIDEmpty)
                    idNew = npLabel->id;

                /*
                 * If they somehow entered a valid define but tried to
                 * give it a different id value, show an error.
                 */
                if (npLabel->id != idNew) {
                    Myitoa(npLabel->id, szIDTemp);
                    Message(MSG_IDSYMMISMATCH, szIDTemp);
                    SetFocus(GetDlgItem(hwndStatus, DID_STATUSNAMEID));
                    return FALSE;
                }

                /*
                 * Make the name field into an ordinal.
                 */
                WriteOrd((PORDINAL)pszName, idNew);
            }
            else {
                /*
                 * The name was not found as a label.  Is the id field
                 * empty?
                 */
                if (fIDEmpty) {
                    /*
                     * Since the id field is empty, we can assume that
                     * what they entered is a name (not a define)
                     * and we are done.
                     */
                }
                else {
                    /*
                     * At this point we know the name field is not empty,
                     * and there is an id entered along with it.  We also
                     * know that the name (which we can now assume is
                     * a symbol) is not already found in the include file.
                     * We will check it for validity, copy it to the
                     * pszSym buffer, translate the name to an ordinal
                     * and set a flag saying that the sym/id pair should
                     * be added to the include list.
                     */
                    if (!IsSymbol(pszName)) {
                        Message(MSG_BADSYMBOLID);
                        SetFocus(GetDlgItem(hwndStatus, DID_STATUSNAME));
                        return FALSE;
                    }

                    /*
                     * Pass back the values.
                     */
                    lstrcpy(pszSym, pszName);
                    WriteOrd((PORDINAL)pszName, idNew);
                    *pidNew = idNew;

                    fAddLabel = TRUE;
                }
            }
        }
    }

    /*
     * Inform caller whether they should add this id/symbol
     * as a label to the include list.
     */
    *pfAddLabel = fAddLabel;

    /*
     * Return success.
     */
    return TRUE;
}



/************************************************************************
* ValidateNewID
*
* Validates the new ID from the processing of the OK command from
* the Status ribbon window.  It will return the new id in *pidNew.
* Note that the control is not actually updated by this routine.  This
* is so that if other validations fail nothing will have been done to the
* control.  It is assumed that ApplyNewID will be called later to do
* the actual update of the control.
*
* Arguments:
*   NPCTYPE npc      = Pointer to the control.
*   LPTSTR pszSym    = The symbol.
*   LPTSTR pszID     = The ID.
*   PINT pidNew      = Where to return the new ID if successful.
*   BOOL *pfAddLabel = Set to TRUE if this symbol/id should be added
*                      to the include list.  Not touched otherwise.
*
* Returns:
*   TRUE if successful, FALSE otherwise.  If TRUE is returned and they
*   changed the id, the new id will have been placed in *pidNew.
*
************************************************************************/

STATICFN BOOL ValidateNewID(
    NPCTYPE npc,
    LPTSTR pszSym,
    LPTSTR pszID,
    PINT pidNew,
    BOOL *pfAddLabel)
{
    NPLABEL npLabel;
    BOOL fIDEmpty = FALSE;
    BOOL fIDChanged = FALSE;
    BOOL fSymEmpty = FALSE;
    BOOL fSymChanged = FALSE;
    BOOL fAddLabel = FALSE;
    INT idNew;
    TCHAR szIDTemp[CCHIDMAX + 1];

    /*
     * Start by assuming that the label will NOT be added.
     */
    *pfAddLabel = FALSE;

    /*
     * If in translate mode, they cannot change the symbol or id,
     * and so it should not be validated.  Even if there was a
     * problem, they could not fix it.
     */
    if (gfTranslateMode)
        return TRUE;

    /*
     * Special case if they selected the "unused" label.
     * Blank out the symbol so it doesn't cause trouble
     * later, force the id to be zero, and then check if
     * the id was changed.
     */
    if (lstrcmp(pszSym, ids(IDS_UNUSED)) == 0) {
        pszSym = szEmpty;
        idNew = IDUNUSED;
        if (idNew != npc->id)
            fIDChanged = TRUE;
    }
    else if (lstrcmp(pszSym, ids(IDS_IDOK)) == 0 &&
            !FindLabel(ids(IDS_IDOK), plInclude)) {
        pszSym = szEmpty;
        idNew = IDOK;
        if (idNew != npc->id)
            fIDChanged = TRUE;
    }
    else if (lstrcmp(pszSym, ids(IDS_IDCANCEL)) == 0 &&
            !FindLabel(ids(IDS_IDCANCEL), plInclude)) {
        pszSym = szEmpty;
        idNew = IDCANCEL;
        if (idNew != npc->id)
            fIDChanged = TRUE;
    }
    /*
     * Is the ID field non-blank?
     */
    else if (*pszID) {
        /*
         * Is the id valid?
         */
        if (!IsValue(pszID)) {
            Message(MSG_BADSYMBOLID);
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSSYMID));
            return FALSE;
        }

        /*
         * Did they change the id value?
         */
        idNew = valtoi(pszID);
        if (idNew != npc->id)
            fIDChanged = TRUE;
    }
    else {
        /*
         * The id field is blank.  This implies they changed it.
         */
        fIDEmpty = TRUE;
        fIDChanged = TRUE;
    }

    /*
     * Is the symbol field blank?
     */
    if (!(*pszSym))
        fSymEmpty = TRUE;

    /*
     * Determine if they have changed the symbol.
     * Did the original id have a symbol associated with it?
     */
    if (npLabel = FindID(npc->id, plInclude)) {
        if (lstrcmp(npLabel->pszLabel, pszSym) != 0)
            fSymChanged = TRUE;
    }
    else {
        /*
         * Since the original id did not have a symbol, if they
         * have entered a symbol it is changed.
         */
        if (!fSymEmpty)
            fSymChanged = TRUE;
    }

    /*
     * Quit if nothing changed.
     */
    if (!fSymChanged && !fIDChanged)
        return TRUE;

    /*
     * Is the symbol field empty?
     */
    if (fSymEmpty) {
        /*
         * If the id field is empty also, return an error.
         */
        if (fIDEmpty) {
            Message(MSG_BADSYMBOLID);
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSSYMID));
            return FALSE;
        }
        else {
            /*
             * Otherwise, go on to the final test.  It doesn't matter
             * if this new id has a symbol or not.
             */
            goto CheckForDups;
        }
    }

    /*
     * At this point we know the symbol field is not empty.
     * Is it a valid symbol?
     */
    if (!IsSymbol(pszSym)) {
        Message(MSG_BADSYMBOLID);
        SetFocus(GetDlgItem(hwndStatus, DID_STATUSSYM));
        return FALSE;
    }

    /*
     * Does this symbol already exist?
     */
    if (npLabel = FindLabel(pszSym, plInclude)) {
        /*
         * Since the symbol exists, if they blanked the id field
         * be friendly and assume they want the matching id value.
         */
        if (fIDEmpty)
            idNew = npLabel->id;

        /*
         * Does the id that is in the id field match the id of the
         * symbol they entered?
         */
        if (npLabel->id == idNew) {
            /*
             * Yes, go on to the final test.
             */
            goto CheckForDups;
        }
        else {
            /*
             * No, give them an error message saying that it has to.
             */
            Myitoa(npLabel->id, szIDTemp);
            Message(MSG_IDSYMMISMATCH, szIDTemp);
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSSYMID));
            return FALSE;
        }
    }
    else {
        /*
         * Since the symbol doesn't exist, if they left the id field
         * blank, assume they want the next available id.
         */
        if (fIDEmpty)
            idNew = NextID(NEXTID_CONTROL, plInclude, 0);

        /*
         * They should add this id/symbol as a label to the
         * include list.
         */
        fAddLabel = TRUE;
        goto CheckForDups;
    }

CheckForDups:
    /*
     * If the id changed, it is not the special "unused" id,
     * and it is a duplicate of another id, don't allow it.
     */
    if (idNew != npc->id && !IsUniqueID(idNew)) {
        Message(MSG_CTRLDUPID);
        SetFocus(GetDlgItem(hwndStatus, DID_STATUSSYM));
        return FALSE;
    }
    else {
        /*
         * Pass back the new id value.
         */
        *pidNew = idNew;

        /*
         * Inform caller whether they should add this id/symbol
         * as a label to the include list.
         */
        *pfAddLabel = fAddLabel;

        /*
         * Return success.
         */
        return TRUE;
    }
}



/************************************************************************
* ApplyNewName
*
* Updates an icon control's or dialog's name with a new name.
*
* If fAddLabel is TRUE, pszSym is assumed to be a string that is
* the symbol to be added and idNew contains the value associated with
* it.  This will cause a new define to be added to the current include
* file.  In either event, pszName will contain either a new name or
* an ordinal that is to be the name.  For dialogs, the dialog name will
* be updated.  For icon controls, the icon's text is updated with the
* name/ordinal.
*
* Arguments:
*   NPCTYPE npc     = Pointer to the control.
*   LPTSTR pszName  = New name.  A string or ordinal.
*   BOOL fAddLabel  = TRUE if a new label for this id/name should be added.
*   LPTSTR pszSym   = The new symbol to add (only valid if fAddLabel is TRUE).
*   INT idNew       = The new ID (only valid if fAddLabel is TRUE).
*
* Returns:
*
* TRUE if successful; otherwise, FALSE is returned.
*
************************************************************************/

STATICFN BOOL ApplyNewName(
    NPCTYPE npc,
    LPTSTR pszName,
    BOOL fAddLabel,
    LPTSTR pszSym,
    INT idNew)
{
    LPTSTR psz;

    if (fAddLabel) {
        /*
         * Go ahead and quietly add the label for them.
         */
        if (AddLabel(pszSym, idNew, FPOS_MAX, 0, &plInclude,
                &plDelInclude, NULL, NULL)) {
            /*
             * Return the controls new id, update the status
             * windows symbol combo box, mark the fact that
             * we have changed the include file, and return
             * success.
             */
            gfIncChged = TRUE;
            StatusFillSymbolList(plInclude);
        }
        else {
            /*
             * An error occurred on the AddLabel.  The most likely
             * cause of this is if they are trying to add a symbol
             * with a duplicate id.
             */
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSNAMEID));
            return FALSE;
        }
    }

    switch (npc->pwcd->iType) {
        case W_ICON:
            /*
             * The resource name for an icon is stored in it's text.
             */
            ApplyNewText(npc, pszName);
            break;

        case W_DIALOG:
            /*
             * We are done if the name was not changed.
             */
            if (NameOrdCmp(gcd.pszDlgName, pszName) == 0)
                break;

            /*
             * Allocate room for the new name (it can be an ordinal!).
             */
            if (!(psz = MyAlloc(NameOrdLen(pszName))))
                return FALSE;

            NameOrdCpy(psz, pszName);

            if (gcd.pszDlgName)
                MyFree(gcd.pszDlgName);

            gcd.pszDlgName = psz;

            /*
             * If the new name is an ordinal, update the base id of
             * the dialog with it.
             */
            if (IsOrd(gcd.pszDlgName))
                gcd.npc->id = OrdID(gcd.pszDlgName);

            /*
             * Finally, set the "changed" flag.
             */
            gfResChged = gfDlgChanged = TRUE;

            break;
    }

    return TRUE;
}



/************************************************************************
* ApplyNewID
*
* Updates the control with the new id.
*
* Arguments:
*   NPCTYPE npc    = Pointer to the control.
*   INT idNew      = The ID.
*   BOOL fAddLabel = TRUE if a new label for this id/symbol should be added.
*   LPTSTR pszSym  = Symbol to add if fAddLabel is TRUE.
*
* Returns:
*
* TRUE if successful; otherwise, FALSE is returned.
*
************************************************************************/

STATICFN BOOL ApplyNewID(
    NPCTYPE npc,
    INT idNew,
    BOOL fAddLabel,
    LPTSTR pszSym)
{
    if (fAddLabel) {
        /*
         * Go ahead and quietly add the label for them.
         */
        if (AddLabel(pszSym, idNew, FPOS_MAX, 0, &plInclude,
                &plDelInclude, NULL, NULL)) {
            /*
             * Return the controls new id, update the status
             * windows symbol combo box, mark the fact that
             * we have changed the include file, and return
             * success.
             */
            gfIncChged = TRUE;
            StatusFillSymbolList(plInclude);
        }
        else {
            /*
             * An error occurred on the AddLabel.  The most likely
             * cause of this is if they are trying to add a symbol
             * with a duplicate id.
             */
            SetFocus(GetDlgItem(hwndStatus, DID_STATUSSYMID));
            return FALSE;
        }
    }

    /*
     * Return if the id wasn't changed.
     */
    if (npc->id == idNew)
        return TRUE;

    /*
     * Update the controls id and set the resource changed flag.
     */
    npc->id = idNew;
    gfResChged = gfDlgChanged = TRUE;
    return TRUE;
}



/************************************************************************
* ApplyNewText
*
* Sets the new text from the processing of the OK command from
* the Status ribbon window.  If the text was changed, the selected
* control will have its text updated.
*
* Arguments:
*   NPCTYPE npc       = Pointer to the control.
*   LPTSTR pszNewText = The new text.
*
* Returns:
*
* TRUE if successful, FALSE otherwise.
*
************************************************************************/

STATICFN BOOL ApplyNewText(
    NPCTYPE npc,
    LPTSTR pszNewText)
{
    INT cb;
    LPTSTR psz;

    /*
     * Did the text change?
     */
    if ((npc->text == NULL && *pszNewText) ||
            (npc->text != NULL && NameOrdCmp(npc->text, pszNewText) != 0)) {
        if (*pszNewText) {
            cb = NameOrdLen(pszNewText);

            if (npc->text == NULL)
                psz = MyAlloc(cb);
            else
                psz = MyRealloc(npc->text, cb);

            if (psz == NULL) {
                return FALSE;
            }
            else {
                NameOrdCpy(psz, pszNewText);
                npc->text = psz;
            }
        }
        else {
            if (npc->text) {
                MyFree(npc->text);
                npc->text = NULL;
            }
        }

        /*
         * Change the text of the control, except for W_ICON controls
         * because they have resource names.  In that case, we don't
         * want to change the text of the actual control because
         * the resource would probably not be found.
         */
        if (npc->pwcd->iType != W_ICON) {
            SetWindowText(npc->hwnd, pszNewText);

            /*
             * Redraw the control after the text changed.  This is
             * necessary because changing the text probably caused
             * the drag handles to get overwritten.
             */
            if (npc->pwcd->iType == W_DIALOG)
                /*
                 * Redraw the frame, which just overwrote the drag
                 * handles when the text was changed.
                 */
                SetWindowPos(npc->hwnd, NULL, 0, 0, 0, 0,
                        SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOMOVE |
                        SWP_NOSIZE | SWP_NOZORDER);
            else
                /*
                 * Redraw the control(s).
                 */
                RedrawSelection();
        }

        gfResChged = gfDlgChanged = TRUE;
    }

    return TRUE;
}



/************************************************************************
* StatusFillSymbolList
*
* This function fills the Symbol and Name combo boxes with
* the given list of labels.
*
* Arguments:
*   NPLABEL plHead - pointer to the list of labels.
*
************************************************************************/

VOID StatusFillSymbolList(
    NPLABEL plHead)
{
    NPLABEL npLabel;
    HWND hwndSymCB;
    HWND hwndNameCB;
    HCURSOR hcurSave;

    hcurSave = SetCursor(hcurWait);

    /*
     * Get the handles to the combo boxes and clear out all items.
     */
    hwndSymCB = GetDlgItem(hwndStatus, DID_STATUSSYM);
    hwndNameCB = GetDlgItem(hwndStatus, DID_STATUSNAME);
    SendMessage(hwndSymCB, CB_RESETCONTENT, 0, 0L);
    SendMessage(hwndNameCB, CB_RESETCONTENT, 0, 0L);

    /*
     * Fill the combo boxes with the items.
     */
    for (npLabel = plHead; npLabel; npLabel = npLabel->npNext) {
        SendMessage(hwndSymCB, CB_ADDSTRING, 0, (DWORD)npLabel->pszLabel);
        SendMessage(hwndNameCB, CB_ADDSTRING, 0, (DWORD)npLabel->pszLabel);
    }

    /*
     * Put some special entries into the symbol combo.  Note that we allow
     * the user to have defined IDOK and IDCANCEL to be something else,
     * in which case we will NOT add these special entries after all.
     */
    SendMessage(hwndSymCB, CB_ADDSTRING, 0, (DWORD)ids(IDS_UNUSED));

    if (!FindLabel(ids(IDS_IDOK), plInclude))
        SendMessage(hwndSymCB, CB_ADDSTRING, 0, (DWORD)ids(IDS_IDOK));

    if (!FindLabel(ids(IDS_IDCANCEL), plInclude))
        SendMessage(hwndSymCB, CB_ADDSTRING, 0, (DWORD)ids(IDS_IDCANCEL));

    SetCursor(hcurSave);
}



/************************************************************************
* StatusSetCoords
*
* This function sets the coordinates in the status display
* to the given rectangle.
*
* Arguments:
*    PRECT prc - coordinates.
*
************************************************************************/

VOID StatusSetCoords(
    PRECT prc)
{
    static INT xSave = 0x7FFF;
    static INT ySave = 0x7FFF;
    static INT cxSave = 0x7FFF;
    static INT cySave = 0x7FFF;
    static INT x2Save = 0x7FFF;
    static INT y2Save = 0x7FFF;
    TCHAR szBuf[CCHTEXTMAX];

    if (prc) {
        if (prc->bottom - prc->top != cySave) {
            /*
             *  Save it for the next time.
             */
            cySave = prc->bottom - prc->top;

            wsprintf(szBuf, ids(IDS_CYFMTSTR), cySave);
            SetDlgItemText(hwndStatus, DID_STATUSCY, szBuf);
        }

        if (prc->right != x2Save || prc->bottom != y2Save) {
            /*
             *  Save them for the next time.
             */
            x2Save = prc->right;
            y2Save = prc->bottom;

            wsprintf(szBuf, L"(%d, %d)", x2Save, y2Save);
            SetDlgItemText(hwndStatus, DID_STATUSX2Y2, szBuf);
        }

        if (prc->left != xSave || prc->top != ySave) {
            /*
             *  Save them for the next time.
             */
            xSave = prc->left;
            ySave = prc->top;

            wsprintf(szBuf, L"(%d, %d)", xSave, ySave);
            SetDlgItemText(hwndStatus, DID_STATUSXY, szBuf);
        }

        if (prc->right - prc->left != cxSave) {
            /*
             *  Save it for the next time.
             */
            cxSave = prc->right - prc->left;

            wsprintf(szBuf, ids(IDS_CXFMTSTR), cxSave);
            SetDlgItemText(hwndStatus, DID_STATUSCX, szBuf);
        }
    }
    else {
        /*
         * Clear the fields.
         */
        SetDlgItemText(hwndStatus, DID_STATUSXY, szEmpty);
        SetDlgItemText(hwndStatus, DID_STATUSX2Y2, szEmpty);
        SetDlgItemText(hwndStatus, DID_STATUSCX, szEmpty);
        SetDlgItemText(hwndStatus, DID_STATUSCY, szEmpty);

        /*
         * Reset the cache variables so that the next "set" of
         * the coords is sure to update the display fields.
         */
        xSave =
        ySave =
        cxSave =
        cySave =
        x2Save =
        y2Save = 0x7FFF;
    }
}



/************************************************************************
* StatusSetText
*
* This function sets the "Text" field in the status display
* to the given text.
*
* Arguments:
*   LPTSTR pszText - The text to display.  This can be NULL.
*   INT Type       - Type of control.  One of the W_* constants.
*
************************************************************************/

STATICFN VOID StatusSetText(
    LPTSTR pszText,
    INT Type)
{
    /*
     * Protect against getting a null value.
     */
    if (!pszText)
        pszText = szEmpty;

    /*
     * If this is an icon control, set the name instead (the text
     * of an icon is really a resource name).
     */
    if (Type == W_ICON)
        StatusSetName(pszText, Type);
    else
        SetDlgItemText(hwndStatus, DID_STATUSTEXT, pszText);
}



/************************************************************************
* StatusSetTextLabels
*
* This function sets the labels of the descriptive text fields in
* Status window based on the type of control that is selected.
*
* Arguments:
*   INT Type    - Type of control.  One of the W_* constants.
*   BOOL fIsOrd - TRUE if the name is an ordinal.  This is ignored
*                 unless Type is W_DIALOG or W_ICON.
*
************************************************************************/

STATICFN VOID StatusSetTextLabels(
    INT Type,
    BOOL fIsOrd)
{
    WORD ids1;
    WORD ids2;

    switch (Type) {
        case W_DIALOG:
            ids1 = (WORD)(fIsOrd ? IDS_DLGIDLABEL : IDS_DLGNAMELABEL);
            ids2 = IDS_CAPTIONLABEL;
            break;

        case W_ICON:
            ids1 = IDS_SYMBOLLABEL;
            ids2 = (WORD)(fIsOrd ? IDS_ICONIDLABEL : IDS_ICONNAMELABEL);
            break;

        default:
            ids1 = IDS_SYMBOLLABEL;
            ids2 = IDS_TEXTLABEL;
            break;
    }

    SetDlgItemText(hwndStatus, DID_STATUSLABEL1, ids(ids1));
    SetDlgItemText(hwndStatus, DID_STATUSLABEL2, ids(ids2));
}



/************************************************************************
* StatusSetID
*
* Updates the id field of the given label.
*
* Arguments:
*     INT id        = The id.
*     BOOL fSymAlso = Find the corresponding label and update the
*                     symbol field also.
*
************************************************************************/

STATICFN VOID StatusSetID(
    INT id,
    BOOL fSymAlso)
{
    TCHAR szValue[CCHIDMAX + 1];
    NPLABEL npLabel;
    LPTSTR pszLabel;

    Myitoa(id, szValue);
    SetDlgItemText(hwndStatus, DID_STATUSSYMID, szValue);

    if (fSymAlso) {
        /*
         * If a matching label is found, the text is used.
         * Otherwise, if the id is zero, we use the "unused"
         * label.  Otherwise, we just use an empty string.
         */
        if (npLabel = FindID(id, plInclude))
            pszLabel = npLabel->pszLabel;
        else if (id == IDUNUSED)
            pszLabel = ids(IDS_UNUSED);
        else if (id == IDOK && !FindLabel(ids(IDS_IDOK), plInclude))
            pszLabel = ids(IDS_IDOK);
        else if (id == IDCANCEL && !FindLabel(ids(IDS_IDCANCEL), plInclude))
            pszLabel = ids(IDS_IDCANCEL);
        else
            pszLabel = szEmpty;

        SetDlgItemText(hwndStatus, DID_STATUSSYM, pszLabel);
    }
}



/************************************************************************
* StatusSetName
*
* This function sets the "Res. Name" field in the status display
* to the given text.
*
* Arguments:
*
*   LPTSTR pszName - The name to display.  NULL values and ordinals
*                    are handled properly.
*   INT Type       - Type of control.  One of the W_* constants.
*
************************************************************************/

STATICFN VOID StatusSetName(
    LPTSTR pszName,
    INT Type)
{
    NPLABEL npLabel;

    /*
     * Protect against getting a null value.
     */
    if (!pszName)
        pszName = szEmpty;

    /*
     * Does the name represent an ordinal?  If so, set the nameid
     * field to the value and fill in the name field with the
     * associated symbol, if there is one.
     *
     * Note that this does NOT produce a hex value in Hex Mode for
     * dialog names, because rc.exe does not parse hex ordinals
     * for dialog names.
     */
    if (IsOrd(pszName)) {
        StatusSetNameID(OrdID(pszName), Type);

        if (npLabel = FindID(OrdID(pszName), plInclude))
            pszName = npLabel->pszLabel;
        else
            pszName = szEmpty;
    }
    else {
        SetDlgItemText(hwndStatus, DID_STATUSNAMEID, szEmpty);
    }

    SetDlgItemText(hwndStatus, DID_STATUSNAME, pszName);
}



/************************************************************************
* StatusSetNameID
*
* This function sets the id edit field associated with the
* "Res. Name" field in the status display
*
* Arguments:
*
*   INT id   - The id to set into the field.
*   INT Type - Type of control.  One of the W_* constants.
*
************************************************************************/

STATICFN VOID StatusSetNameID(
    INT id,
    INT Type)
{
    TCHAR szValue[CCHIDMAX + 1];

    /*
     * If the current control is a dialog, do NOT produce a hex
     * value, even if the hex mode is on.  RC.Exe doesn't recognize
     * hex values for the dialog name as ordinals.
     */
    if (Type == W_DIALOG)
        itoaw(id, szValue, 10);
    else
        Myitoa(id, szValue);

    SetDlgItemText(hwndStatus, DID_STATUSNAMEID, szValue);
}



/************************************************************************
* StatusClearID
*
* Clears the id of the status window..
*
************************************************************************/

STATICFN VOID StatusClearID(VOID)
{
    SetDlgItemText(hwndStatus, DID_STATUSSYM, szEmpty);
    SetDlgItemText(hwndStatus, DID_STATUSSYMID, szEmpty);
}



/************************************************************************
* StatusClearName
*
* Clears the name of the status window.
*
************************************************************************/

STATICFN VOID StatusClearName(VOID)
{
    SetDlgItemText(hwndStatus, DID_STATUSNAME, szEmpty);
    SetDlgItemText(hwndStatus, DID_STATUSNAMEID, szEmpty);
}



/************************************************************************
* StatusClear
*
* Clears the status window.
*
************************************************************************/

STATICFN VOID StatusClear(VOID)
{
    StatusSetCoords(NULL);
    StatusSetText(NULL, W_NOTHING);
    StatusShowFields(W_NOTHING);
    StatusClearID();
    StatusClearName();
    gfStatusChanged = FALSE;
}



/************************************************************************
* StatusUpdate
*
* Updates the status window to the current selection.
*
************************************************************************/

VOID StatusUpdate(VOID)
{
    if (gnpcSel) {
        StatusSetCoords(&gnpcSel->rc);
        StatusSetText(gnpcSel->text, gnpcSel->pwcd->iType);

        if (gfDlgSelected)
            StatusSetName(gcd.pszDlgName, W_DIALOG);
        else
            StatusSetID(gnpcSel->id, TRUE);

        StatusShowFields(gnpcSel->pwcd->iType);

        gfStatusChanged = FALSE;
    }
    else {
        StatusClear();
    }
}



/************************************************************************
* StatusShowFields
*
* This function shows and hides fields in the Status ribbon based on
* whether the dialog is currently selected or not.  This is necessary
* because dialogs have a name, whereas controls do not, and controls
* have an id that dialogs do not have, etc.
*
* It will also call StatusSetTextLabels to set the text for the
* descriptive labels in front of the fields.
*
* This function should be called whenever it is possible that the current
* selection has been changed.
*
* Arguments:
*   INT Type - Type of control that is selected (W_* constant).
*
************************************************************************/

STATICFN VOID StatusShowFields(
    INT Type)
{
    /*
     * This static caches the type of the selected control.
     */
    static INT TypeSave = W_NOTHING;

    /*
     * We only have special cases for the dialog and for icon controls.
     * All other types are treated the same (we use W_CHECKBOX arbitrarily).
     * This prevents some repainting when switching between controls that
     * have the same layout of fields in the status window.
     */
    if (Type != W_DIALOG && Type != W_ICON)
        Type = W_CHECKBOX;

    if (Type != TypeSave) {
        switch (Type) {
            case W_DIALOG:
                MoveWindow(GetDlgItem(hwndStatus, DID_STATUSNAME),
                        grcTopCombo.left, grcTopCombo.top,
                        grcTopCombo.right - grcTopCombo.left,
                        grcTopCombo.bottom - grcTopCombo.top, TRUE);
                MoveWindow(GetDlgItem(hwndStatus, DID_STATUSNAMEID),
                        grcTopEdit.left, grcTopEdit.top,
                        grcTopEdit.right - grcTopEdit.left,
                        grcTopEdit.bottom - grcTopEdit.top, TRUE);

                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSNAME), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSNAMEID), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSTEXT), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSSYM), SW_HIDE);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSSYMID), SW_HIDE);

                StatusSetTextLabels(Type, IsOrd(gcd.pszDlgName));

                break;

            case W_ICON:
                MoveWindow(GetDlgItem(hwndStatus, DID_STATUSNAME),
                        grcBottomCombo.left, grcBottomCombo.top,
                        grcBottomCombo.right - grcBottomCombo.left,
                        grcBottomCombo.bottom - grcBottomCombo.top, TRUE);
                MoveWindow(GetDlgItem(hwndStatus, DID_STATUSNAMEID),
                        grcBottomEdit.left, grcBottomEdit.top,
                        grcBottomEdit.right - grcBottomEdit.left,
                        grcBottomEdit.bottom - grcBottomEdit.top, TRUE);

                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSNAME), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSNAMEID), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSTEXT), SW_HIDE);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSSYM), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSSYMID), SW_SHOW);

                StatusSetTextLabels(Type,
                        gnpcSel->text ? IsOrd(gnpcSel->text) : FALSE);

                break;

            default:
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSTEXT), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSSYM), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSSYMID), SW_SHOW);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSNAME), SW_HIDE);
                ShowWindow(GetDlgItem(hwndStatus, DID_STATUSNAMEID), SW_HIDE);

                StatusSetTextLabels(Type, FALSE);

                break;
        }

        TypeSave = Type;
    }
}



/************************************************************************
* StatusSetEnable
*
* This routine sets the enable state of the editable controls in the
* status window based on various state globals.
*
* The controls will be disabled on the following conditions:
*
*   1. Nothing is selected.
*   2. We are in Test mode.
*   3. One of the editors own dialogs is up (gfDisabled == TRUE).
*
* In addition, some controls will always be disabled if in Translate
* mode, and the text field will be disabled if this control cannot
* have text.
*
************************************************************************/

VOID StatusSetEnable(VOID)
{
    BOOL fEnableText = TRUE;
    BOOL fEnableSym = TRUE;
    BOOL fEnableName = TRUE;

    if (!gnpcSel || gfTestMode || gfDisabled)
        fEnableText = fEnableSym = fEnableName = FALSE;

    /*
     * Disable the text field if this control cannot have text.
     */
    if (gnpcSel && !gnpcSel->pwcd->fHasText)
        fEnableText = FALSE;

    /*
     * If the dialog is selected and the style does not include
     * a caption, disable the text field.
     */
    if (gfDlgSelected && (gnpcSel->flStyle & WS_CAPTION) != WS_CAPTION)
        fEnableText = FALSE;

    /*
     * Always disable the symbol and name fields if Translating.
     */
    if (gfTranslateMode)
        fEnableSym = fEnableName = FALSE;

    EnableWindow(GetDlgItem(hwndStatus, DID_STATUSTEXT), fEnableText);

    EnableWindow(GetDlgItem(hwndStatus, DID_STATUSSYM), fEnableSym);
    EnableWindow(GetDlgItem(hwndStatus, DID_STATUSSYMID), fEnableSym);

    EnableWindow(GetDlgItem(hwndStatus, DID_STATUSNAME), fEnableName);
    EnableWindow(GetDlgItem(hwndStatus, DID_STATUSNAMEID), fEnableName);
}
