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

845 lines
22 KiB
C++

/*
* template - Dialog box property sheet for "Templates"
*/
#include "tweakui.h"
#define ctchProgMax 80
#pragma BEGIN_CONST_DATA
const static DWORD CODESEG rgdwHelp[] = {
IDC_TEMPLATETEXT, IDH_TEMPLATE,
IDC_TEMPLATE, IDH_TEMPLATE,
IDC_LVDELETE, IDH_TEMPLATEDEL,
0, 0,
};
#pragma END_CONST_DATA
typedef struct TI { /* ti - template info */
BYTE isi; /* Index to state icon */
HKEY hkRoot; /* Root for locating the key */
/* HKCR or HKCR\CLSID */
TCH tszExt[10]; /* Filename extension (for icon refresh) */
TCH tszKey[ctchKeyMax + 6]; /* 6 = strlen("\\CLSID") */
} TI, *PTI;
#define itiPlvi(plvi) ((UINT)(plvi)->lParam)
#define ptiIti(iti) (&ptdii->pti[iti])
#define ptiPlvi(plvi) ptiIti(itiPlvi(plvi))
typedef struct TDII {
Declare_Gxa(TI, ti);
WNDPROC wpTemplate;
BOOL fRundll; /* Need to run Rundll on Apply */
} TDII, *PTDII;
TDII tdii;
#define ptdii (&tdii)
/*****************************************************************************
*
* ptszStrRChr
*
* Get the rightmost occurrence.
*
*****************************************************************************/
PTSTR PASCAL
ptszStrRChr(PCTSTR ptsz, TCH tch)
{
PTSTR ptszRc = 0;
for (ptsz = ptszStrChr(ptsz, tch); ptsz; ptsz = ptszStrChr(ptsz + 1, tch)) {
ptszRc = (PTSTR)ptsz;
}
return ptszRc;
}
/*****************************************************************************
*
* ptszFilenameCqn
*
* Get the filename part of a cqn.
*
*****************************************************************************/
PTSTR PASCAL
ptszFilenameCqn(PCTSTR cqn)
{
PTSTR ptsz = ptszStrRChr(cqn, TEXT('\\'));
return ptsz ? ptsz + 1 : (PTSTR)cqn;
}
/*****************************************************************************
*
* Path_Append
*
* Append a filename to a directory, inserting a backslash
* as necessary.
*
*****************************************************************************/
void
Path_Append(LPTSTR ptszDir, LPCTSTR ptszFile)
{
ptszDir = TweakUi_TrimTrailingBs(ptszDir);
*ptszDir++ = TEXT('\\');
lstrcpy(ptszDir, ptszFile);
}
/*****************************************************************************
*
* Template_NudgeExplorer
*
* Explorer doesn't recognize changes to templates until an application
* terminates, so we simply execute Rundll spuriously. It realizes that
* there is nothing to do and exits. This exit triggers Explorer to
* rebuild the filename extension list.
*
* This nudge doesn't work on NT 5, so we also have to nuke the
* ShellNew cache.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
ConstString(c_tszRundll, "rundll32");
#pragma END_CONST_DATA
void PASCAL
Template_NudgeExplorer(void)
{
WinExec(c_tszRundll, SW_HIDE);
RegDeleteTree(g_hkCUSMWCV, TEXT("Explorer\\Discardable\\PostSetup\\ShellNew"));
}
/*****************************************************************************
*
* Template_SubkeyExists
*
*****************************************************************************/
BOOL PASCAL
Template_SubkeyExists(HKEY hk, PCTSTR ptszSubkey)
{
return GetRegStr(hk, 0, ptszSubkey, 0, 0);
}
/*****************************************************************************
*
* Template_AddTemplateInfo
*
* pti must be the next ti in the array (i.e., Misc_AllocPx)
*
* Returns the icon index, if successful, or -1 on error.
*
*****************************************************************************/
int PASCAL
Template_AddTemplateInfo(HWND hwnd, PCTSTR ptszExt, PTI pti, BOOL fCheck)
{
int iRc;
SHFILEINFO sfi;
if (SHGetFileInfo(ptszExt, 0, &sfi, cbX(sfi),
SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES |
SHGFI_SYSICONINDEX | SHGFI_SMALLICON)) {
pti->isi = fCheck + 1;
iRc = LV_AddItem(hwnd, ptdii->cti++, sfi.szTypeName, sfi.iIcon, fCheck);
} else {
iRc = -1;
}
return iRc;
}
/*****************************************************************************
*
* TemplateCallbackInfo
*
* Information handed to a template callback.
*
* hk - The ptszShellNew subkey itself
*
*****************************************************************************/
typedef BOOL (PASCAL *TCICALLBACK)(struct TCI *ptci, HKEY hk,
PCTSTR ptszShellNew);
typedef struct TCI {
TCICALLBACK pfn;
PTI pti; /* Template info containing result */
HWND hwnd; /* Listview window handle */
PCTSTR ptszExt; /* The .ext being studied */
} TCI, *PTCI;
/*****************************************************************************
*
* Template_CheckShellNew
*
* Look for the ShellNew stuff (or ShellNew- if temporarily disabled).
*
* Returns boolean success/failure.
*
* If ptszExt exists, then we create the key, too.
*
*****************************************************************************/
BOOL PASCAL
Template_CheckShellNew(PTCI ptci, HKEY hkBase, PCTSTR ptszShellNew)
{
HKEY hk;
BOOL fRc;
if (_RegOpenKey(hkBase, ptszShellNew, &hk) == 0) {
if (Template_SubkeyExists(hk, c_tszNullFile) ||
Template_SubkeyExists(hk, c_tszFileName) ||
Template_SubkeyExists(hk, c_tszCommand) ||
Template_SubkeyExists(hk, c_tszData)) {
fRc = ptci->pfn(ptci, hk, ptszShellNew);
} else {
fRc = 0;
}
RegCloseKey(hk);
} else {
fRc = 0;
}
return fRc;
}
/*****************************************************************************
*
* Template_AddHkeyTemplate
*
* We have a candidate location for a ShellNew.
*
* There is some weirdness here. The actual ShellNew can live at
* a sublevel. For example, the ".doc" extension contains several
* ShellNew's:
*
* HKCR\.doc = WordPad.Document.1
* HKCR\.doc\WordDocument\ShellNew
* HKCR\.doc\Word.Document.6\ShellNew
* HKCR\.doc\WordPad.Document.1\ShellNew
*
* Based on the main value (HKCR\.doc), we choose to use the template
* for "Wordpad.Document.1".
*
* pti->hkRoot - hkey at which to start (either HKCR or HKCR\CLSID)
* pti->tszKey - subkey to open (.ext or {guid})
*
* pti->hkRoot\pti->tszKey = actual key to open
*
* hkBase = the key at hkRoot\ptszKey
* hk = the key where ShellNew actually lives
*
*****************************************************************************/
BOOL PASCAL
Template_AddHkeyTemplate(PTCI ptci)
{
HKEY hkBase;
BOOL fRc;
PTI pti = ptci->pti;
if (_RegOpenKey(pti->hkRoot, pti->tszKey, &hkBase) == 0) {
TCH tszProg[ctchProgMax+1];
if (GetRegStr(hkBase, 0, 0, tszProg, cbX(tszProg))) {
HKEY hk = NULL;
if (_RegOpenKey(hkBase, tszProg, &hk) == 0) {
lstrcatnBsA(pti->tszKey, tszProg);
} else {
_RegOpenKey(hkBase, 0, &hk); /* hk = AddRef(hkBase) */
}
if (hk) {
/* Now open the ShellNew subkey or the ShellNew- subkey */
fRc = fLorFF(Template_CheckShellNew(ptci, hk, c_tszShellNew),
Template_CheckShellNew(ptci, hk, c_tszShellNewDash));
RegCloseKey(hk);
} else {
fRc = 0;
}
} else {
fRc = 0;
}
} else {
fRc = 0;
}
return fRc;
}
/*****************************************************************************
*
* Template_LocateExtension
*
* We have a filename extension (ptci->ptszExt).
*
* Get its program id (progid).
*
* We look in two places. If the progid has a registered CLSID
* (HKCR\progid\CLSID), then we look in HKCR\CLSID\{guid}.
*
* If the CLSID fails to locate anything, then we look under
* HKCR\.ext directly.
*
*****************************************************************************/
BOOL PASCAL
Template_LocateExtension(PTCI ptci)
{
BOOL fRc;
PTI pti = ptci->pti = (PTI)Misc_AllocPx(&ptdii->gxa);
if (pti) {
TCH tszProg[ctchProgMax + ctchKeyMax + 6]; /* 6 = strlen(\\CLSID) */
lstrcpyn(pti->tszExt, ptci->ptszExt, cA(pti->tszExt));
if (GetRegStr(hkCR, ptci->ptszExt, 0, tszProg, cbCtch(ctchProgMax)) &&
tszProg[0]) {
/*
* Make sure the progid exists, or we end up putting garbage
* into the listview.
*/
if (RegKeyExists(hkCR, tszProg)) {
/*
* Is this an OLE class?
*/
lstrcatnBsA(tszProg, c_tszClsid);
if (GetRegStr(hkCR, tszProg, 0,
pti->tszKey, cbX(pti->tszKey))) {
pti->hkRoot = pcdii->hkClsid;
fRc = Template_AddHkeyTemplate(ptci);
} else {
fRc = 0;
}
/*
* If we haven't succeeded yet, then try under the extension
* itself.
*/
if (!fRc) {
pti->hkRoot = hkCR;
lstrcpyn(pti->tszKey, ptci->ptszExt, cA(pti->tszKey));
fRc = Template_AddHkeyTemplate(ptci);
}
} else {
fRc = 0;
}
} else {
fRc = 0;
}
} else {
fRc = 0;
}
return fRc;
}
/*****************************************************************************
*
* Template_AddTemplates_AddMe
*
* Add any templates that exist.
*
* For each filename extension, get its corresponding class.
* Then ask Template_LocateExtension to do the rest.
*
*****************************************************************************/
BOOL PASCAL
Template_AddTemplates_AddMe(PTCI ptci, HKEY hk, PCTSTR ptszShellNew)
{
return Template_AddTemplateInfo(ptci->hwnd, ptci->ptszExt,
ptci->pti,
ptszShellNew == c_tszShellNew) + 1;
}
/*****************************************************************************
*
* Template_AddTemplates
*
* Add any templates that exist.
*
* For each filename extension, get its corresponding class.
* Then ask Template_LocateExtension to do the rest.
*
*****************************************************************************/
void PASCAL
Template_AddTemplates(HWND hwnd)
{
int i;
TCI tci;
TCH tszExt[10];
tci.pfn = Template_AddTemplates_AddMe;
tci.hwnd = hwnd;
tci.ptszExt = tszExt;
HCURSOR hcurPrev = SetCursor(LoadCursor(0, IDC_WAIT));
for (i = 0; ; i++) {
switch (RegEnumKey(HKEY_CLASSES_ROOT, i, tszExt, cbX(tszExt))) {
case ERROR_SUCCESS:
/*
* Don't show ".lnk" in the templates list because it's weird.
*/
if (tszExt[0] == TEXT('.') && lstrcmpi(tszExt, c_tszDotLnk)) {
Template_LocateExtension(&tci);
}
break;
case ERROR_MORE_DATA: /* Can't be a .ext if > 10 */
break;
default: goto endenum;
}
}
endenum:;
SetCursor(hcurPrev);
}
/*****************************************************************************
*
* File template callback info
*
*****************************************************************************/
typedef struct FTCI {
TCI tci;
PCTSTR ptszSrc;
PCTSTR ptszDst;
PCTSTR ptszLastBS;
PCTSTR ptszShellNew;
} FTCI, *PFTCI;
/*****************************************************************************
*
* Template_CopyFile
*
* Copy a file with the hourglass.
*
*****************************************************************************/
BOOL PASCAL
Template_CopyFile(PFTCI pftci)
{
HCURSOR hcurPrev;
BOOL fRc;
hcurPrev = SetCursor(LoadCursor(0, MAKEINTRESOURCE(IDC_WAIT)));
fRc = CopyFile(pftci->ptszSrc, pftci->ptszDst, 0);
SetCursor(hcurPrev);
return fRc;
}
/*****************************************************************************
*
* Template_AddFileTemplate_CheckMe
*
* Make sure the key isn't a Command key.
*
*****************************************************************************/
BOOL PASCAL
Template_AddFileTemplate_CheckMe(PTCI ptci, HKEY hk, PCTSTR ptszShellNew)
{
PFTCI pftci = (PFTCI)ptci;
pftci->ptszShellNew = ptszShellNew;
if (Template_SubkeyExists(hk, c_tszCommand)) {
ptci->ptszExt = 0;
}
return 1;
}
/*****************************************************************************
*
* Template_IsTemplatable
*
* Determine whether there is an application that can handle this
* extension. We assume it's okay if the item is not self-executing.
*
*****************************************************************************/
BOOL PASCAL
Template_IsTemplatable(PCTSTR ptszFile)
{
LONG cb;
TCH tszShort[MAX_PATH];
DWORD ctch;
ctch = GetFullPathName(ptszFile, cA(tszShort), tszShort, NULL);
if (ctch && ctch < MAX_PATH) {
/* If this fails, just proceed with the value from GFPN */
GetShortPathName(ptszFile, tszShort, cA(tszShort));
TCH tszExe[MAX_PATH];
if (FindExecutable(tszShort, NULL, tszExe) >= (HINSTANCE)IntToPtr(HINSTANCE_ERROR)) {
return lstrcmpi(tszExe, tszShort) != 0;
}
}
return 0;
}
/*****************************************************************************
*
* Template_ReplaceTemplate
*
* Replace the current template with the new one.
*
*****************************************************************************/
UINT PASCAL
Template_ReplaceTemplate(PFTCI pftci)
{
#define ptci (&pftci->tci)
UINT id;
if (ptci->ptszExt) {
if (MessageBoxId(GetParent(ptci->hwnd), IDS_CONFIRMNEWTEMPLATE,
pftci->ptszSrc,
MB_YESNO | MB_DEFBUTTON2 | MB_SETFOREGROUND)
== IDYES) {
HKEY hk;
lstrcatnBsA(ptci->pti->tszKey, pftci->ptszShellNew);
if (_RegOpenKey(ptci->pti->hkRoot, ptci->pti->tszKey, &hk) == 0) {
if (Template_CopyFile(pftci)) {
RegDeleteValue(hk, c_tszNullFile);
RegDeleteValue(hk, c_tszData);
RegSetValuePtsz(hk, c_tszFileName, pftci->ptszLastBS+1);
id = 0;
} else {
id = IDS_COPYFAIL;
}
RegCloseKey(hk);
} else {
id = IDS_REGFAIL;
}
} else {
id = 0;
}
} else {
id = IDS_CANNOTTEMPLATE;
}
return id;
}
#undef ptci
/*****************************************************************************
*
* Template_AddFileTemplate
*
* Add the file as a new template type.
*
*****************************************************************************/
UINT PASCAL
Template_AddFileTemplate(HWND hwnd, PCTSTR ptszSrc)
{
#define pftci (&ftci)
UINT id;
FTCI ftci;
pftci->tci.pfn = Template_AddFileTemplate_CheckMe;
pftci->tci.hwnd = hwnd;
pftci->ptszSrc = ptszSrc;
pftci->ptszLastBS = ptszFilenameCqn(ptszSrc) - 1; /* -> \filename.ext */
if (pftci->ptszLastBS) {
pftci->tci.ptszExt = ptszStrRChr(pftci->ptszLastBS, '.'); /* -> .ext */
if (pftci->tci.ptszExt) {
HKEY hk;
if (_RegOpenKey(hkCR, pftci->tci.ptszExt, &hk) == 0) {
if (Template_IsTemplatable(ptszSrc)) {
PTI pti;
TCH tszDst[MAX_PATH];
pftci->ptszDst = tszDst;
SHGetPathFromIDList(pcdii->pidlTemplates, tszDst);
lstrcatnBsA(tszDst, pftci->ptszLastBS+1);
/* Snoop at the next pti to ensure we can get it later */
pti = (PTI)Misc_AllocPx(&ptdii->gxa);
if (pti) {
if (Template_LocateExtension(&pftci->tci)) {
id = Template_ReplaceTemplate(pftci);
} else {
if (Template_CopyFile(pftci)) {
HKEY hk2;
if (RegCreateKey(hk, c_tszShellNew, &hk2) == 0) {
RegSetValuePtsz(hk2, c_tszFileName, pftci->ptszLastBS+1);
RegCloseKey(hk2);
Misc_LV_SetCurSel(hwnd,
Template_AddTemplateInfo(hwnd,
pftci->tci.ptszExt, pti, 1));
Template_NudgeExplorer();
id = 0; /* No problemo */
} else {
id = IDS_REGFAIL;
}
} else {
id = IDS_COPYFAIL;
}
}
} else {
id = 0; /* out of memory! */
}
} else {
id = IDS_BADEXT;
}
RegCloseKey(hk);
} else {
id = IDS_BADEXT;
}
} else {
id = IDS_BADEXT;
}
} else {
id = 0; /* This can't happen! */
}
return id;
}
/*****************************************************************************
*
* Tools_Template_OnDropFiles
*
* Put the file into the template edit control.
*
*****************************************************************************/
void PASCAL
Tools_Template_OnDropFiles(HWND hwnd, HDROP hdrop)
{
if (DragQueryFile(hdrop, (UINT)-1, 0, 0) == 1) {
UINT id;
TCH tszSrc[MAX_PATH];
DragQueryFile(hdrop, 0, tszSrc, cA(tszSrc));
id = Template_AddFileTemplate(hwnd, tszSrc);
if (id) {
MessageBoxId(GetParent(hwnd), id, tszSrc, MB_OK | MB_SETFOREGROUND);
}
} else {
MessageBoxId(GetParent(hwnd), IDS_TOOMANY, g_tszName,
MB_OK | MB_SETFOREGROUND);
}
DragFinish(hdrop);
}
/*****************************************************************************
*
* Tools_Template_WndProc
*
* Subclass procedure so we can handle drag-drop to the template
* edit control.
*
*****************************************************************************/
LRESULT EXPORT
Tools_Template_WndProc(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam)
{
switch (wm) {
case WM_DROPFILES:
Tools_Template_OnDropFiles(hwnd, (HDROP)wParam);
return 0;
}
return CallWindowProc(ptdii->wpTemplate, hwnd, wm, wParam, lParam);
}
/*****************************************************************************
*
* Template_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
Template_GetIcon(LPARAM iti)
{
SHFILEINFO sfi;
PTI pti = ptiIti(iti);
sfi.iIcon = 0;
OutputDebugString(pti->tszExt);
SHGetFileInfo(pti->tszExt, 0, &sfi, cbX(sfi),
SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
return sfi.iIcon;
}
/*****************************************************************************
*
* Template_OnInitDialog
*
* Turn the "drop a file here" gizmo into a valid drop target.
*
*****************************************************************************/
BOOL PASCAL
Template_OnInitDialog(HWND hwnd)
{
ptdii->wpTemplate = SubclassWindow(hwnd, Tools_Template_WndProc);
DragAcceptFiles(hwnd, 1);
if (Misc_InitPgxa(&ptdii->gxa, cbX(TI))) {
Template_AddTemplates(hwnd);
}
return 1;
}
/*****************************************************************************
*
* Template_OnDelete
*
* Really nuke it. The interaction between this and adding a new
* template is sufficiently weird that I don't want to try to do
* delayed-action.
*
*****************************************************************************/
void PASCAL
Template_OnDelete(HWND hwnd, int iItem)
{
LV_ITEM lvi;
HKEY hk;
PTI pti;
TCH tszDesc[MAX_PATH];
lvi.pszText = tszDesc;
lvi.cchTextMax = cA(tszDesc);
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM | LVIF_TEXT);
pti = ptiPlvi(&lvi);
if (MessageBoxId(GetParent(hwnd), IDS_TEMPLATEDELETEWARN,
lvi.pszText, MB_YESNO | MB_DEFBUTTON2) == IDYES) {
if (_RegOpenKey(pti->hkRoot, pti->tszKey, &hk) == 0) {
RegDeleteTree(hk, c_tszShellNewDash);
RegDeleteTree(hk, c_tszShellNew);
ListView_DeleteItem(hwnd, iItem);
Misc_LV_EnsureSel(hwnd, iItem);
Common_SetDirty(GetParent(hwnd));
RegCloseKey(hk);
}
}
}
/*****************************************************************************
*
* Template_OnSelChange
*
* Disable the Remove button if we can't remove the thing.
*
*****************************************************************************/
void PASCAL
Template_OnSelChange(HWND hwnd, int iItem)
{
PTI pti = ptiIti(Misc_LV_GetParam(hwnd, iItem));
HKEY hk;
BOOL fEnable;
if (_RegOpenKey(pti->hkRoot, pti->tszKey, &hk) == 0) {
if (GetRegStr(hk, pti->isi == isiUnchecked ?
c_tszShellNewDash : c_tszShellNew, c_tszCommand, 0, 0)) {
fEnable = 0;
} else {
fEnable = 1;
}
RegCloseKey(hk);
} else {
fEnable = 0;
}
EnableWindow(GetDlgItem(GetParent(hwnd), IDC_LVDELETE), fEnable);
}
/*****************************************************************************
*
* Template_OnApply
*
*****************************************************************************/
void PASCAL
Template_OnApply(HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_TEMPLATE);
int cItems = ListView_GetItemCount(hwnd);
BOOL fDirty;
LV_ITEM lvi;
fDirty = 0;
for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++) {
PTI pti;
lvi.stateMask = LVIS_STATEIMAGEMASK;
Misc_LV_GetItemInfo(hwnd, &lvi, lvi.iItem, LVIF_PARAM | LVIF_STATE);
pti = ptiPlvi(&lvi);
if (pti->isi != isiPlvi(&lvi)) {
PCTSTR ptszFrom, ptszTo;
if (pti->isi == isiUnchecked) {
ptszFrom = c_tszShellNewDash;
ptszTo = c_tszShellNew;
} else {
ptszFrom = c_tszShellNew;
ptszTo = c_tszShellNewDash;
}
if (Misc_RenameReg(pti->hkRoot, pti->tszKey, ptszFrom, ptszTo)) {
pti->isi = isiPlvi(&lvi);
}
fDirty = 1;
}
}
if (fDirty) {
Template_NudgeExplorer();
}
}
/*****************************************************************************
*
* Template_OnDestroy
*
* Free the memory we allocated.
*
*****************************************************************************/
void PASCAL
Template_OnDestroy(HWND hdlg)
{
Misc_FreePgxa(&ptdii->gxa);
}
/*****************************************************************************
*
* Oh yeah, we need this too.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
LVCI lvciTemplate[] = {
{ IDC_LVDELETE, Template_OnDelete },
{ 0, 0 },
};
LVV lvvTemplate = {
0, /* Template_OnCommand */
0, /* Template_OnInitContextMenu */
0, /* Template_Dirtify */
Template_GetIcon,
Template_OnInitDialog,
Template_OnApply,
Template_OnDestroy,
Template_OnSelChange,
3,
rgdwHelp,
0, /* Double-click action */
lvvflIcons | /* We need icons */
lvvflCanCheck | /* And check boxes */
lvvflCanDelete, /* and you can delete them too */
lvciTemplate,
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* Our window procedure.
*
*****************************************************************************/
INT_PTR EXPORT
Template_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
return LV_DlgProc(&lvvTemplate, hdlg, wm, wParam, lParam);
}