/*
 *  Microsoft Confidential
 *  Copyright (C) Microsoft Corporation 1991
 *  All Rights Reserved.
 *
 *
 *  PIFSUB.C
 *  Misc. subroutines for PIFMGR.DLL
 *
 *  History:
 *  Created 31-Jul-1992 3:30pm by Jeff Parsons
 */


#include "shellprv.h"
#pragma hdrstop

// shell priv can alter the definition of IsDBCSLeadByte!
#if defined(FE_SB)
#ifdef IsDBCSLeadByte
#undef IsDBCSLeadByte
#define IsDBCSLeadByte(x) IsDBCSLeadByteEx(CP_ACP,x)
#endif
#endif

/*
 * Most of the routines in this file will need to stay ANSI.  If a UNICODE
 * version is needed, it is supplied.
 *
 * This is because for the most part, the information in the PIF files
 * is ANSI and needs to stay that way.
 *
 * (RickTu)
 *
 */


/** lstrcpytrimA - copy fixed-length string, trimming trailing blanks
 *
 * INPUT
 *  lpszDst -> destination string
 *  lpszSrc -> fixed-length source string
 *  cchMax = size (in characters) of fixed-length source string
 *
 * OUTPUT
 *  Nothing
 */

void lstrcpytrimA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax)
{
    CHAR ch;
    int cchSave = 0;
    LPSTR lpszSave = lpszDst;
    FunctionName(lstrcpytrim);

    while (cchMax && *lpszSrc) {
        ch = *lpszSrc++;
        if (ch == ' ') {
            if (!cchSave) {
                cchSave = cchMax;
                lpszSave = lpszDst;
            }
        } else
            cchSave = 0;
        *lpszDst = ch;
        if (--cchMax) {
            lpszDst++;
#if (defined(DBCS) || defined(FE_SB))
            if (IsDBCSLeadByte(ch) && --cchMax) {
                *lpszDst++ = *lpszSrc++;
            }
#endif
        }
    }
    while (cchSave--)
        *lpszSave++ = '\0';
    *lpszDst = '\0';
}


/** lstrcpypadA - copy to fixed-length string, appending trailing blanks
 *
 * INPUT
 *  lpszDst -> fixed-length destination string
 *  lpszSrc -> source string
 *  cchMax = size of fixed-length destination string (count of characters)
 *
 * OUTPUT
 *  Nothing
 */

void lstrcpypadA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax)
{
    FunctionName(lstrcpypadA);
    while (cchMax && *lpszSrc) {
        cchMax--;
        *lpszDst++ = *lpszSrc++;
    }
    while (cchMax--) {
        *lpszDst++ = ' ';
    }
}


/** lstrcpyncharA - copy variable-length string, until char
 *
 * INPUT
 *  lpszDst -> fixed-length destination string
 *  lpszSrc -> source string
 *  cchMax = size of fixed-length destination string (count of characters)
 *  ch = character to stop copying at
 *
 * OUTPUT
 *  # of characters copied, excluding terminating NULL
 */

int lstrcpyncharA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax, CHAR ch)
{
    int cch = 0;
    FunctionName(lstrcpyncharA);

    while (--cchMax && *lpszSrc && *lpszSrc != ch) {
#if (defined(DBCS) || defined(FE_SB))
        if (IsDBCSLeadByte(*lpszSrc)) {
            cch++;
            *lpszDst++ = *lpszSrc++;
            if (!*lpszSrc) break;   /* Eek!  String ends in DBCS lead byte! */
        }
#endif
        cch++;
        *lpszDst++ = *lpszSrc++;
    }
    *lpszDst = '\0';
    return cch;
}


/** lstrskipcharA - skip char in variable-length string
 *
 * INPUT
 *  lpszSrc -> source string
 *  ch = character to skip
 *
 * OUTPUT
 *  # of characters skipped, 0 if none
 */

int lstrskipcharA(LPCSTR lpszSrc, CHAR ch)
{
    int cch = 0;
    FunctionName(lstrskipcharA);

    while (*lpszSrc && *lpszSrc == ch) {
        cch++;
        lpszSrc++;
    }
    return cch;
}

/** lstrskiptocharA - skip *to* char in variable-length string
 *
 * INPUT
 *  lpszSrc -> source string
 *  ch = character to skip *to*
 *
 * OUTPUT
 *  # of characters skipped, 0 if none;  if char didn't exist, then all
 *  characters are skipped.
 */

