Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

503 lines
15 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #define MAX_ICONS 500 // that is a lot 'o icons
  4. #define CX_BORDER 4
  5. #define CY_BORDER 12
  6. typedef struct {
  7. LPCTSTR pszDialogTitle; // input
  8. BOOL bShowRestoreButton; // input
  9. LPTSTR pszIconPath; // input/output
  10. int cbIconPath; // input
  11. int iIconIndex; // input/output
  12. // private state variables
  13. HWND hDlg;
  14. BOOL fFirstPass;
  15. TCHAR szPathField[MAX_PATH];
  16. TCHAR szBuffer[MAX_PATH];
  17. } PICKICON_DATA, *LPPICKICON_DATA;
  18. typedef struct
  19. {
  20. int iResult; // icon index within the resources
  21. int iResId; // resource ID to search for!
  22. } ICONENUMSTATE, *LPICONENUMSTATE;
  23. // Call back function used when trying to find the correct icon to be
  24. // highlighted, called with the name of each resource - we compare this
  25. // against the one specified in the structure and bail out if we get
  26. // a match.
  27. BOOL CALLBACK IconEnumProc( HANDLE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam )
  28. {
  29. LPICONENUMSTATE pState = (LPICONENUMSTATE)lParam;
  30. if ( (INT_PTR)lpszName == pState->iResId )
  31. return FALSE; // bail out of enum loop
  32. pState->iResult++;
  33. return TRUE;
  34. }
  35. // Checks if the file exists, if it doesn't it tries tagging on .exe and
  36. // if that fails it reports an error. The given path is environment expanded.
  37. // If it needs to put up an error box, it changes the cursor back.
  38. // Path s assumed to be MAXITEMPATHLEN long.
  39. // The main reason for moving this out of the DlgProc was because we're
  40. // running out of stack space on the call to the comm dlg.
  41. BOOL IconFileExists(LPPICKICON_DATA lppid)
  42. {
  43. TCHAR szExpBuffer[ ARRAYSIZE(lppid->szBuffer) ];
  44. if (lppid->szBuffer[0] == 0)
  45. return FALSE;
  46. if (SHExpandEnvironmentStrings(lppid->szBuffer, szExpBuffer, ARRAYSIZE(szExpBuffer)))
  47. {
  48. PathUnquoteSpaces(lppid->szBuffer);
  49. PathUnquoteSpaces(szExpBuffer);
  50. if (PathResolve(szExpBuffer, NULL, PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS))
  51. return TRUE;
  52. ShellMessageBox(HINST_THISDLL, lppid->hDlg, MAKEINTRESOURCE(IDS_BADPATHMSG), 0, MB_OK | MB_ICONEXCLAMATION, (LPTSTR)lppid->szPathField);
  53. }
  54. return FALSE;
  55. }
  56. //
  57. // GetDefaultIconImageName:
  58. // szBuffer should be at least MAX_PATH chars big
  59. //
  60. void GetDefaultIconImageName( LPTSTR szBuffer )
  61. {
  62. TCHAR szModName[ MAX_PATH ];
  63. TCHAR szSystemDir[ MAX_PATH ];
  64. DWORD cbSysDir;
  65. GetModuleFileName(HINST_THISDLL, szModName, ARRAYSIZE(szModName));
  66. cbSysDir = GetSystemDirectory(szSystemDir, ARRAYSIZE(szSystemDir) );
  67. if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, szSystemDir, cbSysDir, szModName, cbSysDir) == 2)
  68. {
  69. //
  70. // Okay, the path for SHELL32.DLL starts w/the system directory.
  71. // To be sneaky and helpfull, we're gonna change it to "%systemroot%"
  72. //
  73. lstrcpy( szBuffer, TEXT("%SystemRoot%\\system32") );
  74. PathAppend( szBuffer, PathFindFileName(szModName) );
  75. }
  76. else
  77. {
  78. lstrcpy(szBuffer, szModName );
  79. }
  80. }
  81. void PutIconsInList(LPPICKICON_DATA lppid)
  82. {
  83. HICON *rgIcons;
  84. int cIcons;
  85. HWND hDlg = lppid->hDlg;
  86. DECLAREWAITCURSOR;
  87. LONG err = LB_ERR;
  88. SendDlgItemMessage(hDlg, IDD_ICON, LB_RESETCONTENT, 0, 0L);
  89. GetDlgItemText(hDlg, IDD_PATH, lppid->szPathField, ARRAYSIZE(lppid->szPathField));
  90. lstrcpy(lppid->szBuffer, lppid->szPathField);
  91. if (!IconFileExists(lppid)) {
  92. if (lppid->fFirstPass) {
  93. // Icon File doesn't exist, use progman
  94. lppid->fFirstPass = FALSE; // Only do this bit once.
  95. GetDefaultIconImageName( lppid->szBuffer );
  96. } else {
  97. return;
  98. }
  99. }
  100. lstrcpy(lppid->szPathField, lppid->szBuffer);
  101. SetDlgItemText(hDlg, IDD_PATH, lppid->szPathField);
  102. SetWaitCursor();
  103. rgIcons = (HICON *)LocalAlloc(LPTR, MAX_ICONS*SIZEOF(HICON));
  104. if (rgIcons != NULL)
  105. cIcons = (int)ExtractIconEx(lppid->szBuffer, 0, rgIcons, NULL, MAX_ICONS);
  106. else
  107. cIcons = 0;
  108. ResetWaitCursor();
  109. if (!cIcons) {
  110. if (lppid->fFirstPass) {
  111. lppid->fFirstPass = FALSE; // Only do this bit once.
  112. ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_NOICONSMSG1), 0, MB_OK | MB_ICONEXCLAMATION, (LPCTSTR)lppid->szBuffer);
  113. // No icons here - change the path do somewhere where we
  114. // know there are icons. Get the path to progman.
  115. GetDefaultIconImageName( lppid->szPathField );
  116. SetDlgItemText(hDlg, IDD_PATH, lppid->szPathField);
  117. PutIconsInList(lppid);
  118. } else {
  119. ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_NOICONSMSG), 0, MB_OK | MB_ICONEXCLAMATION, (LPCTSTR)lppid->szBuffer);
  120. return;
  121. }
  122. }
  123. SetWaitCursor();
  124. SendDlgItemMessage(hDlg, IDD_ICON, WM_SETREDRAW, FALSE, 0L);
  125. if (rgIcons) {
  126. int i;
  127. for (i = 0; i < cIcons; i++) {
  128. SendDlgItemMessage(hDlg, IDD_ICON, LB_ADDSTRING, 0, (LPARAM)(UINT_PTR)rgIcons[i]);
  129. }
  130. LocalFree((HLOCAL)rgIcons);
  131. }
  132. // Cope with being given a resource ID, not an index into the icon array. To do this
  133. // we must enumerate the icon names checking for a match. If we have one then highlight
  134. // that, otherwise default to the first.
  135. //
  136. // A resource icon reference is indicated by being passed a -ve iIconIndex.
  137. if ( lppid->iIconIndex >= 0 )
  138. {
  139. err = (LONG) SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, lppid->iIconIndex, 0L);
  140. }
  141. else
  142. {
  143. HMODULE hModule = LoadLibrary( lppid->szBuffer );
  144. if (hModule)
  145. {
  146. ICONENUMSTATE state;
  147. state.iResult = 0;
  148. state.iResId = -(lppid->iIconIndex);
  149. EnumResourceNames( hModule, RT_GROUP_ICON, IconEnumProc, (LONG_PTR)&state );
  150. err = (LONG) SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, state.iResult, 0L );
  151. FreeLibrary( hModule );
  152. }
  153. }
  154. // Check for failure, if we did then ensure we highlight the first!
  155. if ( err == LB_ERR )
  156. SendDlgItemMessage( hDlg, IDD_ICON, LB_SETCURSEL, 0, 0L );
  157. SendDlgItemMessage(hDlg, IDD_ICON, WM_SETREDRAW, TRUE, 0L);
  158. InvalidateRect(GetDlgItem(hDlg, IDD_ICON), NULL, TRUE);
  159. ResetWaitCursor();
  160. }
  161. void InitPickIconDlg(HWND hDlg, LPPICKICON_DATA lppid)
  162. {
  163. RECT rc;
  164. UINT cy;
  165. HWND hwndIcons;
  166. // init state variables
  167. lppid->hDlg = hDlg;
  168. lstrcpyn(lppid->szPathField, lppid->pszIconPath, ARRAYSIZE(lppid->szPathField));
  169. // this first pass stuff is so that the first time something
  170. // bogus happens (file not found, no icons) we give the user
  171. // a list of icons from progman.
  172. lppid->fFirstPass = TRUE;
  173. // Override the Dialog Title if Set. Else use the default Title defined in the Dialog resource.
  174. if (lppid->pszDialogTitle && (lppid->pszDialogTitle[0] != TEXT('\0')))
  175. {
  176. SetWindowText(hDlg, lppid->pszDialogTitle);
  177. }
  178. // Enable or Disable the Restore Default button.
  179. if (lppid->bShowRestoreButton)
  180. ShowWindow(GetDlgItem(lppid->hDlg, IDD_RESTORE),SW_SHOW);
  181. else
  182. ShowWindow(GetDlgItem(lppid->hDlg, IDD_RESTORE), SW_HIDE);
  183. // init the dialog controls
  184. SetDlgItemText(hDlg, IDD_PATH, lppid->pszIconPath);
  185. // Cannot max against 0 because 0 means "no limit"
  186. SendDlgItemMessage(hDlg, IDD_PATH, EM_LIMITTEXT, max(lppid->cbIconPath-1, 1), 0L);
  187. SendDlgItemMessage(hDlg, IDD_ICON, LB_SETCOLUMNWIDTH, GetSystemMetrics(SM_CXICON) + CX_BORDER, 0L);
  188. hwndIcons = GetDlgItem(hDlg, IDD_ICON);
  189. /* compute the height of the listbox based on icon dimensions */
  190. GetClientRect(hwndIcons, &rc);
  191. cy = ((GetSystemMetrics(SM_CYICON) + CY_BORDER) * 4) +
  192. GetSystemMetrics(SM_CYHSCROLL) +
  193. GetSystemMetrics(SM_CYEDGE) * 3;
  194. SetWindowPos(hwndIcons, NULL, 0, 0, rc.right, cy, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  195. SHAutoComplete(GetDlgItem(hDlg, IDD_PATH), 0);
  196. PutIconsInList(lppid);
  197. }
  198. // call the common browse code for this
  199. BOOL BrowseForIconFile(LPPICKICON_DATA lppid)
  200. {
  201. TCHAR szTitle[80];
  202. GetWindowText(lppid->hDlg, szTitle, ARRAYSIZE(szTitle));
  203. GetDlgItemText(lppid->hDlg, IDD_PATH, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer));
  204. // We will never be quoted here because IconFileExists() removes quotes (of course user could type them in)
  205. if (lppid->szBuffer[0] != '"')
  206. PathQuoteSpaces(lppid->szBuffer);
  207. if (GetFileNameFromBrowse(lppid->hDlg, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer), NULL, MAKEINTRESOURCE(IDS_ICO), MAKEINTRESOURCE(IDS_ICONSFILTER), szTitle))
  208. {
  209. PathQuoteSpaces(lppid->szBuffer);
  210. SetDlgItemText(lppid->hDlg, IDD_PATH, lppid->szBuffer);
  211. // Set default button to OK.
  212. SendMessage(lppid->hDlg, DM_SETDEFID, IDOK, 0);
  213. return TRUE;
  214. } else
  215. return FALSE;
  216. }
  217. // test if the name field is different from the last file we looked at
  218. BOOL NameChange(LPPICKICON_DATA lppid)
  219. {
  220. GetDlgItemText(lppid->hDlg, IDD_PATH, lppid->szBuffer, ARRAYSIZE(lppid->szBuffer));
  221. return lstrcmpi(lppid->szBuffer, lppid->szPathField);
  222. }
  223. //
  224. // dialog procedure for picking an icon (ala progman change icon)
  225. // uses DLG_PICKICON template
  226. //
  227. // in:
  228. // pszIconFile
  229. // cbIconFile
  230. // iIndex
  231. //
  232. // out:
  233. // pszIconFile
  234. // iIndex
  235. //
  236. BOOL_PTR CALLBACK PickIconDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  237. {
  238. LPPICKICON_DATA lppid = (LPPICKICON_DATA)GetWindowLongPtr(hDlg, DWLP_USER);
  239. DWORD dwOldLayout;
  240. // Array for context help:
  241. static const DWORD aPickIconHelpIDs[] = {
  242. IDD_PATH, IDH_FCAB_LINK_ICONNAME,
  243. IDD_ICON, IDH_FCAB_LINK_CURRENT_ICON,
  244. IDD_BROWSE, IDH_BROWSE,
  245. 0, 0
  246. };
  247. switch (wMsg) {
  248. case WM_INITDIALOG:
  249. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  250. InitPickIconDlg(hDlg, (LPPICKICON_DATA)lParam);
  251. break;
  252. case WM_COMMAND:
  253. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  254. case IDD_BROWSE:
  255. if (BrowseForIconFile(lppid))
  256. PutIconsInList(lppid);
  257. break;
  258. case IDD_PATH:
  259. if (NameChange(lppid))
  260. SendDlgItemMessage(hDlg, IDD_ICON, LB_SETCURSEL, (WPARAM)-1, 0);
  261. break;
  262. case IDD_ICON:
  263. if (NameChange(lppid)) {
  264. PutIconsInList(lppid);
  265. break;
  266. }
  267. if (GET_WM_COMMAND_CMD(wParam, lParam) != LBN_DBLCLK)
  268. break;
  269. /*** FALL THRU on double click ***/
  270. case IDOK:
  271. if (NameChange(lppid)) {
  272. PutIconsInList(lppid);
  273. } else {
  274. int iIconIndex = (int)SendDlgItemMessage(hDlg, IDD_ICON, LB_GETCURSEL, 0, 0L);
  275. if (iIconIndex < 0)
  276. iIconIndex = 0;
  277. lppid->iIconIndex = iIconIndex;
  278. lstrcpyn(lppid->pszIconPath, lppid->szPathField, lppid->cbIconPath);
  279. EndDialog(hDlg, S_OK);
  280. }
  281. break;
  282. case IDCANCEL:
  283. EndDialog(hDlg, HRESULT_FROM_WIN32(ERROR_CANCELLED));
  284. break;
  285. case IDD_RESTORE:
  286. EndDialog(hDlg, S_FALSE);
  287. break;
  288. default:
  289. return(FALSE);
  290. }
  291. break;
  292. // owner draw messages for icon listbox
  293. case WM_DRAWITEM:
  294. #define lpdi ((DRAWITEMSTRUCT *)lParam)
  295. if (lpdi->itemState & ODS_SELECTED)
  296. SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
  297. else
  298. SetBkColor(lpdi->hDC, GetSysColor(COLOR_WINDOW));
  299. /* repaint the selection state */
  300. ExtTextOut(lpdi->hDC, 0, 0, ETO_OPAQUE, &lpdi->rcItem, NULL, 0, NULL);
  301. dwOldLayout = GET_DC_LAYOUT(lpdi->hDC);
  302. if (g_bMirroredOS && dwOldLayout)
  303. {
  304. SET_DC_LAYOUT(lpdi->hDC, dwOldLayout | LAYOUT_PRESERVEBITMAP);
  305. }
  306. /* draw the icon */
  307. if ((int)lpdi->itemID >= 0)
  308. DrawIcon(lpdi->hDC, (lpdi->rcItem.left + lpdi->rcItem.right - GetSystemMetrics(SM_CXICON)) / 2,
  309. (lpdi->rcItem.bottom + lpdi->rcItem.top - GetSystemMetrics(SM_CYICON)) / 2, (HICON)lpdi->itemData);
  310. if (dwOldLayout)
  311. {
  312. SET_DC_LAYOUT(lpdi->hDC, dwOldLayout);
  313. }
  314. // InflateRect(&lpdi->rcItem, -1, -1);
  315. /* if it has the focus, draw the focus */
  316. if (lpdi->itemState & ODS_FOCUS)
  317. DrawFocusRect(lpdi->hDC, &lpdi->rcItem);
  318. #undef lpdi
  319. break;
  320. case WM_MEASUREITEM:
  321. #define lpmi ((MEASUREITEMSTRUCT *)lParam)
  322. lpmi->itemWidth = GetSystemMetrics(SM_CXICON) + CX_BORDER;
  323. lpmi->itemHeight = GetSystemMetrics(SM_CYICON) + CY_BORDER;
  324. #undef lpmi
  325. break;
  326. case WM_DELETEITEM:
  327. #define lpdi ((DELETEITEMSTRUCT *)lParam)
  328. DestroyIcon((HICON)lpdi->itemData);
  329. #undef lpdi
  330. break;
  331. case WM_HELP:
  332. WinHelp(((LPHELPINFO) lParam)->hItemHandle, NULL,
  333. HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aPickIconHelpIDs);
  334. break;
  335. case WM_CONTEXTMENU:
  336. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  337. (ULONG_PTR)(LPVOID)aPickIconHelpIDs);
  338. break;
  339. default:
  340. return FALSE;
  341. }
  342. return TRUE;
  343. }
  344. // puts up the pick icon dialog
  345. STDAPI_(int) PickIconDlg(HWND hwnd, IN OUT LPTSTR pszIconPath, UINT cbIconPath, int *piIconIndex)
  346. {
  347. return SUCCEEDED(PickIconDlgWithTitle(hwnd, NULL, FALSE, pszIconPath, cbIconPath, piIconIndex));
  348. }
  349. // puts up the pick icon dialog with a customized Title for the Dialog Window.
  350. STDAPI PickIconDlgWithTitle(HWND hwnd, LPCTSTR pszTitle, BOOL bShowRestoreButton, IN OUT LPTSTR pszIconPath, UINT cbIconPath, int *piIconIndex)
  351. {
  352. RIPMSG(pszIconPath && IS_VALID_WRITE_BUFFER(pszIconPath, TCHAR, cbIconPath), "PickIconDlgWithTitle: caller passed bad pszIconPath");
  353. RIPMSG(piIconIndex != NULL, "PickIconDlgWithTitle: caller passed bad piIconIndex");
  354. if (pszIconPath && piIconIndex)
  355. {
  356. PICKICON_DATA *pid = (PICKICON_DATA *)LocalAlloc(LPTR, sizeof(PICKICON_DATA));
  357. if (pid)
  358. {
  359. HRESULT res;
  360. pid->pszDialogTitle = pszTitle;
  361. pid->bShowRestoreButton = bShowRestoreButton;
  362. pid->pszIconPath = pszIconPath;
  363. pid->cbIconPath = cbIconPath;
  364. pid->iIconIndex = *piIconIndex;
  365. res = (HRESULT)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_PICKICON), hwnd, PickIconDlgProc, (LPARAM)pid);
  366. *piIconIndex = pid->iIconIndex;
  367. LocalFree(pid);
  368. return res;
  369. }
  370. *piIconIndex = 0;
  371. *pszIconPath = 0;
  372. return HRESULT_FROM_WIN32(ERROR_CANCELLED);
  373. }
  374. return E_INVALIDARG;
  375. }