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

737 lines
21 KiB
C++

/*
* desktop - Dialog box property sheet for "desktop customization"
*/
#include "tweakui.h"
#pragma BEGIN_CONST_DATA
#define c_tszCLSIDMyDocs TEXT("CLSID\\{450D8FBA-AD25-11D0-98A8-0800361B1103}")
#define c_tszParseMyDocs TEXT("::{450D8FBA-AD25-11D0-98A8-0800361B1103}")
#define c_tszParseMyComp TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}")
KL const c_klMyDocsOrder = { &c_hkCR, c_tszCLSIDMyDocs, TEXT("SortOrderIndex") };
#define ORDER_BEFOREMYCOMP 0x48
#define ORDER_AFTERMYCOMP 0x54
const static DWORD CODESEG rgdwHelp[] = {
IDC_ICONLVTEXT, IDH_GROUP,
IDC_ICONLVTEXT2, IDH_ICONLV,
IDC_ICONLV, IDH_ICONLV,
IDC_CREATENOWTEXT, IDH_GROUP,
IDC_CREATENOW, IDH_CREATENOW,
IDC_ENUMFIRSTTEXT, IDH_DESKFIRSTICON,
IDC_ENUMFIRST, IDH_DESKFIRSTICON,
#if 0
IDC_RESET, IDH_RESET,
#endif
0, 0,
};
#pragma END_CONST_DATA
/*
* cchFriendlyMax should be at least MAX_PATH, because we also use it
* to hold icon file names.
*/
#define cchFriendlyMax 256
/*
* Evil hack! Since the ::{guid} hack doesn't work unless the object
* really exists in the name space, we pull an evil trick and just
* create the idlist that the shell would've made if you had asked for
* it...
*/
typedef struct RIDL {
USHORT cb; /* Must be 20 */
BYTE bFlags; /* Must be 0x1F */
BYTE bOrder; /* Sorting order */
CLSID clsid; /* Guid goes here */
USHORT zero; /* Must be zero */
} RIDL, *PRIDL;
#define pidlPnsi(pnsi) ((PIDL)&(pnsi)->ridl)
/*
* The only abnormal key is Network Neighborhood, since its presence
* is controlled by a system policy...
*
* You need to set nsiflNever if something should not have a check
* box next to it because it cannot be added to the namespace.
*
* You need to set nsiflDir for things that aren't valid namespace
* items but should be created as directory-like objects.
*
* Briefcase is doubly abnormal, because it doesn't do anything at all!
* So we just exclude him from the enumeration.
*/
typedef BYTE NSIFL; /* Random flags */
#define nsiflNormal 1 /* Is a regular thing */
#define nsiflDir 2 /* Is a directory-like object */
#define nsiflNever 4 /* Not a valid namespace item */
#define nsiflEdited 8 /* The name has been edited */
typedef struct NSI { /* namespace item */
NSIFL nsifl; /* Is this a normal regkey? */
TCH tszClsid[ctchClsid]; /* Class id */
RIDL ridl; /* Regitem idlist */
} NSI, *PNSI;
#define insiPlvi(plvi) ((UINT)(plvi)->lParam)
#define pnsiInsi(insi) (&pddii->pnsi[insi])
#define pnsiPlvi(plvi) pnsiInsi(insiPlvi(plvi))
typedef struct DDII {
Declare_Gxa(NSI, nsi);
HKEY hkNS;
int iFirstIcon;
} DDII, *PDDII;
DDII ddii;
#define pddii (&ddii)
/*****************************************************************************
*
* Desktop_GetClsidAttributes
*
* Return the Attributes registry key for a class id.
*
*****************************************************************************/
#define ctchPathShellFolder 21
DWORD PASCAL
Desktop_GetClsidAttributes(PCTSTR ptszClsid)
{
TCH tsz[ctchPathShellFolder + ctchClsid];
wsprintf(tsz, c_tszPathShellFolder, ptszClsid);
return GetRegDword(hhkCR, tsz, c_tszAttributes, 0);
}
/*****************************************************************************
*
* Desktop_HasSubkey
*
* Return whether the key has a child key with the specified name.
*
*****************************************************************************/
BOOL PASCAL
Desktop_HasSubkey(HKEY hk, LPCTSTR ptszChild)
{
HKEY hkS;
if (_RegOpenKey(hk, ptszChild, &hkS) == 0) {
RegCloseKey(hkS);
return 1;
} else {
return 0;
}
}
/*****************************************************************************
*
* Desktop_IsNSKey
*
* Determine whether the specified class is in the desktop namespace
* right now.
*
*****************************************************************************/
#define Desktop_IsNSKey(ptszClsid) Desktop_HasSubkey(pddii->hkNS, ptszClsid)
/*****************************************************************************
*
* Desktop_GetNetHood
*
* Determine whether the network neighborhood is visible now.
*
*****************************************************************************/
#define Desktop_GetNetHood() GetRestriction(c_tszNoNetHood)
/*****************************************************************************
*
* Desktop_SetNetHood
*
* Set the new network neighborhood visibility.
*
*****************************************************************************/
#define Desktop_SetNetHood(f) SetRestriction(c_tszNoNetHood, f)
/*****************************************************************************
*
* Desktop_IsHereNow
*
* Determine the state of the object as it is in the world today.
*
*****************************************************************************/
#define Desktop_IsHereNow(pnsi, ptszClsid) \
((pnsi->nsifl & nsiflNormal) ? Desktop_IsNSKey(ptszClsid) \
: Desktop_GetNetHood())
/*****************************************************************************
*
* Desktop_AddNSKey
*
* Okay, we've committed ourselves to adding the key to the listview.
* No turning back now!
*
* We default to the shared desktop, so set the initial state accordingly.
*
*****************************************************************************/
void PASCAL
Desktop_AddNSKey(HWND hwnd, PNSI pnsi, LPCTSTR ptszClsid,
LPCTSTR ptszFriendly, int iImage, NSIFL nsifl)
{
pnsi->nsifl = nsifl;
lstrcpy(pnsi->tszClsid, ptszClsid);
LV_AddItem(hwnd, pddii->cnsi++, ptszFriendly, iImage,
(nsifl & nsiflNever) ? -1 : Desktop_IsHereNow(pnsi, ptszClsid));
}
/*****************************************************************************
*
* Desktop_MakeRidl
*
* Initialize a RIDL from a GUID display name.
*
*****************************************************************************/
HRESULT PASCAL
Desktop_MakeRidl(PNSI pnsi, LPCTSTR ptszClsid)
{
pnsi->ridl.cb = 20; /* Always */
pnsi->ridl.bFlags = 0x1F; /* Always */
pnsi->ridl.bOrder = 0; /* Unsorted */
pnsi->ridl.zero = 0; /* Always */
return Ole_ClsidFromString(ptszClsid, &pnsi->ridl.clsid);
}
/*****************************************************************************
*
* Desktop_ShouldUseNSClsid
*
* Check if this is a CLSID we should bother showing.
*
*****************************************************************************/
const GUID c_rgguidExclude[] = {
/*
* These must be rooted on a filesystem object.
*/
{ 0x85BBD920, 0x42A0, 0x1069, { 0xA2, 0xE4, 0x08, 0x00, 0x2B, 0x30, 0x30, 0x9D} }, /* Briefcase */
{ 0x1A9BA3A0, 0x143A, 0x11CF, { 0x83, 0x50, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }, /* Shell Favorites */
{ 0xAFDB1F70, 0x2A4C, 0x11d2, { 0x90, 0x39, 0x00, 0xC0, 0x4F, 0x8E, 0xEB, 0x3E} }, /* Offline Files Folder */
{ 0x0CD7A5C0, 0x9F37, 0x11CE, { 0xAE, 0x65, 0x08, 0x00, 0x2B, 0x2E, 0x12, 0x62} }, /* Cabinet File */
{ 0x88C6C381, 0x2E85, 0x11d0, { 0x94, 0xDE, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }, /* ActiveX Cache Folder */
{ 0xE88DCCE0, 0xB7B3, 0x11d1, { 0xA9, 0xF0, 0x00, 0xAA, 0x00, 0x60, 0xFA, 0x31} }, /* Compressed Folder (Zip) */
/*
* These simply weren't meant to be seen.
*/
{ 0x1f4de370, 0xd627, 0x11d1, { 0xba, 0x4f, 0x00, 0xa0, 0xc9, 0x1e, 0xed, 0xba} }, /* Search Results - Computers */
{ 0x63da6ec0, 0x2e98, 0x11cf, { 0x8d, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }, /* Microsoft FTP Folder */
/*
* These have other ways of being hidden.
*/
{ 0x450D8FBA, 0xAD25, 0x11D0, { 0x98, 0xA8, 0x08, 0x00, 0x36, 0x1B, 0x11, 0x03} }, /* My Documents */
};
/*
* The Win98 "Networking and Dial-Up Connections" CLSID is excluded on NT5.
* (They leave it around for back compat reasons but it shouldn't be
* exposed to the user.)
*/
const GUID c_clsidDUN98 =
{ 0x992CFFA0, 0xF557, 0x101A, { 0x88, 0xEC, 0x00, 0xDD, 0x01, 0x0C, 0xCC, 0x48} };
const GUID c_clsidIE =
{ 0x871C5380, 0x42A0, 0x1069, { 0xA2, 0xEA, 0x08, 0x00, 0x2B, 0x30, 0x30, 0x9D} };
BOOL PASCAL
Desktop_ShouldUseNSClsid(PNSI pnsi)
{
int i;
for (i = 0; i < cA(c_rgguidExclude); i++) {
if (IsEqualGUID(pnsi->ridl.clsid, c_rgguidExclude[i])) {
return FALSE;
}
}
if (g_fNT5 && IsEqualGUID(pnsi->ridl.clsid, c_clsidDUN98)) {
return FALSE;
}
/*
* If IE5 is installed, then it already has the "Show IE on desktop"
* setting in its Advanced dialog. Don't need one here.
*/
if (IsEqualGUID(pnsi->ridl.clsid, c_clsidIE) &&
(g_fShell5 || RegKeyExists(g_hkLMSMIE, TEXT("AdvancedOptions\\BROWSEE\\IEONDESKTOP")))) {
return FALSE;
}
return TRUE;
}
/*****************************************************************************
*
* Desktop_CheckNSKey
*
* Check and possibly add a new namespace key. My Computer is
* excluded because you can't get rid of it. Network Neighborhood
* is here, although it is somewhat weird. We handle the weirdness
* as it arises...
*
* All that has been validated so far is that the key exists, it
* has a ShellEx subkey, and it has nonzero attributes.
*
* We haven't yet validated that it has an icon. We'll notice that
* when we try to build up the listview info.
*
* And people complain that lisp has too many levels of nesting...
*
*****************************************************************************/
void PASCAL
Desktop_CheckNSKey(HWND hwnd, HKEY hk, LPCTSTR ptszClsid, NSIFL nsifl)
{
PNSI pnsi = (PNSI)Misc_AllocPx(&pddii->gxa);
if (pnsi) {
if (SUCCEEDED(Desktop_MakeRidl(pnsi, ptszClsid)) &&
Desktop_ShouldUseNSClsid(pnsi)) {
SHFILEINFO sfi;
if (SHGetFileInfo((LPCSTR)pidlPnsi(pnsi), 0, &sfi, cbX(sfi),
SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX |
SHGFI_SMALLICON)) {
/*
* gacky Net Hood hack. Shell won't give me a name
* if I don't have one in the registry.
*/
if (sfi.szDisplayName[0] == 0 && !(nsifl & nsiflNormal)) {
LoadString(hinstCur, IDS_NETHOOD,
sfi.szDisplayName, cA(sfi.szDisplayName));
}
/*
* It must have a name and must have a custom icon.
*/
if (sfi.szDisplayName[0] && sfi.iIcon != 3) {
Desktop_AddNSKey(hwnd, pnsi, ptszClsid, sfi.szDisplayName,
sfi.iIcon, nsifl);
}
} /* couldn't get file info */
} /* not a valid guid */
} /* else no memory to store this entry */
}
/*****************************************************************************
*
* Desktop_AddSpecialNSKey
*
* Add some special namespace keys, which eluded our enumeration.
*
*****************************************************************************/
void PASCAL
Desktop_AddSpecialNSKey(HWND hwnd, LPCTSTR ptszClsid, NSIFL nsifl)
{
int insi;
HKEY hk;
for (insi = 0; insi < pddii->cnsi; insi++) {
if (lstrcmpi(pddii->pnsi[insi].tszClsid, ptszClsid) == 0) {
goto found;
}
}
hk = hkOpenClsid(ptszClsid);
if (hk) {
Desktop_CheckNSKey(hwnd, hk, ptszClsid, nsifl);
RegCloseKey(hk);
}
found:;
}
/*****************************************************************************
*
* Desktop_EnumClasses
*
* Locate all the classes that are possible namespace keys.
*
*****************************************************************************/
void PASCAL
Desktop_EnumClasses(HWND hwnd)
{
int ihk;
TCH tsz[ctchClsid];
for (ihk = 0; RegEnumKey(pcdii->hkClsid, ihk, tsz, cA(tsz)) == 0; ihk++) {
HKEY hk = hkOpenClsid(tsz);
if (hk) {
if (Desktop_GetClsidAttributes(tsz)) {
Desktop_CheckNSKey(hwnd, hk, tsz, nsiflNormal);
}
RegCloseKey(hk);
}
}
Desktop_AddSpecialNSKey(hwnd, c_tszClsidNetHood, nsiflDir);
Desktop_AddSpecialNSKey(hwnd, c_tszClsidCpl,
nsiflNormal | nsiflNever | nsiflDir);
Desktop_AddSpecialNSKey(hwnd, c_tszClsidPrint,
nsiflNormal | nsiflNever | nsiflDir);
Misc_LV_SetCurSel(hwnd, 0); /* Default to top of list */
}
/*****************************************************************************
*
* Desktop_OnInitDialog
*
* We have much nontrivial work to do. Fill all the list boxes with
* defaults.
*
*****************************************************************************/
BOOL PASCAL
Desktop_OnInitDialog(HWND hwnd)
{
ZeroMemory(pddii, cbX(*pddii));
CWaitCursor wc;
if (Misc_InitPgxa(&pddii->gxa, cbX(NSI))) {
if (RegCreateKey(pcdii->hkLMExplorer, c_tszDesktopNameSpace,
&pddii->hkNS) == 0) {
Desktop_EnumClasses(hwnd);
}
}
/*
* Set the order for My Documents if it exists.
*/
HWND hdlg = GetParent(hwnd);
PRIDL pridl = (PRIDL)pidlFromPath(psfDesktop, c_tszParseMyDocs);
if (pridl && (pridl->bOrder == ORDER_BEFOREMYCOMP ||
pridl->bOrder == ORDER_AFTERMYCOMP))
{
SHFILEINFO sfi;
HWND hwndCombo = GetDlgItem(hdlg, IDC_ENUMFIRST);
/* Item 0 = My Documents */
SHGetFileInfo((LPCSTR)pridl, 0, &sfi, cbX(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME);
ComboBox_AddString(hwndCombo, sfi.szDisplayName);
/* Item 1 = My Computer */
SHGetFileInfo(c_tszParseMyComp, 0, &sfi, cbX(sfi), SHGFI_DISPLAYNAME);
ComboBox_AddString(hwndCombo, sfi.szDisplayName);
pddii->iFirstIcon = pridl->bOrder == ORDER_AFTERMYCOMP;
ComboBox_SetCurSel(hwndCombo, pddii->iFirstIcon);
} else {
DestroyDlgItems(hdlg, IDC_ENUMFIRSTTEXT, IDC_ENUMFIRST);
}
return 1;
}
/*****************************************************************************
*
* Desktop_OnDestroy
*
* Free the memory we allocated.
*
* We also destroy the imagelist, because listview gets confused if
* it gets two image lists which are the same.
*
*****************************************************************************/
void PASCAL
Desktop_OnDestroy(HWND hdlg)
{
Misc_FreePgxa(&pddii->gxa);
if (pddii->hkNS) {
RegCloseKey(pddii->hkNS);
}
}
#if 0
/*****************************************************************************
*
* Desktop_FactoryReset
*
* This is scary and un-undoable, so let's do extra confirmation.
*
*****************************************************************************/
void PASCAL
Desktop_FactoryReset(HWND hdlg)
{
if (MessageBoxId(hdlg, IDS_DESKTOPRESETOK,
tszName, MB_YESNO + MB_DEFBUTTON2) == IDYES) {
pcdii->fRunShellInf = 1;
Common_NeedLogoff(hdlg);
PropSheet_Apply(GetParent(hdlg));
}
}
#endif
/*****************************************************************************
*
* Desktop_OnCreateNow
*
* Somebody asked to create another one...
*
* Use common dialogs to do the work.
*
*****************************************************************************/
void PASCAL
Desktop_OnCreateNow(HWND hwnd, int iItem)
{
LV_ITEM lvi;
COFN cofn;
lvi.pszText = cofn.tsz;
lvi.cchTextMax = cA(cofn.tsz);
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM | LVIF_TEXT);
InitOpenFileName(GetParent(hwnd), &cofn, IDS_ALLFILES, cofn.tsz);
cofn.ofn.nMaxFile -= ctchClsid + 1; /* Leave room for dot and clsid */
cofn.ofn.Flags |= OFN_NOREADONLYRETURN;
if (GetSaveFileName(&cofn.ofn)) {
PNSI pnsi;
lstrcat(cofn.tsz, c_tszDot);
pnsi = pnsiPlvi(&lvi);
lstrcat(cofn.tsz, pnsi->tszClsid);
if ((pnsi->nsifl & nsiflDir) ||
Desktop_GetClsidAttributes(pnsi->tszClsid) & SFGAO_FOLDER) {
CreateDirectory(cofn.tsz, 0);
} else {
fCreateNil(cofn.tsz);
}
}
}
/*****************************************************************************
*
* Desktop_OnCommand
*
*****************************************************************************/
void PASCAL
Desktop_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
switch (id) {
#if 0
case IDC_RESET:
if (codeNotify == BN_CLICKED) {
Desktop_FactoryReset(hdlg);
}
break;
#endif
case IDC_ENUMFIRST:
if (codeNotify == CBN_SELCHANGE) {
Common_SetDirty(hdlg);
}
break;
}
}
/*****************************************************************************
*
* Desktop_LV_Dirtify
*
* Mark this item as having been renamed during the property sheet
* page's lifetime.
*
*****************************************************************************/
void PASCAL
Desktop_LV_Dirtify(LPARAM insi)
{
pddii->pnsi[insi].nsifl |= nsiflEdited;
}
/*****************************************************************************
*
* Desktop_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.
*
*****************************************************************************/
int PASCAL
Desktop_LV_GetIcon(LPARAM insi)
{
SHFILEINFO sfi;
sfi.iIcon = 0;
SHGetFileInfo((LPCSTR)pidlPnsi(pnsiInsi(insi)), 0, &sfi, cbX(sfi),
SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
return sfi.iIcon;
}
/*****************************************************************************
*
* Desktop_OnSelChange
*
* Disable the "Create as File" button if we are on Net Hood.
*
*****************************************************************************/
void PASCAL
Desktop_OnSelChange(HWND hwnd, int iItem)
{
PNSI pnsi = pnsiInsi(Misc_LV_GetParam(hwnd, iItem));
EnableWindow(GetDlgItem(GetParent(hwnd), IDC_CREATENOW),
pnsi->nsifl & nsiflNormal);
}
/*****************************************************************************
*
* Desktop_OnApply
*
* Write the changes to the registry.
*
*****************************************************************************/
void PASCAL
Desktop_OnApply(HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_ICONLV);
int cItems = ListView_GetItemCount(hwnd);
BOOL fChanged = 0;
LV_ITEM lvi;
TCH tsz[cchFriendlyMax];
lvi.pszText = tsz;
lvi.cchTextMax = cA(tsz);
for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++) {
PNSI pnsi;
lvi.stateMask = LVIS_STATEIMAGEMASK;
Misc_LV_GetItemInfo(hwnd, &lvi, lvi.iItem,
LVIF_PARAM | LVIF_TEXT | LVIF_STATE);
pnsi = pnsiPlvi(&lvi);
if (Desktop_IsHereNow(pnsi, pnsi->tszClsid) != LV_IsChecked(&lvi)) {
fChanged = 1;
if (pnsi->nsifl & nsiflNormal) {
if (LV_IsChecked(&lvi)) {
HKEY hk;
if (RegCreateKey(pddii->hkNS, pnsi->tszClsid, &hk) == 0) {
RegSetValuePtsz(hk, 0, lvi.pszText);
RegCloseKey(hk);
}
} else {
RegDeleteTree(pddii->hkNS, pnsi->tszClsid);
}
} else { /* Ah, the Net Hood... */
Desktop_SetNetHood(LV_IsChecked(&lvi));
Common_NeedLogoff(hdlg);
if (!LV_IsChecked(&lvi)) {
if (MessageBoxId(hdlg, IDS_NONETHOOD, g_tszName, MB_YESNO)
== IDYES) {
WinHelp(hdlg, c_tszMyHelp, HELP_CONTEXT, IDH_NONETHOOD);
}
}
}
}
/* Not worth cacheing this */
if (pnsi->nsifl & nsiflEdited) {
SetNameOfPidl(psfDesktop, pidlPnsi(pnsi), lvi.pszText);
SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD,
IntToPtr(lvi.iImage), 0L);
}
}
HWND hwndCombo = GetDlgItem(hdlg, IDC_ENUMFIRST);
int iFirstIcon = ComboBox_GetCurSel(hwndCombo);
if (iFirstIcon != pddii->iFirstIcon) {
SetDwordPkl2(&c_klMyDocsOrder, iFirstIcon ? ORDER_AFTERMYCOMP : ORDER_BEFOREMYCOMP);
pddii->iFirstIcon = iFirstIcon;
MessageBoxId(hdlg, IDS_REORDERDESKTOP, g_tszName, MB_OK);
fChanged = TRUE;
}
if (fChanged) {
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_DWORD, 0L, 0L);
}
}
/*****************************************************************************
*
* Desktop_LV_OnInitContextMenu
*
* Propagate the status of the Create Now button.
*
*****************************************************************************/
void PASCAL
Desktop_LV_OnInitContextMenu(HWND hwnd, int iItem, HMENU hmenu)
{
Misc_EnableMenuFromHdlgId(hmenu, GetParent(hwnd), IDC_CREATENOW);
}
/*****************************************************************************
*
* Oh yeah, we need this too.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
LVCI lvciDesktop[] = {
{ IDC_CREATENOW, Desktop_OnCreateNow },
{ 0, 0 },
};
LVV lvvDesktop = {
Desktop_OnCommand,
Desktop_LV_OnInitContextMenu,
Desktop_LV_Dirtify,
Desktop_LV_GetIcon,
Desktop_OnInitDialog,
Desktop_OnApply,
Desktop_OnDestroy,
Desktop_OnSelChange,
1, /* iMenu */
rgdwHelp,
0, /* Double-click action */
lvvflIcons | /* We need icons */
lvvflCanCheck | /* And check boxes */
lvvflCanRename, /* and you can rename by clicking */
lvciDesktop,
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* Our window procedure.
*
*****************************************************************************/
INT_PTR EXPORT
Desktop_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
return LV_DlgProc(&lvvDesktop, hdlg, wm, wParam, lParam);
}