int lstrskiptocharA(LPCSTR lpszSrc, CHAR ch)
{
    int cch = 0;
    FunctionName(lstrskiptocharA);

    while (*lpszSrc && *lpszSrc != ch) {
        cch++;
        lpszSrc++;
    }
    return cch;
}


/** lstrcpyfnameA - copy filename appropriately
 *
 * INPUT
 *  lpszDst -> output buffer
 *  lpszSrc -> source filename
 *  cbMax = size of output buffer
 *
 * OUTPUT
 *  # of characters copied, including quotes if any, excluding terminating NULL
 */

int lstrcpyfnameA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax)
{
    int cch;
    CHAR ch;
    FunctionName(lstrcpyfnameA);

    if (TEXT('\0') != (ch = lpszSrc[lstrskiptocharA(lpszSrc, ' ')])) {
        cchMax -= 2;
        *lpszDst++ = '\"';
    }

    lstrcpynA(lpszDst, lpszSrc, cchMax);
    cch = lstrlenA(lpszDst);

    if (ch) {
        lpszDst[cch++] = '\"';
        lpszDst[cch] = 0;
    }
    return cch;
}


/** lstrunquotefnameA - unquote filename if it contains quotes
 *
 * INPUT
 *  lpszDst -> output buffer
 *  lpszSrc -> source filename (quoted or unquoted)
 *  cchMax = size of output buffer (count of characters)
 *  fShort = TRUE if filename should be converted to 8.3 (eg, for real-mode);
 *           -1 if the filename is known to not be quoted and should just be converted
 * OUTPUT
 *  # of characters copied, excluding terminating NULL
 */

int lstrunquotefnameA(LPSTR lpszDst, LPCSTR lpszSrc, int cchMax, BOOL fShort)
{
    int cch;
    FunctionName(lstrunquotefnameA);

    if (fShort != -1) {

        if (lpszSrc[0] == '\"') {
            cch = lstrcpyncharA(lpszDst, lpszSrc+1, cchMax, '\"');
        }
        else {
            cch = lstrcpyncharA(lpszDst, lpszSrc, cchMax, ' ');
        }
        lpszSrc = lpszDst;
    }
    if (fShort) {
        cch = 1;
        CharToOemA(lpszSrc, lpszDst);
        cch = GetShortPathNameA( lpszSrc, lpszDst, cchMax );
        if (cch) {                       // if no error...
            if (fShort == TRUE) {       // if conversion for real-mode...
                if ((int)GetFileAttributesA(lpszDst) == -1) {
                                        // if filename doesn't exist,
                                        // then just copy the 8.3 portion
                                        // and hope the user's real-mode PATH
                                        // ultimately finds it!

                    if (NULL != (lpszSrc = StrRChrA(lpszDst, NULL, '\\'))) {
                        lstrcpyA(lpszDst, lpszSrc+1);
                    }
                }
            }
            cch = lstrlenA(lpszDst);      // recompute the length of the string
        }
    }
    return cch;
}


/** lstrskipfnameA - skip filename in string
 *
 * INPUT
 *  lpszSrc -> string beginning with filename (quoted or unquoted)
 *
 * OUTPUT
 *  # of characters skipped, 0 if none
 */

int lstrskipfnameA(LPCSTR lpszSrc)
{
    int cch = 0;
    FunctionName(lstrskipfname);

    if (lpszSrc[0] == '\"') {
        cch = lstrskiptocharA(lpszSrc+1, '\"') + 1;
        if (lpszSrc[cch] == '\"')
            cch++;
    }
    else
        cch = lstrskiptocharA(lpszSrc, ' ');
    return cch;
}


/*
 * NOTE! The careful definitions of achBuf and achFmt, so that
 * we can support total output of 2 * MAX_STRING_SIZE bytes.
 */
