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