461 lines
12 KiB
C
461 lines
12 KiB
C
|
|
|
|
|
|
#include "smtidy.h"
|
|
#include "smwiz.h"
|
|
#include "resource.h"
|
|
#include "util.h"
|
|
#include "killactv.h"
|
|
#include "finish.h"
|
|
|
|
|
|
#define POINTS_PER_INCH 72
|
|
|
|
|
|
DWORD GetOriginalTargetAttribs(IShellLink *psl)
|
|
{
|
|
DWORD dwRet = 0;
|
|
WIN32_FIND_DATA fd;
|
|
|
|
if (SUCCEEDED(psl->lpVtbl->GetPath(psl, NULL, 0, &fd, 0)))
|
|
dwRet = fd.dwFileAttributes;
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
// Given a pidl for a link, extract the appropriate info and append it to
|
|
// the app list.
|
|
void SMIList_AppendItem(PSMTIDYINFO psmti, PIDL pidlFolder, PIDL pidlItem, DWORD dwFlags)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
WCHAR wszPath[MAX_PATH];
|
|
SMITEM smi;
|
|
PIDL pidl;
|
|
|
|
Assert(psmti->hdsaSMI);
|
|
Assert(pidlFolder);
|
|
Assert(pidlItem);
|
|
Assert(psmti->psl);
|
|
Assert(psmti->ppf);
|
|
|
|
|
|
pidl = ILCombine(pidlFolder, pidlItem);
|
|
|
|
memset(&smi, 0, SIZEOF(smi));
|
|
smi.dwFlags = dwFlags;
|
|
smi.pidlItem = pidl;
|
|
|
|
SHGetPathFromIDList(pidl, szPath);
|
|
STRTOOLESTR(wszPath, szPath);
|
|
if (SUCCEEDED(psmti->ppf->lpVtbl->Load(psmti->ppf, wszPath, 0)))
|
|
{
|
|
PIDL pidlTarget;
|
|
if (SUCCEEDED(psmti->psl->lpVtbl->GetIDList(psmti->psl, &pidlTarget)))
|
|
{
|
|
SHGetPathFromIDList(pidlTarget, szPath);
|
|
if (*szPath)
|
|
{
|
|
Sz_AllocCopy(szPath, &smi.pszTarget);
|
|
smi.dwMatch = GetOriginalTargetAttribs(psmti->psl) & FILE_ATTRIBUTE_DIRECTORY; // Needed for link tracking.
|
|
}
|
|
else
|
|
{
|
|
smi.dwFlags |= SMIF_TARGET_NOT_FILE;
|
|
}
|
|
}
|
|
}
|
|
DSA_AppendItem(psmti->hdsaSMI, &smi);
|
|
}
|
|
|
|
|
|
typedef BOOL (*PFNENUMFOLDERCALLBACK)(LPSHELLFOLDER psf, HWND hwndOwner, PIDL pidlFolder, PIDL pidlItem, LPVOID pv);
|
|
|
|
|
|
BOOL EnumFolder(HWND hwndOwner, PIDL pidlFolder, DWORD grfFlags, PFNENUMFOLDERCALLBACK pfn, LPVOID pv)
|
|
{
|
|
LPSHELLFOLDER psf;
|
|
LPSHELLFOLDER psfDesktop;
|
|
BOOL fRet = FALSE;
|
|
|
|
if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellDesktop, &IID_IShellFolder, &psfDesktop)))
|
|
{
|
|
if (SUCCEEDED(psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlFolder, NULL, &IID_IShellFolder, &psf)))
|
|
{
|
|
LPENUMIDLIST penum;
|
|
if (SUCCEEDED(psf->lpVtbl->EnumObjects(psf, hwndOwner, grfFlags, &penum)))
|
|
{
|
|
PIDL pidl;
|
|
UINT celt;
|
|
while (penum->lpVtbl->Next(penum, 1, &pidl, &celt)==NOERROR && celt==1)
|
|
{
|
|
if (!(*pfn)(psf, hwndOwner, pidlFolder, pidl, pv))
|
|
{
|
|
ILFree(pidl);
|
|
break;
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
fRet = TRUE;
|
|
penum->lpVtbl->Release(penum);
|
|
}
|
|
psf->lpVtbl->Release(psf);
|
|
}
|
|
psfDesktop->lpVtbl->Release(psfDesktop);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL EnumFolder_Callback(LPSHELLFOLDER psf, HWND hwndOwner, PIDL pidlFolder,
|
|
PIDL pidlItem, LPVOID pv)
|
|
{
|
|
DWORD dwAttribs = SFGAO_LINK|SFGAO_FOLDER;
|
|
PSMTIDYINFO psmti = pv;
|
|
BOOL fRet = FALSE;
|
|
|
|
Assert(psmti);
|
|
|
|
if (SUCCEEDED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidlItem, &dwAttribs)))
|
|
{
|
|
// Is it a folder?
|
|
if (dwAttribs & SFGAO_FOLDER)
|
|
{
|
|
PIDL pidlRecurse = ILCombine(pidlFolder, pidlItem);
|
|
SMIList_AppendItem(psmti, pidlFolder, pidlItem, SMIF_FOLDER);
|
|
// Recurse.
|
|
if (pidlRecurse)
|
|
{
|
|
EnumFolder(hwndOwner, pidlRecurse, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Callback, psmti);
|
|
ILFree(pidlRecurse);
|
|
}
|
|
}
|
|
else if (dwAttribs & SFGAO_LINK)
|
|
{
|
|
// Regular link, add it to the list.
|
|
SMIList_AppendItem(psmti, pidlFolder, /*psf,*/ pidlItem, SMIF_NONE);
|
|
}
|
|
fRet = TRUE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL SMIList_Build(PSMTIDYINFO psmti)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
PIDL pidlStartmenu;
|
|
PIDL pidlPrograms;
|
|
|
|
Assert(psmti);
|
|
Assert(psmti->hdsaSMI);
|
|
|
|
// Build a DPA of pidls for each item.
|
|
Dbg(TEXT("smw.smt_ea: Enumerating everything..."));
|
|
|
|
pidlStartmenu = SHCloneSpecialIDList(NULL, CSIDL_STARTMENU, TRUE);
|
|
if (pidlStartmenu)
|
|
{
|
|
pidlPrograms = SHCloneSpecialIDList(NULL, CSIDL_PROGRAMS, TRUE);
|
|
if (pidlPrograms)
|
|
{
|
|
if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psmti->psl)))
|
|
{
|
|
if (SUCCEEDED(psmti->psl->lpVtbl->QueryInterface(psmti->psl, &IID_IPersistFile, &psmti->ppf)))
|
|
{
|
|
// Enum Startmenu.
|
|
EnumFolder(NULL, pidlStartmenu, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
|
|
EnumFolder_Callback, psmti);
|
|
// Is programs under the StartMenu?
|
|
if (!ILIsParent(pidlStartmenu, pidlPrograms, FALSE))
|
|
{
|
|
// Nope, enum it seperately.
|
|
EnumFolder(NULL, pidlPrograms, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
|
|
EnumFolder_Callback, psmti);
|
|
}
|
|
psmti->ppf->lpVtbl->Release(psmti->ppf);
|
|
psmti->ppf = NULL;
|
|
fRet = TRUE;
|
|
}
|
|
psmti->psl->lpVtbl->Release(psmti->psl);
|
|
psmti->psl = NULL;
|
|
}
|
|
ILFree(pidlPrograms);
|
|
}
|
|
ILFree(pidlStartmenu);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// REVIEW UNDONE IANEL - Allow these defaults to be overriden from the
|
|
// registry (preserve them their too).
|
|
void SetDefaultOptions(HWND hDlg, LPPROPSHEETPAGE pps)
|
|
{
|
|
UINT idPage = PtrToUlong(pps->pszTemplate);
|
|
|
|
switch (idPage)
|
|
{
|
|
case DLG_SMINTRO:
|
|
CheckDlgButton(hDlg, IDC_REMOVE_BROKEN_SHORTCUTS, BST_CHECKED);
|
|
CheckDlgButton(hDlg, IDC_REMOVE_EMPTY_FOLDERS, BST_CHECKED);
|
|
CheckDlgButton(hDlg, IDC_MOVE_SINGLE_ITEMS, BST_CHECKED);
|
|
break;
|
|
case DLG_GROUP_READMES:
|
|
CheckRadioButton(hDlg, IDC_REMOVE_READMES, IDC_LEAVE_READMES_ALONE, IDC_GROUP_READMES);
|
|
break;
|
|
case DLG_UNUSED_SHORTCUTS:
|
|
CheckRadioButton(hDlg, IDC_REMOVE_UNUSED_SHORTCUTS, IDC_LEAVE_UNUSED_SHORTCUTS_ALONE, IDC_GROUP_UNUSED_SHORTCUTS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void OnSetActive(HWND hDlg, LPPROPSHEETPAGE pps)
|
|
{
|
|
PSMTIDYINFO psmti = (PSMTIDYINFO) pps->lParam;
|
|
UINT idPage = PtrToUlong(pps->pszTemplate);
|
|
LRESULT lRes = 0;
|
|
|
|
Assert(psmti);
|
|
Assert(idPage);
|
|
|
|
if (psmti->hbmp)
|
|
SendMessage(GetDlgItem(hDlg, IDC_WIZBMP), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)psmti->hbmp);
|
|
|
|
// Only show pages that actually apply.
|
|
switch (idPage)
|
|
{
|
|
case DLG_SMINTRO:
|
|
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT);
|
|
break;
|
|
case DLG_SMFINISH:
|
|
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_FINISH|PSWIZB_BACK);
|
|
break;
|
|
case DLG_GROUP_READMES:
|
|
case DLG_UNUSED_SHORTCUTS:
|
|
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT|PSWIZB_BACK);
|
|
break;
|
|
}
|
|
SetDlgMsgResult(hDlg, WM_NOTIFY, lRes);
|
|
}
|
|
|
|
|
|
void OnKillActive(HWND hDlg, LPPROPSHEETPAGE pps)
|
|
{
|
|
PSMTIDYINFO psmti = (PSMTIDYINFO) pps->lParam;
|
|
UINT idPage = PtrToUlong(pps->pszTemplate);
|
|
LRESULT lRes = 0;
|
|
|
|
Assert(psmti);
|
|
Assert(idPage);
|
|
|
|
switch (idPage)
|
|
{
|
|
case DLG_SMINTRO:
|
|
KillActiveLostTargets(psmti, hDlg);
|
|
KillActiveEmptyFolders(psmti, hDlg);
|
|
KillActiveSingleItemFolder(psmti, hDlg);
|
|
break;
|
|
case DLG_GROUP_READMES:
|
|
KillActiveGroupReadMes(psmti, hDlg);
|
|
break;
|
|
case DLG_UNUSED_SHORTCUTS:
|
|
KillActiveUnusedShortcuts(psmti, hDlg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL OnWizNotify(HWND hDlg, LPPROPSHEETPAGE pps, NMHDR *pnm)
|
|
{
|
|
switch (pnm->code)
|
|
{
|
|
case PSN_SETACTIVE:
|
|
OnSetActive(hDlg, pps);
|
|
break;
|
|
|
|
case PSN_KILLACTIVE:
|
|
OnKillActive(hDlg, pps);
|
|
break;
|
|
|
|
case PSN_WIZFINISH:
|
|
OnKillActive(hDlg, pps); // Should apply the changes of current page
|
|
OnFinish(hDlg, pps);
|
|
// fall through
|
|
case PSN_WIZNEXT:
|
|
case PSN_WIZBACK:
|
|
case PSN_RESET:
|
|
SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
|
|
return FALSE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
typedef BOOL (*PFCFGSTART)(HWND, BOOL);
|
|
|
|
|
|
void CallAppWiz(HWND hDlg, BOOL bDelItems)
|
|
{
|
|
HANDLE hmodWiz = LoadLibrary(TEXT("AppWiz.Cpl"));
|
|
if (hmodWiz)
|
|
{
|
|
PFCFGSTART pfnCfgStart = (PFCFGSTART)GetProcAddress(hmodWiz, "ConfigStartMenu");
|
|
if (pfnCfgStart)
|
|
{
|
|
pfnCfgStart(hDlg, bDelItems);
|
|
}
|
|
FreeLibrary(hmodWiz);
|
|
}
|
|
}
|
|
|
|
|
|
void OnCommand(HWND hDlg, WORD cmd)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case IDHELP:
|
|
break;
|
|
case IDC_ADD_SHORTCUTS:
|
|
CallAppWiz(hDlg, FALSE);
|
|
break;
|
|
case IDC_REMOVE_SHORTCUTS:
|
|
CallAppWiz(hDlg, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void SetFonts(PSMTIDYINFO psmti, HWND hDlg)
|
|
{
|
|
HWND hwndTitle = GetDlgItem(hDlg, IDC_TITLE);
|
|
|
|
if (hwndTitle)
|
|
{
|
|
if (!psmti->hfontLarge)
|
|
{
|
|
HFONT hfont = (HFONT)SendMessage(hwndTitle, WM_GETFONT, 0, 0);
|
|
if (hfont)
|
|
{
|
|
LOGFONT lf;
|
|
if (GetObject(hfont, SIZEOF(lf), &lf))
|
|
{
|
|
HDC hdc = GetDC(NULL);
|
|
lf.lfHeight = -MulDiv(14, GetDeviceCaps(hdc, LOGPIXELSY), POINTS_PER_INCH );
|
|
// lf.lfWeight = FW_BOLD;
|
|
ReleaseDC(NULL, hdc);
|
|
psmti->hfontLarge = CreateFontIndirect(&lf);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (psmti->hfontLarge)
|
|
SendMessage(hwndTitle, WM_SETFONT, (WPARAM)psmti->hfontLarge, 0);
|
|
}
|
|
}
|
|
|
|
|
|
INT_PTR CALLBACK SMTidyProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPPROPSHEETPAGE pps = (LPPROPSHEETPAGE)(GetWindowLongPtr(hDlg, DWLP_USER));
|
|
|
|
switch(msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LPPROPSHEETPAGE pps = (LPPROPSHEETPAGE)lParam;
|
|
PSMTIDYINFO psmti = (PSMTIDYINFO)(pps->lParam);
|
|
|
|
Assert(pps);
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
SetDefaultOptions(hDlg, pps);
|
|
SetFonts(psmti, hDlg);
|
|
SetWindowPos(GetParent(hDlg), NULL, psmti->rc.left, psmti->rc.top, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR *pnm = (NMHDR*)lParam;
|
|
return OnWizNotify(hDlg, pps, pnm);
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
case WM_HELP:
|
|
case WM_CONTEXTMENU:
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
OnCommand(hDlg, LOWORD(wParam));
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define CMAXPAGES 10
|
|
|
|
|
|
void PropertySheet_AddPage(LPPROPSHEETHEADER ppsh, UINT id, DLGPROC pfn, LPVOID pv)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
|
|
if (ppsh->nPages >= CMAXPAGES)
|
|
{
|
|
Dbg(TEXT("smt.ps_ap: Too many pages."));
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
psp.dwSize = sizeof(psp);
|
|
psp.dwFlags = PSP_DEFAULT;
|
|
psp.hInstance = g_hinstApp;
|
|
psp.pszTemplate = MAKEINTRESOURCE(id);
|
|
psp.pfnDlgProc = pfn;
|
|
psp.lParam = (LPARAM)pv;
|
|
|
|
ppsh->phpage[ppsh->nPages] = CreatePropertySheetPage(&psp);
|
|
|
|
if (ppsh->phpage[ppsh->nPages])
|
|
ppsh->nPages++;
|
|
}
|
|
|
|
|
|
// StartMenu wizard.
|
|
BOOL TidyStartMenuWizard(PSMTIDYINFO psmti)
|
|
{
|
|
HPROPSHEETPAGE rPages[CMAXPAGES];
|
|
PROPSHEETHEADER psh;
|
|
|
|
psh.pszCaption = NULL;
|
|
psh.nPages = 0;
|
|
psh.nStartPage = 0;
|
|
psh.dwSize = sizeof(psh);
|
|
psh.dwFlags = PSH_PROPTITLE | PSH_WIZARD | PSH_WIZARDHASFINISH;
|
|
psh.hwndParent = psmti->hwnd;
|
|
psh.hInstance = g_hinstApp;
|
|
psh.phpage = rPages;
|
|
|
|
PropertySheet_AddPage(&psh, DLG_SMINTRO, SMTidyProc, psmti);
|
|
PropertySheet_AddPage(&psh, DLG_UNUSED_SHORTCUTS, SMTidyProc, psmti);
|
|
PropertySheet_AddPage(&psh, DLG_GROUP_READMES, SMTidyProc, psmti);
|
|
PropertySheet_AddPage(&psh, DLG_SMFINISH, SMTidyProc, psmti);
|
|
|
|
PropertySheet(&psh);
|
|
|
|
return TRUE;
|
|
}
|