int cdecl Warning(HWND hwnd, WORD id, WORD type, ...)
{
    LPCTSTR lpchFmt;
    PPROPLINK ppl = NULL;
    TCHAR achBuf[2*MAX_STRING_SIZE];
#define achFmt (&achBuf[MAX_STRING_SIZE])
    va_list ArgList;
    FunctionName(Warning);

    lpchFmt = achFmt;

    // We never use MB_FOCUS to mean whatever it's really supposed
    // to mean;  we just use it as a kludge to support warning dialogs
    // when all we have is a ppl, not an hwnd.

    if (type & MB_NOFOCUS) {
        ppl = (PPROPLINK)hwnd;
        hwnd = NULL;
        type &= ~MB_NOFOCUS;
    }
    else if (hwnd)
        ppl = ((PPROPLINK)GetWindowLongPtr(hwnd, DWLP_USER))->ppl;

    if (id == IDS_ERROR + ERROR_NOT_ENOUGH_MEMORY)
        lpchFmt = TEXT("");
    else {
        if (!LoadString(g_hinst, id, achFmt, MAX_STRING_SIZE)) {
            ASSERTFAIL();
            lpchFmt = TEXT("");
        }
    }

    va_start(ArgList,type);
    wvnsprintf(achBuf, MAX_STRING_SIZE, lpchFmt, ArgList);
    va_end(ArgList);

    lpchFmt = NULL;
    if (ppl) {
        ASSERTTRUE(ppl->iSig == PROP_SIG);
        if (!(lpchFmt = ppl->lpszTitle))
            lpchFmt = ppl->szPathName+ppl->iFileName;
    }
    return MessageBox(hwnd, achBuf, lpchFmt, type);
}
#undef achFmt

int MemoryWarning(HWND hwnd)
{
    FunctionName(MemoryWarning);
    return Warning(hwnd, IDS_ERROR + ERROR_NOT_ENOUGH_MEMORY, MB_ICONEXCLAMATION | MB_OK);
}


LPTSTR LoadStringSafe(HWND hwnd, UINT id, LPTSTR lpsz, int cchsz)
{
    FunctionName(LoadStringSafe);
    if (!LoadString(g_hinst, id, lpsz, cchsz)) {
        ASSERTFAIL();
        if (hwnd) {
            MemoryWarning(hwnd);
            return NULL;
        }
        lpsz = TEXT("");
    }
    return lpsz;
}


/** SetDlgBits - Check various dialog checkboxes according to given flags
 *
 * INPUT
 *  hDlg   = HWND of dialog box
 *  pbinf -> array of bitinfo descriptors
 *  cbinf  = size of array
 *  wFlags = flags
 *
 * OUTPUT
 *  Returns NOTHING
 */

void SetDlgBits(HWND hDlg, PBINF pbinf, UINT cbinf, WORD wFlags)
{
    FunctionName(SetDlgBits);

    ASSERTTRUE(cbinf > 0);
    do {
        ASSERTTRUE((pbinf->bBit & 0x3F) < 16);
        CheckDlgButton(hDlg, pbinf->id,
                       !!(wFlags & (1 << (pbinf->bBit & 0x3F))) == !(pbinf->bBit & 0x80));
    } while (++pbinf, --cbinf);
}


/** GetDlgBits - Set various flags according to dialog checkboxes
 *
 * INPUT
 *  hDlg   = HWND of dialog box
 *  pbinf -> array of bitinfo descriptors
 *  cbinf  = size of array
 *  lpwFlags -> flags word
 *
 * OUTPUT
 *  Returns NOTHING
 */

void GetDlgBits(HWND hDlg, PBINF pbinf, UINT cbinf, LPWORD lpwFlags)
{
    WORD wFlags;
    FunctionName(GetDlgBits);

    ASSERTTRUE(cbinf > 0);
    wFlags = *lpwFlags;
    do {
        ASSERTTRUE((pbinf->bBit & 0x3F) < 16);

        if (pbinf->bBit & 0x40)         // 0x40 is a special bit mask
            continue;                   // that means "set but don't get
                                        // this control's value"
        wFlags &= ~(1 << (pbinf->bBit & 0x3F));
        if (!!IsDlgButtonChecked(hDlg, pbinf->id) == !(pbinf->bBit & 0x80))
            wFlags |= (1 << (pbinf->bBit & 0x3F));

    } while (++pbinf, --cbinf);
    *lpwFlags = wFlags;
}


/** SetDlgInts - Set various edit controls according to integer fields
 *
 * INPUT
 *  hDlg   = HWND of dialog box
 *  pvinf -> array of validation info descriptors
 *  cvinf  = size of array
 *  lp    -> structure of integers
 *
 * OUTPUT
 *  Returns NOTHING
 */

