windows-nt/Source/XPSP1/NT/sdktools/tweakui/mycomp.cpp
2020-09-26 16:20:57 +08:00

800 lines
21 KiB
C++

/*
* mycomp - Dialog box property sheet for "My Computer"
*
* For now, we display only Drives.
*/
#include "tweakui.h"
#pragma BEGIN_CONST_DATA
const static DWORD CODESEG rgdwHelp[] = {
IDC_ICONLVTEXT, IDH_GROUP,
IDC_ICONLVTEXT2, IDH_MYCOMP,
IDC_ICONLV, IDH_MYCOMP,
IDC_FLDGROUP, IDH_GROUP,
IDC_FLDNAMETXT, IDH_FOLDERNAME,
IDC_FLDNAMELIST, IDH_FOLDERNAME,
IDC_FLDLOCTXT, IDH_FOLDERNAME,
IDC_FLDLOC, IDH_FOLDERNAME,
IDC_FLDCHG, IDH_FOLDERNAME,
0, 0,
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* CFolderDesc - Describes a single special folder
*
*****************************************************************************/
#define MAKEKL(nm) \
KL const c_kl##nm = { &pcdii->hkCUExplorer, \
c_tszUserShellFolders, c_tsz##nm }
MAKEKL(Desktop);
MAKEKL(Programs);
MAKEKL(Personal);
MAKEKL(Favorites);
MAKEKL(Startup);
MAKEKL(Recent);
MAKEKL(SendTo);
MAKEKL(StartMenu);
MAKEKL(Templates);
KL const c_klMyMusic = { &pcdii->hkCUExplorer, c_tszUserShellFolders, TEXT("My Music") };
KL const c_klMyVideo = { &pcdii->hkCUExplorer, c_tszUserShellFolders, TEXT("My Video") };
KL const c_klMyPictures = { &pcdii->hkCUExplorer, c_tszUserShellFolders, TEXT("My Pictures") };
#undef MAKEKL
KL const c_klProgramFiles = { &g_hkLMSMWCV, 0, c_tszProgramFilesDir };
KL const c_klCommonFiles = { &g_hkLMSMWCV, 0, c_tszCommonFilesDir };
KL const c_klSourcePath = { &g_hkLMSMWCV, c_tszSetup, c_tszSourcePath };
/*
* HACKHACK - Fake some private CSIDL's
* by stealing the various CSIDL_COMMON_* values.
*/
enum {
CSIDL_SOURCEPATH = CSIDL_COMMON_STARTUP,
};
/*
* Declare as a struct so you can initialize it statically.
*/
struct CFolderDesc { /* fldd */
PIDL GetPidl() const;
BOOL SetValue(LPTSTR ptsz) const; /* returns fNeedLogoff */
inline int GetFriendlyName(LPTSTR pszBuf, int cch) const
{
return LoadString(hinstCur, _csidl + IDS_FOLDER_BASE, pszBuf, cch);
}
UINT _csidl;
PKL _pkl;
/*
* These are really private members, but you can't say "private"
* in a struct definition if you want it to be statically initializable.
*/
static BOOL _UnexpandEnvironmentString(LPTSTR ptsz, LPCTSTR ptszEnv);
static void _SetUserShellFolder(LPTSTR ptsz, LPCTSTR ptszSubkey);
};
/*****************************************************************************
*
* CFolderDesc::GetPidl
*
* Wrapper around SHGetSpecialFolderLocation that also knows how
* to read our hacky values.
*
*****************************************************************************/
PIDL
CFolderDesc::GetPidl() const
{
HRESULT hres;
PIDL pidl;
TCHAR tszPath[MAX_PATH];
switch (_csidl) {
case CSIDL_PROGRAM_FILES:
case CSIDL_PROGRAM_FILES_COMMON:
case CSIDL_SOURCEPATH:
if (GetStrPkl(tszPath, cbX(tszPath), _pkl)) {
pidl = pidlSimpleFromPath(tszPath);
} else {
pidl = NULL;
}
break;
default:
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, _csidl, &pidl))) {
} else {
pidl = NULL;
}
break;
}
return pidl;
}
/*****************************************************************************
*
* CFolderDesc::_UnexpandEnvironmentString
*
* If the string begins with the value of the environment string,
* then change it to said string.
*
* Example:
* In: "C:\WINNT\SYSTEM32\FOO.TXT", "%SystemRoot%"
* Out: "%SystemRoot%\SYSTEM32\FOO.TXT"
*
*****************************************************************************/
BOOL
CFolderDesc::_UnexpandEnvironmentString(LPTSTR ptsz, LPCTSTR ptszEnv)
{
TCHAR tszEnv[MAX_PATH];
DWORD ctch;
BOOL fRc;
/*
* Note that NT ExpandEnvironmentStrings returns the wrong
* value, so we can't rely on it.
*/
ExpandEnvironmentStrings(ptszEnv, tszEnv, cA(tszEnv));
ctch = lstrlen(tszEnv);
/*
* Source must be at least as long as the env string for
* us to have a chance of succeeding. This check avoids
* accidentally reading past the end of the source.
*/
if ((DWORD)lstrlen(ptsz) >= ctch) {
if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
tszEnv, ctch, ptsz, ctch) == 2) {
int ctchEnv = lstrlen(ptszEnv);
/*
* Must use hmemcpy to avoid problems with overlap.
*/
hmemcpy(ptsz + ctchEnv, ptsz + ctch,
cbCtch(1 + lstrlen(ptsz + ctch)));
hmemcpy(ptsz, ptszEnv, ctchEnv);
fRc = 1;
} else {
fRc = 0;
}
} else {
fRc = 0;
}
return fRc;
}
/*****************************************************************************
*
* CFolderDesc::_SetUserShellFolder
*
* Don't use REG_EXPAND_SZ if the shell doesn't support it.
*
*****************************************************************************/
void
CFolderDesc::_SetUserShellFolder(LPTSTR ptsz, LPCTSTR ptszSubkey)
{
HKEY hk;
if (g_fShellSz) {
if (!_UnexpandEnvironmentString(ptsz, TEXT("%USERPROFILE%")) &&
!_UnexpandEnvironmentString(ptsz, TEXT("%SystemRoot%"))) {
}
}
if (RegCreateKey(pcdii->hkCUExplorer, c_tszUserShellFolders, &hk) == 0) {
RegSetValueEx(hk, ptszSubkey, 0, g_fShellSz ? REG_EXPAND_SZ
: REG_SZ, (LPBYTE)ptsz,
cbCtch(1 + lstrlen(ptsz)));
RegCloseKey(hk);
}
}
/*****************************************************************************
*
* CFolderDesc::SetValue
*
* Stash the puppy.
*
* Returns nonzero if logoff required.
*
*****************************************************************************/
BOOL
CFolderDesc::SetValue(LPTSTR ptsz) const
{
TCHAR tszDefault[MAX_PATH];
UINT ctch;
BOOL fNeedLogoff = FALSE;
/*
* Is it already in the default location?
*
* Note that the special gizmos don't have default
* locations.
*/
ctch = GetWindowsDirectory(tszDefault, cA(tszDefault));
if (ctch && tszDefault[ctch - 1] != TEXT('\\')) {
tszDefault[ctch++] = TEXT('\\');
}
if (LoadString(hinstCur, _csidl + IDS_DEFAULT_BASE,
&tszDefault[ctch], cA(tszDefault) - ctch)) {
if (lstrcmpi(tszDefault, ptsz) == 0) {
/*
* In default location.
*/
DelPkl(_pkl);
} else {
/*
* In other location.
*
* Note that we cannot use SetStrPkl here, because
* we need to set the value type to REG_EXPAND_SZ.
*
*/
_SetUserShellFolder(ptsz, _pkl->ptszSubkey);
}
fNeedLogoff = TRUE;
} else {
SetStrPkl(_pkl, ptsz);
/*
* On NT5, Program Files and Common Files are CSIDLs,
* and Program Files is an environment variable!
*/
if (g_fNT5 && (_csidl == CSIDL_PROGRAM_FILES ||
_csidl == CSIDL_PROGRAM_FILES_COMMON)) {
fNeedLogoff = TRUE;
}
}
return fNeedLogoff;
}
/*****************************************************************************
*
* CSpecialFolders -- Wangle the "special folders" combobox
*
*****************************************************************************/
static const CFolderDesc c_rgfldd[] = {
{ CSIDL_DESKTOPDIRECTORY, &c_klDesktop },
{ CSIDL_PROGRAMS, &c_klPrograms },
{ CSIDL_PERSONAL, &c_klPersonal },
{ CSIDL_FAVORITES, &c_klFavorites },
{ CSIDL_STARTUP, &c_klStartup },
{ CSIDL_RECENT, &c_klRecent },
{ CSIDL_SENDTO, &c_klSendTo },
{ CSIDL_STARTMENU, &c_klStartMenu },
{ CSIDL_TEMPLATES, &c_klTemplates },
{ CSIDL_MYMUSIC, &c_klMyMusic },
{ CSIDL_MYVIDEO, &c_klMyVideo },
{ CSIDL_MYPICTURES, &c_klMyPictures },
{ CSIDL_PROGRAM_FILES, &c_klProgramFiles },
{ CSIDL_PROGRAM_FILES_COMMON, &c_klCommonFiles },
{ CSIDL_SOURCEPATH, &c_klSourcePath },
};
#define cfldd cA(c_rgfldd)
class CSpecialFolders {
public:
void Init(HWND hwndCombo, HWND hwndText);
void Resync();
void Destroy();
BOOL Apply(); /* returns fNeedLogoff */
void ChangeFolder(HWND hdlg);
private:
typedef struct FOLDERINSTANCE {
BOOL fEdited;
PIDL pidl;
} FINST, *PFINST;
static int CALLBACK _ChangeFolder_Callback(HWND hwnd, UINT wm, LPARAM lp, LPARAM lpRef);
HWND _hwndCombo;
HWND _hwndText; /* IDC_FLDLOC */
BOOL _fWarned; /* Did we warn about changing folders? */
PIDL _pidlEditing; /* Which one is the user editing? */
FINST _rgfinst[cfldd];
};
/*****************************************************************************
*
* CSpecialFolders::Destroy
*
* Free the memory.
*
*****************************************************************************/
void
CSpecialFolders::Destroy()
{
UINT i;
for (i = 0; i < cfldd; i++) {
_rgfinst[i].fEdited = 0;
if (_rgfinst[i].pidl) {
Ole_Free(_rgfinst[i].pidl);
_rgfinst[i].pidl = 0;
}
}
}
/*****************************************************************************
*
* CSpecialFolders::Reset
*
*****************************************************************************/
void
CSpecialFolders::Init(HWND hwndCombo, HWND hwndText)
{
_hwndCombo = hwndCombo;
_hwndText = hwndText;
/*
* Free the old memory, if any.
*/
Destroy();
/*
* Set up the new combobox.
*/
ComboBox_ResetContent(_hwndCombo);
UINT i;
for (i = 0; i < cfldd; i++) {
_rgfinst[i].pidl = c_rgfldd[i].GetPidl();
if (_rgfinst[i].pidl) {
TCHAR tsz[MAX_PATH];
c_rgfldd[i].GetFriendlyName(tsz, cA(tsz));
int iItem = ComboBox_AddString(_hwndCombo, tsz);
ComboBox_SetItemData(_hwndCombo, iItem, i);
}
}
ComboBox_SetCurSel(_hwndCombo, 0);
Resync();
}
/*****************************************************************************
*
* CSpecialFolders::Resync
*
* Update goo since the combo box changed.
*
*****************************************************************************/
void
CSpecialFolders::Resync()
{
LRESULT icsidl = Misc_Combo_GetCurItemData(_hwndCombo);
TCHAR tsz[MAX_PATH];
tsz[0] = TEXT('\0');
SHGetPathFromIDList(_rgfinst[icsidl].pidl, tsz);
SetWindowText(_hwndText, tsz);
}
/*****************************************************************************
*
* CSpecialFolders::Apply
*
* Updating the Folder locations is really annoying, thanks
* to NT's roving profiles.
*
*****************************************************************************/
BOOL
CSpecialFolders::Apply()
{
UINT i;
BOOL fNeedLogoff = FALSE;
for (i = 0; i < cfldd; i++) {
if (_rgfinst[i].fEdited) {
TCHAR tsz[MAX_PATH];
SHGetPathFromIDList(_rgfinst[i].pidl, tsz);
BOOL fNeedLogoffT = c_rgfldd[i].SetValue(tsz);
fNeedLogoff |= fNeedLogoffT;
}
}
return fNeedLogoff;
}
/*****************************************************************************
*
* CSpecialFolders::_ChangeFolder_Callback
*
* Start the user at the old location, and don't let the user pick
* something that collides with something else.
*
*****************************************************************************/
int CALLBACK
CSpecialFolders::_ChangeFolder_Callback(HWND hwnd, UINT wm, LPARAM lp, LPARAM lpRef)
{
CSpecialFolders *self = (CSpecialFolders *)lpRef;
int icsidl;
TCHAR tsz[MAX_PATH];
switch (wm) {
case BFFM_INITIALIZED:
SendMessage(hwnd, BFFM_SETSELECTION, 0, (LPARAM)self->_pidlEditing);
break;
case BFFM_SELCHANGED:
/* Picking nothing is bad */
if (!lp) goto bad;
/* Picking yourself is okay; just don't pick somebody else */
if (ComparePidls((PIDL)lp, self->_pidlEditing) == 0) goto done;
for (icsidl = 0; icsidl < cfldd; icsidl++) {
if (self->_rgfinst[icsidl].pidl &&
ComparePidls(self->_rgfinst[icsidl].pidl, (PIDL)lp) == 0) {
bad:;
SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
goto done;
}
}
/* Don't allow a removable drive */
tsz[1] = TEXT('\0'); /* Not a typo */
SHGetPathFromIDList((PIDL)lp, tsz);
if (tsz[1] == TEXT(':')) {
tsz[3] = TEXT('\0');
if (GetDriveType(tsz) == DRIVE_REMOVABLE) {
goto bad;
}
}
break;
}
done:;
return 0;
}
/*****************************************************************************
*
* CSpecialFolders::ChangeFolder
*
*****************************************************************************/
void
CSpecialFolders::ChangeFolder(HWND hdlg)
{
if (_fWarned ||
MessageBoxId(hdlg, IDS_WARNFOLDERCHANGE, g_tszName,
MB_YESNO | MB_DEFBUTTON2) == IDYES) {
int iItem;
int icsidl;
BROWSEINFO bi;
LPITEMIDLIST pidl;
TCHAR tsz[MAX_PATH];
TCHAR tszTitle[MAX_PATH];
TCHAR tszName[MAX_PATH];
_fWarned = TRUE;
iItem = ComboBox_GetCurSel(_hwndCombo);
ComboBox_GetLBText(_hwndCombo, iItem, tszName);
LoadString(hinstCur, IDS_FOLDER_PATTERN, tsz, cA(tsz));
wsprintf(tszTitle, tsz, tszName);
bi.hwndOwner = hdlg;
bi.pidlRoot = 0;
bi.pszDisplayName = tsz; /* Garbage */
bi.lpszTitle = tszTitle;
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = _ChangeFolder_Callback;
icsidl = (int)ComboBox_GetItemData(_hwndCombo, iItem);
_pidlEditing = _rgfinst[icsidl].pidl;
bi.lParam = (LPARAM)this;
pidl = SHBrowseForFolder(&bi);
if (pidl) {
if (ComparePidls(pidl, _rgfinst[icsidl].pidl) != 0) {
Ole_Free(_rgfinst[icsidl].pidl);
_rgfinst[icsidl].pidl = (PIDL)pidl;
_rgfinst[icsidl].fEdited = TRUE;
Common_SetDirty(hdlg);
Resync();
} else {
Ole_Free(pidl);
}
}
}
}
/*****************************************************************************
*
* My Computer Dialog Info
*
*****************************************************************************/
typedef class _MDI { /* mdi = my computer dialog info */
public:
DWORD dwNoDrives;
DWORD dwValidDrives;
CSpecialFolders _sf;
} MDI, *PMDI;
MDI mdi;
#define pdmi (&mdi)
/*****************************************************************************
*
* MyComp_BuildRoot
*
* Build the root directory of a drive. The buffer must be 4 chars.
*
*****************************************************************************/
LPTSTR PASCAL
MyComp_BuildRoot(LPTSTR ptsz, UINT uiDrive)
{
ptsz[0] = uiDrive + TEXT('A');
ptsz[1] = TEXT(':');
ptsz[2] = TEXT('\\');
ptsz[3] = TEXT('\0');
return ptsz;
}
/*****************************************************************************
*
* MyComp_LV_GetIcon
*
* Produce the icon associated with an item. This is called when
* we need to rebuild the icon list after the icon cache has been
* purged.
*
*****************************************************************************/
#define idiPhantom -11 /* Magic index for disconnected drive */
int PASCAL
MyComp_LV_GetIcon(LPARAM insi)
{
if (pdmi->dwValidDrives & (1 << insi)) {
SHFILEINFO sfi;
TCHAR tszRoot[4]; /* Root directory thing */
SHGetFileInfo(MyComp_BuildRoot(tszRoot, (UINT)insi), 0, &sfi, cbX(sfi),
SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
return sfi.iIcon;
} else {
if (g_fNT) {
UnicodeFromPtsz(wsz, g_tszPathShell32);
return mit.Shell_GetCachedImageIndex(wsz, idiPhantom, 0);
} else {
return mit.Shell_GetCachedImageIndex(g_tszPathShell32,
idiPhantom, 0);
}
}
}
/*****************************************************************************
*
* MyComp_OnInitDialog
*
* For now, just populate with each physical local drive.
*
*****************************************************************************/
BOOL PASCAL
MyComp_OnInitDialog(HWND hwnd)
{
UINT ui;
TCHAR tszDrive[3];
tszDrive[1] = TEXT(':');
tszDrive[2] = TEXT('\0');
pdmi->dwNoDrives = GetRegDword(g_hkCUSMWCV, c_tszRestrictions,
c_tszNoDrives, 0);
pdmi->dwValidDrives = GetLogicalDrives();
for (ui = 0; ui < 26; ui++) {
int iIcon = MyComp_LV_GetIcon(ui);
tszDrive[0] = ui + TEXT('A');
LV_AddItem(hwnd, ui, tszDrive, iIcon, !(pdmi->dwNoDrives & (1 << ui)));
}
/*
* And initialize the special folders stuff.
*/
HWND hdlg = GetParent(hwnd);
pdmi->_sf.Init(GetDlgItem(hdlg, IDC_FLDNAMELIST),
GetDlgItem(hdlg, IDC_FLDLOC));
return 1;
}
/*****************************************************************************
*
* MyComp_OnDestroy
*
* Free the memory we allocated.
*
*****************************************************************************/
void PASCAL
MyComp_OnDestroy(HWND hdlg)
{
/*
* Destroy the special folders stuff.
*/
pdmi->_sf.Destroy();
}
#if 0
/*****************************************************************************
*
* MyComp_FactoryReset
*
* This is scary and un-undoable, so let's do extra confirmation.
*
*****************************************************************************/
void PASCAL
MyComp_FactoryReset(HWND hdlg)
{
if (MessageBoxId(hdlg, IDS_MyCompRESETOK,
tszName, MB_YESNO + MB_DEFBUTTON2) == IDYES) {
pcdii->fRunShellInf = 1;
Common_NeedLogoff(hdlg);
PropSheet_Apply(GetParent(hdlg));
}
}
#endif
/*****************************************************************************
*
* MyComp_OnApply
*
* Write the changes to the registry.
*
*****************************************************************************/
void PASCAL
MyComp_OnApply(HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_ICONLV);
DWORD dwDrives = 0;
LV_ITEM lvi;
for (lvi.iItem = 0; lvi.iItem < 26; lvi.iItem++) {
lvi.stateMask = LVIS_STATEIMAGEMASK;
Misc_LV_GetItemInfo(hwnd, &lvi, lvi.iItem, LVIF_STATE);
if (!LV_IsChecked(&lvi)) {
dwDrives |= 1 << lvi.iItem;
}
}
if (pdmi->dwNoDrives != dwDrives) {
DWORD dwChanged;
UINT ui;
TCHAR tszRoot[4];
SetRegDword(g_hkCUSMWCV, c_tszRestrictions, c_tszNoDrives, dwDrives);
/* Recompute GetLogicalDrives() in case new drives are here */
dwChanged = (pdmi->dwNoDrives ^ dwDrives) & GetLogicalDrives();
pdmi->dwNoDrives = dwDrives;
/*
* SHCNE_UPDATEDIR doesn't work for CSIDL_DRIVES because
* Drivesx.c checks the restrictions only in response to a
* SHCNE_ADDDRIVE. So walk through every drive that changed
* and send a SHCNE_DRIVEADD or SHCNE_DRIVEREMOVED for it.
*/
for (ui = 0; ui < 26; ui++) {
DWORD dwMask = 1 << ui;
if (dwChanged & dwMask) {
MyComp_BuildRoot(tszRoot, ui);
SHChangeNotify((dwDrives & dwMask) ? SHCNE_DRIVEREMOVED
: SHCNE_DRIVEADD,
SHCNF_PATH, tszRoot, 0L);
}
}
}
/*
* And update the special folders, too.
*/
BOOL fNeedLogoff = pdmi->_sf.Apply();
if (fNeedLogoff) {
Common_NeedLogoff(hdlg);
}
}
/*****************************************************************************
*
* MyComp_OnCommand
*
* Ooh, we got a command.
*
*****************************************************************************/
void PASCAL
MyComp_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
switch (id) {
case IDC_FLDNAMELIST:
if (codeNotify == CBN_SELCHANGE) pdmi->_sf.Resync();
break;
case IDC_FLDCHG:
if (codeNotify == BN_CLICKED) pdmi->_sf.ChangeFolder(hdlg);
break;
}
}
/*****************************************************************************
*
* Oh yeah, we need this too.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
LVV lvvMyComp = {
MyComp_OnCommand,
0,
0, /* MyComp_LV_Dirtify */
MyComp_LV_GetIcon,
MyComp_OnInitDialog,
MyComp_OnApply,
MyComp_OnDestroy,
0,
4, /* iMenu */
rgdwHelp,
0, /* Double-click action */
lvvflIcons | /* We need icons */
lvvflCanCheck, /* And check boxes */
NULL,
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* Our window procedure.
*
*****************************************************************************/
INT_PTR EXPORT
MyComp_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
return LV_DlgProc(&lvvMyComp, hdlg, wm, wParam, lParam);
}