504 lines
15 KiB
C
504 lines
15 KiB
C
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#define MAX_ICONS 500 // that is a lot 'o icons
|
|
|
|
#define CX_BORDER 4
|
|
#define CY_BORDER 12
|
|
|
|
typedef struct {
|
|
LPCTSTR pszDialogTitle; // input
|
|
BOOL bShowRestoreButton; // input
|
|
LPTSTR pszIconPath; // input/output
|
|
int cbIconPath; // input
|
|
int iIconIndex; // input/output
|
|
// private state variables
|
|
HWND hDlg;
|
|
BOOL fFirstPass;
|
|
TCHAR szPathField[MAX_PATH];
|
|
TCHAR szBuffer[MAX_PATH];
|
|
} PICKICON_DATA, *LPPICKICON_DATA;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
int iResult; // icon index within the resources
|
|
int iResId; // resource ID to search for!
|
|
} ICONENUMSTATE, *LPICONENUMSTATE;
|
|
|
|
|
|
// Call back function used when trying to find the correct icon to be
|
|
// highlighted, called with the name of each resource - we compare this
|
|
// against the one specified in the structure and bail out if we get
|
|
// a match.
|
|
|
|
BOOL CALLBACK IconEnumProc( HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam )
|
|
{
|
|
LPICONENUMSTATE pState = (LPICONENUMSTATE)lParam;
|
|
|
|
if ( (INT_PTR)lpszName == pState->iResId )
|
|
return FALSE; // bail out of enum loop
|
|
|
|
pState->iResult++;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Checks if the file exists, if it doesn't it tries tagging on .exe and
|
|
// if that fails it reports an error. The given path is environment expanded.
|
|
// If it needs to put up an error box, it changes the cursor back.
|
|
// Path s assumed to be MAXITEMPATHLEN long.
|
|
// The main reason for moving this out of the DlgProc was because we're
|
|
// running out of stack space on the call to the comm dlg.
|
|
|
|
BOOL IconFileExists(LPPICKICON_DATA lppid)
|
|
{
|
|
TCHAR szExpBuffer[ ARRAYSIZE(lppid->szBuffer) ];
|
|
|
|
if (lppid->szBuffer[0] == 0)
|
|
return FALSE;
|
|
|
|
if (SHExpandEnvironmentStrings(lppid->szBuffer, szExpBuffer, ARRAYSIZE(szExpBuffer)))
|
|
{
|
|
PathUnquoteSpaces(lppid->szBuffer);
|
|
PathUnquoteSpaces(szExpBuffer);
|
|
|
|
if (PathResolve(szExpBuffer, NULL, PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS))
|
|
return TRUE;
|
|
|
|
ShellMessageBox(HINST_THISDLL, lppid->hDlg, MAKEINTRESOURCE(IDS_BADPATHMSG), 0, MB_OK | MB_ICONEXCLAMATION, (LPTSTR)lppid->szPathField);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// GetDefaultIconImageName:
|
|
// szBuffer should be at least MAX_PATH chars big
|
|
//
|
|
void GetDefaultIconImageName( LPTSTR szBuffer )
|
|
{
|
|
TCHAR szModName[ MAX_PATH ];
|
|
TCHAR szSystemDir[ MAX_PATH ];
|
|
DWORD cbSysDir;
|
|
|
|
GetModuleFileName(HINST_THISDLL, szModName, ARRAYSIZE(szModName));
|
|
cbSysDir = GetSystemDirectory(szSystemDir, ARRAYSIZE(szSystemDir) );
|
|
if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, szSystemDir, cbSysDir, szModName, cbSysDir) == 2)
|
|
{
|
|
//
|
|
// Okay, the path for SHELL32.DLL starts w/the system directory.
|
|
// To be sneaky and helpfull, we're gonna change it to "%systemroot%"
|
|
//
|
|
|
|
lstrcpy( szBuffer, TEXT("%SystemRoot%\\system32") );
|
|
PathAppend( szBuffer, PathFindFileName(szModName) );
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(szBuffer, szModName );
|
|
}
|
|
}
|
|
|
|
void PutIconsInList(LPPICKICON_DATA lppid)
|
|
{
|
|
HICON *rgIcons;
|
|
int cIcons;
|
|
HWND hDlg = lppid->hDlg;
|
|
DECLAREWAITCURSOR;
|
|
LONG err = LB_ERR;
|
|
|
|
SendDlgItemMessage(hDlg, IDD_ICON, LB_RESETCONTENT, 0, 0L);
|
|
|
|
GetDlgItemText(hDlg, IDD_PATH, lppid->szPathField, ARRAYSIZE(lppid->szPathField));
|
|
|
|
lstrcpy(lppid->szBuffer, lppid->szPathField);
|
|
|
|
if (!IconFileExists(lppid)) {
|
|
if (lppid->fFirstPass) {
|
|
|
|
// Icon File doesn't exist, use progman
|
|
lppid->fFirstPass = FALSE; // Only do this bit once.
|
|
GetDefaultIconImageName( lppid->szBuffer );
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
lstrcpy(lppid->szPathField, lppid->szBuffer);
|
|
SetDlgItemText(hDlg, IDD_PATH, lppid->szPathField);
|
|
|
|
SetWaitCursor();
|
|
|
|
rgIcons = (HICON *)LocalAlloc(LPTR, MAX_ICONS*SIZEOF(HICON));
|
|
|
|
if (rgIcons != NULL)
|
|
cIcons = (int)ExtractIconEx(lppid->szBuffer, 0, rgIcons, NULL, MAX_ICONS);
|
|
else
|
|
cIcons = 0;
|
|
|
|
ResetWaitCursor();
|
|
if (!cIcons) {
|
|
|
|
if (lppid->fFirstPass) {
|
|
|
|
lppid->fFirstPass = FALSE; // Only do this bit once.
|
|
|
|
ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_NOICONSMSG1), 0, MB_OK | MB_ICONEXCLAMATION, (LPCTSTR)lppid->szBuffer);
|
|
|
|
// No icons here - change the path do somewhere where we
|
|
// know there are icons. Get the path to progman.
|
|
GetDefaultIconImageName( lppid->szPathField );
|
|
SetDlgItemText(hDlg, IDD_PATH, lppid->szPathField);
|
|
PutIconsInList(lppid);
|
|
} else {
|
|
|
|
ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_NOICONSMSG), 0, MB_OK | MB_ICONEXCLAMATION, (LPCTSTR)lppid->szBuffer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
SetWaitCursor();
|
|
|
|
SendDlgItemMessage(hDlg, IDD_ICON, WM_SETREDRAW, FALSE, 0L);
|
|
|
|
if (rgIcons) {
|
|
int i;
|
|
for (i = 0; i < cIcons; i++) {
|
|
SendDlgItemMessage(hDlg, IDD_ICON, LB_ADDSTRING, 0, (LPARAM)(UINT_PTR)rgIcons[i]);
|
|
}
|
|
LocalFree((HLOCAL)rgIcons);
|
|
}
|
|
|
|
// Cope with being given a resource ID, not an index into the icon array. To do this
|
|
// we must enumerate the icon names checking for a match. If we have one then highlight
|
|
// that, otherwise default to the first.
|
|
//
|
|
// A resource icon reference is indicated by being passed a -ve iIconIndex.
|
|
|
|
if ( lppid->iIconIndex >= 0 )
|
|
{
|
|
err = (LONG) SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, lppid->iIconIndex, 0L);
|
|
}
|
|
else
|
|
{
|
|
HMODULE hModule = LoadLibrary( lppid->szBuffer );
|
|
if (hModule)
|
|
{
|
|
ICONENUMSTATE state;
|
|
|
|
state.iResult = 0;
|
|
state.iResId = -(lppid->iIconIndex);
|
|
|
|
EnumResourceNames( hModule, RT_GROUP_ICON, IconEnumProc, (LONG_PTR)&state );
|
|
|
|
err = (LONG) SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, state.iResult, 0L );
|
|
FreeLibrary( hModule );
|
|
}
|
|
}
|
|
|
|
// Check for failure, if we did then ensure we highlight the first!
|
|
|
|
if ( err == LB_ERR )
|
|
SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, 0, 0L );
|
|
|
|
SendDlgItemMessage(hDlg, IDD_ICON, WM_SETREDRAW, TRUE, 0L);
|
|
InvalidateRect(GetDlgItem(hDlg, IDD_ICON), NULL, TRUE);
|
|
|
|
ResetWaitCursor();
|
|
}
|
|
|
|
|
|
void InitPickIconDlg(HWND hDlg, LPPICKICON_DATA lppid)
|
|
{
|
|
RECT rc;
|
|
UINT cy;
|
|
HWND hwndIcons;
|
|
|
|
// init state variables
|
|
|
|
lppid->hDlg = hDlg;
|
|
lstrcpyn(lppid->szPathField, lppid->pszIconPath, ARRAYSIZE(lppid->szPathField));
|
|
|
|
// this first pass stuff is so that the first time something
|
|
// bogus happens (file not found, no icons) we give the user
|
|
// a list of icons from progman.
|
|
lppid->fFirstPass = TRUE;
|
|
|
|
// Override the Dialog Title if Set. Else use the default Title defined in the Dialog resource.
|
|
if (lppid->pszDialogTitle && (lppid->pszDialogTitle[0] != TEXT('\0')))
|
|
{
|
|
SetWindowText(hDlg, lppid->pszDialogTitle);
|
|
}
|
|
|
|
// Enable or Disable the Restore Default button.
|
|
if (lppid->bShowRestoreButton)
|
|
ShowWindow(GetDlgItem(lppid->hDlg, IDD_RESTORE),SW_SHOW);
|
|
else
|
|
ShowWindow(GetDlgItem(lppid->hDlg, IDD_RESTORE), SW_HIDE);
|
|
|
|
|
|
// init the dialog controls
|
|
|
|
SetDlgItemText(hDlg, IDD_PATH, lppid->pszIconPath);
|
|
|
|
// Cannot max against 0 because 0 means "no limit"
|
|
SendDlgItemMessage(hDlg, IDD_PATH, EM_LIMITTEXT, max(lppid->cbIconPath-1, 1), 0L);
|
|
|
|
SendDlgItemMessage(hDlg, IDD_ICON, LB_SETCOLUMNWIDTH, GetSystemMetrics(SM_CXICON) + CX_BORDER, 0L);
|
|
|
|
hwndIcons = GetDlgItem(hDlg, IDD_ICON);
|
|
|
|
/* compute the height of the listbox based on icon dimensions */
|
|
GetClientRect(hwndIcons, &rc);
|
|
|
|
cy = ((GetSystemMetrics(SM_CYICON) + CY_BORDER) * 4) +
|
|
GetSystemMetrics(SM_CYHSCROLL) +
|
|
GetSystemMetrics(SM_CYEDGE) * 3;
|
|
|
|
SetWindowPos(hwndIcons, NULL, 0, 0, rc.right, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
SHAutoComplete(GetDlgItem(hDlg, IDD_PATH), 0);
|
|
|
|
PutIconsInList(lppid);
|
|
}
|
|
|
|
|
|
// call the common browse code for this
|
|
|
|
BOOL BrowseForIconFile(LPPICKICON_DATA lppid)
|
|
{
|
|
TCHAR szTitle[80];
|
|
|
|
GetWindowText(lppid->hDlg, szTitle, ARRAYSIZE(szTitle));
|
|
GetDlgItemText(lppid->hDlg, IDD_PATH, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer));
|
|
|
|
// We will never be quoted here because IconFileExists() removes quotes (of course user could type them in)
|
|
if (lppid->szBuffer[0] != '"')
|
|
PathQuoteSpaces(lppid->szBuffer);
|
|
|
|
if (GetFileNameFromBrowse(lppid->hDlg, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer), NULL, MAKEINTRESOURCE(IDS_ICO), MAKEINTRESOURCE(IDS_ICONSFILTER), szTitle))
|
|
{
|
|
PathQuoteSpaces(lppid->szBuffer);
|
|
SetDlgItemText(lppid->hDlg, IDD_PATH, lppid->szBuffer);
|
|
// Set default button to OK.
|
|
SendMessage(lppid->hDlg, DM_SETDEFID, IDOK, 0);
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
// test if the name field is different from the last file we looked at
|
|
|
|
BOOL NameChange(LPPICKICON_DATA lppid)
|
|
{
|
|
GetDlgItemText(lppid->hDlg, IDD_PATH, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer));
|
|
|
|
return lstrcmpi(lppid->szBuffer, lppid->szPathField);
|
|
}
|
|
|
|
|
|
//
|
|
// dialog procedure for picking an icon (ala progman change icon)
|
|
// uses DLG_PICKICON template
|
|
//
|
|
// in:
|
|
// pszIconFile
|
|
// cbIconFile
|
|
// iIndex
|
|
//
|
|
// out:
|
|
// pszIconFile
|
|
// iIndex
|
|
//
|
|
|
|
BOOL_PTR CALLBACK PickIconDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPPICKICON_DATA lppid = (LPPICKICON_DATA)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
DWORD dwOldLayout;
|
|
|
|
// Array for context help:
|
|
|
|
static const DWORD aPickIconHelpIDs[] = {
|
|
IDD_PATH, IDH_FCAB_LINK_ICONNAME,
|
|
IDD_ICON, IDH_FCAB_LINK_CURRENT_ICON,
|
|
IDD_BROWSE, IDH_BROWSE,
|
|
|
|
0, 0
|
|
};
|
|
|
|
switch (wMsg) {
|
|
case WM_INITDIALOG:
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
InitPickIconDlg(hDlg, (LPPICKICON_DATA)lParam);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
|
|
case IDD_BROWSE:
|
|
if (BrowseForIconFile(lppid))
|
|
PutIconsInList(lppid);
|
|
break;
|
|
|
|
case IDD_PATH:
|
|
if (NameChange(lppid))
|
|
SendDlgItemMessage(hDlg, IDD_ICON, LB_SETCURSEL, (WPARAM)-1, 0);
|
|
break;
|
|
|
|
case IDD_ICON:
|
|
if (NameChange(lppid)) {
|
|
PutIconsInList(lppid);
|
|
break;
|
|
}
|
|
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) != LBN_DBLCLK)
|
|
break;
|
|
|
|
/*** FALL THRU on double click ***/
|
|
|
|
case IDOK:
|
|
|
|
if (NameChange(lppid)) {
|
|
PutIconsInList(lppid);
|
|
} else {
|
|
int iIconIndex = (int)SendDlgItemMessage(hDlg, IDD_ICON, LB_GETCURSEL, 0, 0L);
|
|
if (iIconIndex < 0)
|
|
iIconIndex = 0;
|
|
lppid->iIconIndex = iIconIndex;
|
|
lstrcpyn(lppid->pszIconPath, lppid->szPathField, lppid->cbIconPath);
|
|
|
|
EndDialog(hDlg, S_OK);
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
|
break;
|
|
|
|
case IDD_RESTORE:
|
|
EndDialog(hDlg, S_FALSE);
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
// owner draw messages for icon listbox
|
|
|
|
case WM_DRAWITEM:
|
|
#define lpdi ((DRAWITEMSTRUCT *)lParam)
|
|
|
|
if (lpdi->itemState & ODS_SELECTED)
|
|
SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
|
|
else
|
|
SetBkColor(lpdi->hDC, GetSysColor(COLOR_WINDOW));
|
|
|
|
|
|
/* repaint the selection state */
|
|
ExtTextOut(lpdi->hDC, 0, 0, ETO_OPAQUE, &lpdi->rcItem, NULL, 0, NULL);
|
|
|
|
dwOldLayout = GET_DC_LAYOUT(lpdi->hDC);
|
|
|
|
if (g_bMirroredOS && dwOldLayout)
|
|
{
|
|
SET_DC_LAYOUT(lpdi->hDC, dwOldLayout | LAYOUT_PRESERVEBITMAP);
|
|
}
|
|
|
|
/* draw the icon */
|
|
if ((int)lpdi->itemID >= 0)
|
|
DrawIcon(lpdi->hDC, (lpdi->rcItem.left + lpdi->rcItem.right - GetSystemMetrics(SM_CXICON)) / 2,
|
|
(lpdi->rcItem.bottom + lpdi->rcItem.top - GetSystemMetrics(SM_CYICON)) / 2, (HICON)lpdi->itemData);
|
|
if (dwOldLayout)
|
|
{
|
|
SET_DC_LAYOUT(lpdi->hDC, dwOldLayout);
|
|
}
|
|
|
|
// InflateRect(&lpdi->rcItem, -1, -1);
|
|
|
|
/* if it has the focus, draw the focus */
|
|
if (lpdi->itemState & ODS_FOCUS)
|
|
DrawFocusRect(lpdi->hDC, &lpdi->rcItem);
|
|
|
|
#undef lpdi
|
|
break;
|
|
|
|
case WM_MEASUREITEM:
|
|
#define lpmi ((MEASUREITEMSTRUCT *)lParam)
|
|
|
|
lpmi->itemWidth = GetSystemMetrics(SM_CXICON) + CX_BORDER;
|
|
lpmi->itemHeight = GetSystemMetrics(SM_CYICON) + CY_BORDER;
|
|
|
|
#undef lpmi
|
|
break;
|
|
|
|
case WM_DELETEITEM:
|
|
#define lpdi ((DELETEITEMSTRUCT *)lParam)
|
|
|
|
DestroyIcon((HICON)lpdi->itemData);
|
|
|
|
#undef lpdi
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp(((LPHELPINFO) lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aPickIconHelpIDs);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
|
|
(ULONG_PTR)(LPVOID)aPickIconHelpIDs);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// puts up the pick icon dialog
|
|
|
|
STDAPI_(int) PickIconDlg(HWND hwnd, IN OUT LPTSTR pszIconPath, UINT cbIconPath, int *piIconIndex)
|
|
{
|
|
return SUCCEEDED(PickIconDlgWithTitle(hwnd, NULL, FALSE, pszIconPath, cbIconPath, piIconIndex));
|
|
}
|
|
|
|
// puts up the pick icon dialog with a customized Title for the Dialog Window.
|
|
|
|
STDAPI PickIconDlgWithTitle(HWND hwnd, LPCTSTR pszTitle, BOOL bShowRestoreButton, IN OUT LPTSTR pszIconPath, UINT cbIconPath, int *piIconIndex)
|
|
{
|
|
RIPMSG(pszIconPath && IS_VALID_WRITE_BUFFER(pszIconPath, TCHAR, cbIconPath), "PickIconDlgWithTitle: caller passed bad pszIconPath");
|
|
RIPMSG(piIconIndex != NULL, "PickIconDlgWithTitle: caller passed bad piIconIndex");
|
|
|
|
if (pszIconPath && piIconIndex)
|
|
{
|
|
PICKICON_DATA *pid = (PICKICON_DATA *)LocalAlloc(LPTR, sizeof(PICKICON_DATA));
|
|
if (pid)
|
|
{
|
|
HRESULT res;
|
|
|
|
pid->pszDialogTitle = pszTitle;
|
|
pid->bShowRestoreButton = bShowRestoreButton;
|
|
pid->pszIconPath = pszIconPath;
|
|
pid->cbIconPath = cbIconPath;
|
|
pid->iIconIndex = *piIconIndex;
|
|
|
|
res = (HRESULT)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_PICKICON), hwnd, PickIconDlgProc, (LPARAM)pid);
|
|
|
|
*piIconIndex = pid->iIconIndex;
|
|
|
|
LocalFree(pid);
|
|
|
|
return res;
|
|
}
|
|
|
|
*piIconIndex = 0;
|
|
*pszIconPath = 0;
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
|
}
|
|
return E_INVALIDARG;
|
|
}
|