void SetDlgInts(HWND hDlg, PVINF pvinf, UINT cvinf, LPVOID lp)
{
    WORD wMin, wMax;
    FunctionName(SetDlgInts);

    ASSERTTRUE(cvinf > 0);
    do {
        wMin = wMax = *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off);

        if (pvinf->fbOpt & VINF_AUTO) {

            SendDlgItemMessage(hDlg, pvinf->id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)g_szAuto);

            AddDlgIntValues(hDlg, pvinf->id, pvinf->iMax);

            if (wMin == 0) {
                SetDlgItemText(hDlg, pvinf->id, g_szAuto);
                continue;
            }
        }
        if (pvinf->fbOpt & VINF_AUTOMINMAX) {

            SendDlgItemMessage(hDlg, pvinf->id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)g_szAuto);
            SendDlgItemMessage(hDlg, pvinf->id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)g_szNone);

            AddDlgIntValues(hDlg, pvinf->id, pvinf->iMax);

            // When AUTOMINMAX is set, we assume that the field
            // we're validating is followed in its structure by a
            // corresponding max WORD.

            wMax = *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off + sizeof(WORD));

            if (wMin == 0 && wMax == 0) {
                SetDlgItemText(hDlg, pvinf->id, g_szNone);
                continue;
            }

            // Let's try to simplify things by mapping 0xFFFF (aka -1)
            // to settings that mean "Auto"

            if (wMin == 0xFFFF || wMax == 0xFFFF) {
                wMin = 0;
                wMax = (WORD)pvinf->iMax;
            }

            if (wMax == (WORD)pvinf->iMax) {
                SetDlgItemText(hDlg, pvinf->id, g_szAuto);
                continue;
            }

            if (wMin != wMax) {
                //
                // We're in a bit of a quandary here.  The settings show
                // explicit min and max values which are not equal, probably
                // due to settings inherited from a 3.1 PIF file.  We'll
                // just go with the wMax value.  Fortunately for us, we
                // don't actually have to *do* anything to make this happen.
                //
            }
        }
        SetDlgItemInt(hDlg, pvinf->id, wMin, pvinf->iMin < 0);

    } while (++pvinf, --cvinf);
}


/** AddDlgIntValues - Fill integer combo-box with appropriate values
 *
 * INPUT
 *  hDlg   = HWND of dialog box
 *  id     = dialog control ID
 *  iMax   = maximum value
 *
 * OUTPUT
 *  Returns NOTHING
 */

void AddDlgIntValues(HWND hDlg, int id, int iMax)
{
    int iStart, iInc;
    TCHAR achValue[16];

    // HACK to make this do something sensible with the environment max;
    // they can still enter larger values (up to ENVSIZE_MAX) but I don't
    // see any sense in encouraging it. -JTP

    if ((WORD)iMax == ENVSIZE_MAX)
        iMax = 4096;

    if ((iMax < 0) || (iMax == 0xFFFF)) // HACK to make this do something sensible
        iMax = 16384;           // with fields that allow huge maximums -JTP

    iStart = iInc = iMax/16;    // arbitrarily chop the range up 16 times

    while (iStart <= iMax) {
        wsprintf(achValue, TEXT("%d"), iStart);
        SendDlgItemMessage(hDlg, id, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)achValue);
        iStart += iInc;
    }
}


/** GetDlgInts - Set various integer fields according to dialog edit controls
 *
 * INPUT
 *  hDlg   = HWND of dialog box
 *  pvinf -> array of validation info descriptors
 *  cvinf  = size of array
 *  lp    -> structure of integers
 *
 * OUTPUT
 *  Returns NOTHING
 */

