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