windows-nt/Source/XPSP1/NT/shell/shell32/pickicon.c
2020-09-26 16:20:57 +08:00

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;
}