void GetDlgInts(HWND hDlg, PVINF pvinf, int cvinf, LPVOID lp)
{
    WORD wMin, wMax;
    UINT uTemp;
    BOOL fSuccess;
    TCHAR achText[32];
    FunctionName(GetDlgInts);

    ASSERTTRUE(cvinf > 0);
    do {
        uTemp = GetDlgItemInt(hDlg, pvinf->id, &fSuccess, pvinf->iMin < 0);
        ASSERT(HIWORD(uTemp)==0);

        wMin = LOWORD(uTemp);

        // In case of error, make sure wMin doesn't actually change

        if (!fSuccess)
            wMin = *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off);

        if (pvinf->fbOpt & VINF_AUTO) {

            GetDlgItemText(hDlg, pvinf->id, achText, ARRAYSIZE(achText));

            if (lstrcmpi(achText, g_szAuto) == 0) {
                wMin = 0;
            }
        }

        if (pvinf->fbOpt & VINF_AUTOMINMAX) {

            // When AUTOMINMAX is set, we assume that the field
            // we're validating is followed in its structure by a
            // corresponding max WORD, which we will ZERO if the
            // user selects NONE, or set to its MAXIMUM if the user
            // selects AUTO, or otherwise set to match the specified
            // MINIMUM.

            wMax = wMin;

            GetDlgItemText(hDlg, pvinf->id, achText, ARRAYSIZE(achText));

            if (lstrcmpi(achText, g_szAuto) == 0) {
                wMin = 0;
                wMax = (WORD)pvinf->iMax;
            }
            else if (lstrcmpi(achText, g_szNone) == 0) {
                wMin = 0;
                wMax = 0;
            }

            *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off + sizeof(WORD)) = wMax;
        }

        *(WORD UNALIGNED *)((LPBYTE)lp + pvinf->off) = wMin;

    } while (++pvinf, --cvinf);
}


/** ValidateDlgInts - Validate that integer fields are value
 *
 * INPUT
 *  hDlg   = HWND of dialog box
 *  pvinf -> array of validation descriptors
 *  cvinf  = size of array
 *
 * OUTPUT
 *  Returns TRUE if something is wrong; FALSE if all is okay.
 */

BOOL ValidateDlgInts(HWND hDlg, PVINF pvinf, int cvinf)
{
    DWORD dw;
    BOOL fSuccess;
    TCHAR achText[32];
    FunctionName(ValidateDlgInts);

    ASSERTTRUE(cvinf > 0);
    do {
        dw = GetDlgItemInt(hDlg, pvinf->id, &fSuccess, pvinf->iMin < 0);

        // NOTE: AUTO is for "Auto" only, whereas AUTOMINMAX is for
        // "Auto" and "None".  However, in the interest of simplicity, I
        // don't complain if either string is used in either case.

        if (pvinf->fbOpt & (VINF_AUTO | VINF_AUTOMINMAX)) {
            if (!fSuccess) {
                GetDlgItemText(hDlg, pvinf->id, achText, ARRAYSIZE(achText));
                if (lstrcmpi(achText, g_szNone) == 0 ||
                    lstrcmpi(achText, g_szAuto) == 0) {
                    continue;   // things be lookin' good, check next int...
                }
            }
        }
        if (!fSuccess || dw < (DWORD)pvinf->iMin || dw > (DWORD)pvinf->iMax) {
            Warning(hDlg, pvinf->idMsg, MB_ICONEXCLAMATION | MB_OK, pvinf->iMin, pvinf->iMax);
            SendDlgItemMessage(hDlg, pvinf->id, EM_SETSEL, 0, MAKELPARAM(0,-1));
            SetFocus(GetDlgItem(hDlg, pvinf->id));
            return TRUE;        // things be lookin' bad, bail out...
        }
    } while (++pvinf, --cvinf);
    return FALSE;
}


/*
 * NOTE -- The compiler emits really bad code for some of these guys.
 * In those cases, we are merely wrapping a call; there is no need to save BP.
 */


/** LimitDlgItemText - Sets the limit for a dialog edit control
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  iCtl = ID of control
 *  uiLimit = text limit
 *
 * OUTPUT
 *  None.
 */
void LimitDlgItemText(HWND hDlg, int iCtl, UINT uiLimit)
{
    FunctionName(LimitDlgItemText);

    SendDlgItemMessage(hDlg, iCtl, EM_LIMITTEXT, uiLimit, 0);
}


/** SetDlgItemPosRange - Sets the pos and range for a dialog slider control
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  iCtl = ID of control
 *  uiPos = Current position
 *  dwRange = Range (min in low word, max in high word)
 *
 * OUTPUT
 *  None.
 */
void SetDlgItemPosRange(HWND hDlg, int iCtl, UINT uiPos, DWORD dwRange)
{
    FunctionName(SetDlgItemPosRange);

    SendDlgItemMessage(hDlg, iCtl, TBM_SETRANGE, 0, dwRange);
    SendDlgItemMessage(hDlg, iCtl, TBM_SETPOS, TRUE, uiPos);
}


/** GetDlgItemPos - Gets the pos of a dialog slider control
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  iCtl = ID of control
 *
 * OUTPUT
 *  Trackbar position.
 */
