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