#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 cchIconPath; // 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: // void GetDefaultIconImageName(LPTSTR pszBuffer, int cchBuffer) { WCHAR szModName[MAX_PATH]; GetModuleFileName(HINST_THISDLL, szModName, ARRAYSIZE(szModName)); if (!PathUnExpandEnvStrings(szModName, pszBuffer, cchBuffer)) { StringCchCopy(pszBuffer, cchBuffer, 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)); StringCchCopy(lppid->szBuffer, ARRAYSIZE(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, ARRAYSIZE(lppid->szBuffer)); } else { return; } } StringCchCopy(lppid->szPathField, ARRAYSIZE(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, ARRAYSIZE(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; StringCchCopy(lppid->szPathField, ARRAYSIZE(lppid->szPathField), lppid->pszIconPath); // 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->cchIconPath-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; StringCchCopy(lppid->pszIconPath, lppid->cchIconPath, lppid->szPathField); 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 cchIconPath, int *piIconIndex) { return SUCCEEDED(PickIconDlgWithTitle(hwnd, NULL, FALSE, pszIconPath, cchIconPath, 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 cchIconPath, int *piIconIndex) { RIPMSG(pszIconPath && IS_VALID_WRITE_BUFFER(pszIconPath, TCHAR, cchIconPath), "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->cchIconPath = cchIconPath; 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 E_OUTOFMEMORY; } return E_INVALIDARG; }