UINT GetDlgItemPos(HWND hDlg, int iCtl)
{
    FunctionName(GetDlgItemPos);

    return (UINT)SendDlgItemMessage(hDlg, iCtl, TBM_GETPOS, 0, 0);
}


/** SetDlgItemPct - Sets the pos for a dialog slider control that measures %
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  iCtl = ID of control
 *  uiPct = Current position (range 0 .. 100)
 *
 * OUTPUT
 *  None.
 */
void SetDlgItemPct(HWND hDlg, int iCtl, UINT uiPct)
{
    FunctionName(SetDlgItemPct);

    SetDlgItemPosRange(hDlg, iCtl, uiPct / (100/NUM_TICKS), MAKELONG(0, NUM_TICKS));
}


/** GetDlgItemPct - Gets the pos of a dialog slider control that measures %
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  iCtl = ID of control
 *
 * OUTPUT
 *  Slider position in the range 0 .. 100.
 */
UINT GetDlgItemPct(HWND hDlg, int iCtl)
{
    FunctionName(GetDlgItemPct);

    return GetDlgItemPos(hDlg, iCtl) * (100/NUM_TICKS);
}


/** EnableDlgItems - Enables or disables a collection of dialog controls
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  pbinf -> array of bitinfo descriptors
 *  cbinf  = size of array
 *  fEnable = whether the control should be enabled or disabled
 *
 * OUTPUT
 *  Dialog items have changed state.
 */
void EnableDlgItems(HWND hDlg, PBINF pbinf, int cbinf, BOOL fEnable)
{
    FunctionName(EnableDlgItems);

    ASSERTTRUE(cbinf > 0);
    do {
        EnableWindow(GetDlgItem(hDlg, pbinf->id), fEnable);
    } while (++pbinf, --cbinf);
}


/** DisableDlgItems - Disables a collection of dialog controls
 *
 * Most of the time, we call EnableDlgItems with fEnable = TRUE, so
 * this is a handy wrapper.  (Too bad the compiler emits awful code.)
 *
 * INPUT
 *  hDlg = HWND of dialog box
 *  pbinf -> array of bitinfo descriptors
 *  cbinf  = size of array
 *
 * OUTPUT
 *  Dialog items have been disabled.
 */
void DisableDlgItems(HWND hDlg, PBINF pbinf, int cbinf)
{
    FunctionName(DisableDlgItems);

    EnableDlgItems(hDlg, pbinf, cbinf, 0);
}


/** AdjustRealModeControls - Disables selected items if single-app mode
 *
 *  If the proplink says that "single-application mode" is enabled,
 *  then hide all controls whose IDs are less than 4000 and show all
 *  controls whose IDs are greater than or equal to 5000.  Controls whose
 *  IDs are in the 4000's are immune to all this hiding/showing.  Controls
 *  in the 3000's are actually disabled rather than hidden.  Controls in
 *  the 6000's are actually disabled rather than hidden as well.
 *
 *  RST: Ok, this is nice in theory, but now that we've pulled over this
 *       stuff into shell32.dll, we'll have to go off the actual IDC_
 *       defines instead of the magic #'s of 3000, 4000 and 5000.
 *
 *       IDC_ICONBMP        == 3001
 *       IDC_PIF_STATIC     == 4000
 *       IDC_REALMODEISABLE == 5001
 *
 *       So, when adding things to shell232.rc or ids.h, plan
 *       accordingly.
 *
 * INPUT
 *  ppl = proplink
 *  hDlg = HWND of dialog box
 *
 * OUTPUT
 *  Dialog items have been disabled/enabled shown/hidden.
 *  Returns nonzero if we are in normal (not single-app) mode.
 */

BOOL CALLBACK EnableEnumProc(HWND hwnd, LPARAM lp)
{
    int f;
    LONG l;

    f = SW_SHOW;
    l = GetWindowLong(hwnd, GWL_ID);

    if (!LOWORD(lp) && l < IDC_PIF_STATIC || LOWORD(lp) && l >= IDC_REALMODEDISABLE)
        f = SW_HIDE;

    if (l < IDC_ICONBMP || l >= IDC_PIF_STATIC && l < IDC_CONFIGLBL)
        ShowWindow(hwnd, f);
    else
        EnableWindow(hwnd, f == SW_SHOW);

    return TRUE;
}


BOOL AdjustRealModeControls(PPROPLINK ppl, HWND hDlg)
{
    BOOL fNormal;
    FunctionName(AdjustRealModeControls);

    fNormal = !(ppl->flProp & PROP_REALMODE);
    EnumChildWindows(hDlg, EnableEnumProc, fNormal);
    return fNormal;
}


/** OnWmHelp - Handle a WM_HELP message
 *
 *  This is called whenever the user presses F1 or clicks the help
 *  button in the title bar.  We forward the call on to the help engine.
 *
 * INPUT
 *  lparam  = LPARAM from WM_HELP message (LPHELPINFO)
 *  pdwHelp = array of DWORDs of help info
 *
 * OUTPUT
 *
 *  None.
 */

void OnWmHelp(LPARAM lparam, const DWORD *pdwHelp)
{
    FunctionName(OnWmHelp);

    WinHelp((HWND) ((LPHELPINFO) lparam)->hItemHandle, NULL,
            HELP_WM_HELP, (DWORD_PTR) (LPTSTR) pdwHelp);
}

/** OnWmContextMenu - Handle a WM_CONTEXTMENU message
 *
 *  This is called whenever the user right-clicks on a control.
 *  We forward the call on to the help engine.
 *
 * INPUT
 *  wparam  = WPARAM from WM_HELP message (HWND)
 *  pdwHelp = array of DWORDs of help info
 *
 * OUTPUT
 *
 *  None.
 */

void OnWmContextMenu(WPARAM wparam, const DWORD *pdwHelp)
{
    FunctionName(OnWmContextMenu);

    WinHelp((HWND) wparam, NULL, HELP_CONTEXTMENU,
            (DWORD_PTR) (LPTSTR) pdwHelp);
}

#ifdef UNICODE
/** PifMgr_WCtoMBPath - Converts UNICODE path to it's ANSI representation
 *
 *  This is called whenever we need to convert a UNICODE path to it's
 *  best approximation in ANSI.  Sometimes this will be a direct mapping,
 *  but sometimes not.  We may have to use the short name, etc.
 *
 * INPUT
 *  lpUniPath  -> pointer UNICODE path (NULL terminated)
 *  lpAnsiPath -> pointer to buffer to hold ANSI path
 *  cchBuf     -> size of ANSI buffer, in characters
 *
 * OUTPUT
 *
 *  lpAnsiPath buffer contains ANSI representation of lpUniPath
 */

void PifMgr_WCtoMBPath(LPWSTR lpUniPath, LPSTR lpAnsiPath, UINT cchBuf )
{
    WCHAR awchPath[ MAX_PATH ]; // Should be bigger than any PIF string
    CHAR  achPath[ MAX_PATH ];  // Should be bigger than any PIF string
    UINT  cchAnsi = 0;

    FunctionName(PifMgr_WCtoMBPath);

    // Try converting to Ansi and then converting back and comparing.
    // If we get back exactly what we started with, this is the "simple"
    // case.

    cchAnsi = WideCharToMultiByte( CP_ACP, 0,
                                   lpUniPath, -1,
                                   achPath, MAX_PATH,
                                   NULL, NULL );

    if (cchAnsi && (cchAnsi<=cchBuf)) {

        // Now try converting back
        MultiByteToWideChar( CP_ACP, 0,
                             achPath, -1,
                             awchPath, MAX_PATH
                            );

        if (lstrcmp(lpUniPath,awchPath)==0) {

            // We're done...copy over the string.
            lstrcpynA( lpAnsiPath, achPath, cchBuf );
            *(BYTE UNALIGNED *)(lpAnsiPath+cchBuf-1) = '\0';
            return;

        }

        // Well, the string has some unmappable UNICODE
        // character in it, so try option #2 -- using the
        // short path name.
        goto TryShortPathName;

    } else {

TryShortPathName:
        // Hmmm, the best we can do is to use the short path name and map
        // it to ANSI.

        GetShortPathName( lpUniPath, awchPath, MAX_PATH );
        awchPath[ MAX_PATH-1 ] = TEXT('\0');
        WideCharToMultiByte( CP_ACP, 0,
                             awchPath, -1,
                             lpAnsiPath, cchBuf,
                             NULL, NULL
                            );

        // Make sure we're NULL terminated
        *(BYTE UNALIGNED *)(lpAnsiPath+cchBuf-1) = '\0';

    }

}
#endif
