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.

425 lines
12 KiB

  1. /*
  2. * pickicon - Icon picker
  3. */
  4. #include "stdafx.h"
  5. #include "pickicon.h"
  6. #include "windowsx.h"
  7. #include "commdlg.h"
  8. #include "resource.h"
  9. #include "util.h"
  10. #define cA(a) (sizeof(a)/sizeof(a[0]))
  11. typedef TCHAR TCH;
  12. typedef struct COFN { /* common open file name */
  13. OPENFILENAME ofn; /* The thing COMMDLG wants */
  14. TCH tsz[MAX_PATH]; /* Where we build the name */
  15. TCH tszFilter[100]; /* File open/save filter */
  16. } COFN, *PCOFN;
  17. /*****************************************************************************
  18. *
  19. * InitOpenFileName
  20. *
  21. * Initialize a COFN structure.
  22. *
  23. *****************************************************************************/
  24. void PASCAL
  25. InitOpenFileName(HWND hwnd, PCOFN pcofn, UINT ids, LPCTSTR pszInit)
  26. {
  27. int itchMax;
  28. TCH tch;
  29. ZeroMemory(&pcofn->ofn, sizeof(pcofn->ofn));
  30. pcofn->ofn.lStructSize |= sizeof(pcofn->ofn);
  31. pcofn->ofn.hwndOwner = hwnd;
  32. pcofn->ofn.lpstrFilter = pcofn->tszFilter;
  33. pcofn->ofn.lpstrFile = pcofn->tsz;
  34. pcofn->ofn.nMaxFile = MAX_PATH;
  35. pcofn->ofn.Flags |= (OFN_HIDEREADONLY | OFN_NOCHANGEDIR);
  36. /* Get the filter string */
  37. itchMax = LoadString(SC::GetHinst(), ids, pcofn->tszFilter, cA(pcofn->tszFilter));
  38. if (itchMax) {
  39. /* Marker character must not be DBCS */
  40. tch = pcofn->tszFilter[itchMax-1];
  41. LPTSTR ptsz = pcofn->tszFilter;
  42. while (ptsz < &pcofn->tszFilter[itchMax]) {
  43. if (*ptsz == tch) *ptsz++ = '\0';
  44. else ptsz = CharNext(ptsz);
  45. }
  46. }
  47. /* Set the initial value */
  48. lstrcpyn(pcofn->tsz, pszInit, cA(pcofn->tsz));
  49. }
  50. /*
  51. * Instance info for the dialog.
  52. */
  53. typedef struct PIDI { /* PickIcon dialog instance */
  54. LPTSTR ptszIconPath; /* Which file? */
  55. UINT ctchIconPath;
  56. int iIconIndex; /* Which icon number? */
  57. int *piIconIndex;
  58. TCH tszCurFile[MAX_PATH]; /* The path in the list box */
  59. } PIDI, *PPIDI;
  60. #define cxIcon GetSystemMetrics(SM_CXICON)
  61. #define cyIcon GetSystemMetrics(SM_CYICON)
  62. /*****************************************************************************
  63. *
  64. * PickIcon_ppidiHdlg
  65. *
  66. * Extract the PPIDI from an hdlg.
  67. *
  68. *****************************************************************************/
  69. #define PickIcon_ppidiHdlg(hdlg) ((PPIDI)GetWindowLongPtr(hdlg, DWLP_USER))
  70. /*****************************************************************************
  71. *
  72. * PickIcon_OnMeasureItem
  73. *
  74. * Tell USER the size of each item.
  75. *
  76. *****************************************************************************/
  77. void PASCAL
  78. PickIcon_OnMeasureItem(HWND hdlg, LPMEASUREITEMSTRUCT lpmi, PPIDI ppidi)
  79. {
  80. lpmi->itemWidth = cxIcon + 12;
  81. lpmi->itemHeight = cyIcon + 4;
  82. }
  83. /*****************************************************************************
  84. *
  85. * PickIcon_OnDrawItem
  86. *
  87. * Draw an icon.
  88. *
  89. *****************************************************************************/
  90. void PASCAL
  91. PickIcon_OnDrawItem(HWND hdlg, LPDRAWITEMSTRUCT lpdi, PPIDI ppidi)
  92. {
  93. SetBkColor(lpdi->hDC, GetSysColor((lpdi->itemState & ODS_SELECTED) ?
  94. COLOR_HIGHLIGHT : COLOR_WINDOW));
  95. /* repaint the selection state */
  96. ExtTextOut(lpdi->hDC, 0, 0, ETO_OPAQUE, &lpdi->rcItem, NULL, 0, NULL);
  97. /*
  98. * Preserve icon shape when BitBlitting it to a
  99. * mirrored DC.
  100. */
  101. DWORD dwLayout=0L;
  102. if ((dwLayout=GetLayout(lpdi->hDC)) & LAYOUT_RTL)
  103. {
  104. SetLayout(lpdi->hDC, dwLayout|LAYOUT_BITMAPORIENTATIONPRESERVED);
  105. }
  106. /* draw the icon centered in the rectangle */
  107. if ((int)lpdi->itemID >= 0) {
  108. DrawIcon(lpdi->hDC,
  109. (lpdi->rcItem.left + lpdi->rcItem.right - cxIcon) / 2,
  110. (lpdi->rcItem.bottom + lpdi->rcItem.top - cyIcon) / 2,
  111. (HICON)lpdi->itemData);
  112. }
  113. /*
  114. * Restore the DC to its previous layout state.
  115. */
  116. if (dwLayout & LAYOUT_RTL)
  117. {
  118. SetLayout(lpdi->hDC, dwLayout);
  119. }
  120. /* if it has the focus, draw the focus */
  121. if (lpdi->itemState & ODS_FOCUS) {
  122. DrawFocusRect(lpdi->hDC, &lpdi->rcItem);
  123. }
  124. }
  125. /*****************************************************************************
  126. *
  127. * PickIcon_OnDeleteItem
  128. *
  129. * USER is nuking an item. Clean it up.
  130. *
  131. *****************************************************************************/
  132. #define PickIcon_OnDeleteItem(hdlg, lpdi, ppidi) \
  133. DestroyIcon((HICON)(lpdi)->itemData)
  134. /*****************************************************************************
  135. *
  136. * PickIcon_FillIconList
  137. *
  138. * Fill in all the icons. If the user picks a bad place, we leave
  139. * garbage in the path (so he can edit the name) and leave the list
  140. * box blank.
  141. *
  142. *****************************************************************************/
  143. void PickIcon_FillIconList(HWND hdlg, PPIDI ppidi)
  144. {
  145. HCURSOR hcurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  146. HWND hwnd = GetDlgItem(hdlg, IDC_PICKICON);
  147. if (!IsWindow (hwnd))
  148. return;
  149. ListBox_SetColumnWidth(hwnd, cxIcon + 12);
  150. ListBox_ResetContent(hwnd);
  151. TCHAR szFile[cA(ppidi->tszCurFile)];
  152. GetDlgItemText(hdlg, IDC_PICKPATH, szFile, cA(szFile));
  153. // support indirect paths (e.g. %SystemRoot%\...")
  154. TCHAR szExpandedFile[cA(ppidi->tszCurFile)];
  155. ExpandEnvironmentStrings (szFile, szExpandedFile, cA(szExpandedFile));
  156. if (SearchPath(0, szExpandedFile, 0, cA(ppidi->tszCurFile),
  157. ppidi->tszCurFile, 0)) {
  158. ExtractIcons:
  159. int cIcons;
  160. cIcons = ExtractIconEx(ppidi->tszCurFile, 0, 0, 0, 0);
  161. if (cIcons) {
  162. HICON *rgIcons = (HICON *)LocalAlloc(LPTR, cIcons * sizeof(HICON));
  163. if (rgIcons) {
  164. cIcons = (int)ExtractIconEx(ppidi->tszCurFile, 0,
  165. rgIcons, NULL, cIcons);
  166. if (cIcons) {
  167. int iicon;
  168. SendMessage(hwnd, WM_SETREDRAW, 0, 0);
  169. for (iicon = 0; iicon < cIcons; iicon++) {
  170. ListBox_AddString(hwnd, rgIcons[iicon]);
  171. }
  172. if (ListBox_SetCurSel(hwnd, ppidi->iIconIndex) == LB_ERR) {
  173. ListBox_SetCurSel(hwnd, 0);
  174. }
  175. SendMessage(hwnd, WM_SETREDRAW, 1, 0);
  176. } else { /* Mysteriously unable to extract */
  177. }
  178. LocalFree((HLOCAL)rgIcons);
  179. } else { /* Not enough memory to load icons */
  180. }
  181. } else { /* No icons in the file */
  182. }
  183. // if indirect path was specified (e.g. "%SystemRoot%\..."), preserve indirection
  184. if ((lstrcmp (szExpandedFile, szFile) != 0) &&
  185. (lstrcmp (szExpandedFile, ppidi->tszCurFile) == 0))
  186. lstrcpy (ppidi->tszCurFile, szFile);
  187. SetDlgItemText(hdlg, IDC_PICKPATH, ppidi->tszCurFile);
  188. } else { /* File not found */
  189. SC sc;
  190. MMCErrorBox (sc.FromWin32(ERROR_FILE_NOT_FOUND));
  191. goto ExtractIcons;
  192. }
  193. InvalidateRect(hwnd, 0, 1);
  194. SetCursor(hcurOld);
  195. }
  196. /*****************************************************************************
  197. *
  198. * PickIcon_OnInitDialog
  199. *
  200. * Dialog init. Populate the list box with what we came in with.
  201. *
  202. *****************************************************************************/
  203. void PASCAL
  204. PickIcon_OnInitDialog(HWND hdlg, PPIDI ppidi)
  205. {
  206. SetWindowLongPtr(hdlg, DWLP_USER, (LPARAM)ppidi);
  207. SetDlgItemText(hdlg, IDC_PICKPATH,
  208. lstrcpyn(ppidi->tszCurFile,
  209. ppidi->ptszIconPath, cA(ppidi->tszCurFile)));
  210. SendDlgItemMessage(hdlg, IDC_PICKPATH, EM_LIMITTEXT,
  211. ppidi->ctchIconPath, 0);
  212. PickIcon_FillIconList(hdlg, ppidi);
  213. }
  214. /*****************************************************************************
  215. *
  216. * PickIcon_OnBrowse
  217. *
  218. *****************************************************************************/
  219. void PASCAL
  220. PickIcon_OnBrowse(HWND hdlg, PPIDI ppidi)
  221. {
  222. DWORD dw;
  223. COFN cofn;
  224. InitOpenFileName(hdlg, &cofn, IDS_ICONFILES, ppidi->tszCurFile);
  225. dw = GetFileAttributes(ppidi->tszCurFile);
  226. if (dw == 0xFFFFFFFF || (dw & FILE_ATTRIBUTE_DIRECTORY)) {
  227. cofn.tsz[0] = '\0';
  228. }
  229. if (GetOpenFileName(&cofn.ofn)) {
  230. SetDlgItemText(hdlg, IDC_PICKPATH, cofn.tsz);
  231. SendMessage(hdlg, DM_SETDEFID, IDOK, 0);
  232. PickIcon_FillIconList(hdlg, ppidi);
  233. }
  234. }
  235. /*****************************************************************************
  236. *
  237. * PickIcon_NameChange
  238. *
  239. * Determine whether the thing in the edit control doesn't match the
  240. * thing whose icons we are showing.
  241. *
  242. *****************************************************************************/
  243. BOOL PASCAL
  244. PickIcon_NameChange(HWND hdlg, PPIDI ppidi)
  245. {
  246. TCH tszBuffer[MAX_PATH];
  247. GetDlgItemText(hdlg, IDC_PICKPATH, tszBuffer, cA(tszBuffer));
  248. return lstrcmpi(tszBuffer, ppidi->tszCurFile);
  249. }
  250. /*****************************************************************************
  251. *
  252. * PickIcon_OnOk
  253. *
  254. * If the name has changed, treat this as a "Okay, now reload
  255. * the icons" rather than "Okay, I'm finished".
  256. *
  257. *****************************************************************************/
  258. void PASCAL
  259. PickIcon_OnOk(HWND hdlg, PPIDI ppidi)
  260. {
  261. if (PickIcon_NameChange(hdlg, ppidi)) {
  262. PickIcon_FillIconList(hdlg, ppidi);
  263. } else {
  264. int iIconIndex = (int)SendDlgItemMessage(hdlg, IDC_PICKICON,
  265. LB_GETCURSEL, 0, 0L);
  266. if (iIconIndex >= 0) { /* We have an icon */
  267. *ppidi->piIconIndex = iIconIndex;
  268. lstrcpyn(ppidi->ptszIconPath, ppidi->tszCurFile,
  269. ppidi->ctchIconPath);
  270. EndDialog(hdlg, 1);
  271. } else { /* No icon, act like cancel */
  272. EndDialog(hdlg, 0);
  273. }
  274. }
  275. }
  276. /*****************************************************************************
  277. *
  278. * PickIcon_OnCommand
  279. *
  280. *****************************************************************************/
  281. void PASCAL
  282. PickIcon_OnCommand(HWND hdlg, int id, UINT codeNotify, PPIDI ppidi)
  283. {
  284. switch (id) {
  285. case IDOK: PickIcon_OnOk(hdlg, ppidi); break;
  286. case IDCANCEL: EndDialog(hdlg, 0); break;
  287. case IDC_PICKBROWSE: PickIcon_OnBrowse(hdlg, ppidi); break;
  288. /*
  289. * When the name changes, remove the selection highlight.
  290. */
  291. case IDC_PICKPATH:
  292. if (PickIcon_NameChange(hdlg, ppidi)) {
  293. SendDlgItemMessage(hdlg, IDC_PICKICON, LB_SETCURSEL, (WPARAM)-1, 0);
  294. }
  295. break;
  296. case IDC_PICKICON:
  297. if (codeNotify == LBN_DBLCLK) {
  298. PickIcon_OnOk(hdlg, ppidi);
  299. }
  300. break;
  301. }
  302. }
  303. /*****************************************************************************
  304. *
  305. * PickIcon_DlgProc
  306. *
  307. * Dialog procedure.
  308. *
  309. *****************************************************************************/
  310. /*
  311. * The HANDLE_WM_* macros weren't designed to be used from a dialog
  312. * proc, so we need to handle the messages manually. (But carefully.)
  313. */
  314. INT_PTR EXPORT
  315. PickIcon_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
  316. {
  317. PPIDI ppidi = PickIcon_ppidiHdlg(hdlg);
  318. switch (wm) {
  319. case WM_INITDIALOG: PickIcon_OnInitDialog(hdlg, (PPIDI)lParam); break;
  320. case WM_COMMAND:
  321. PickIcon_OnCommand(hdlg, (int)GET_WM_COMMAND_ID(wParam, lParam),
  322. (UINT)GET_WM_COMMAND_CMD(wParam, lParam),
  323. ppidi);
  324. break;
  325. case WM_DRAWITEM:
  326. PickIcon_OnDrawItem(hdlg, (LPDRAWITEMSTRUCT)lParam, ppidi);
  327. break;
  328. case WM_MEASUREITEM:
  329. PickIcon_OnMeasureItem(hdlg, (LPMEASUREITEMSTRUCT)lParam, ppidi);
  330. break;
  331. case WM_DELETEITEM:
  332. PickIcon_OnDeleteItem(hdlg, (LPDELETEITEMSTRUCT)lParam, ppidi);
  333. break;
  334. default: return 0; /* Unhandled */
  335. }
  336. return 1; /* Handled */
  337. }
  338. /*****************************************************************************
  339. *
  340. * PickIconDlg
  341. *
  342. * Ask the user to pick an icon.
  343. *
  344. * hwnd - owner window
  345. * ptszIconPath - (in) default icon file
  346. * (out) chosen icon file
  347. * ctchIconPath - size of ptszIconPath buffer
  348. * piIconIndex - (in) default icon index
  349. * (out) index of chosen icon
  350. *
  351. * If the dialog box is cancelled, then no values are changed.
  352. *
  353. *****************************************************************************/
  354. MMCBASE_API INT_PTR PASCAL
  355. PickIconDlg(HWND hwnd, LPTSTR ptszIconPath, UINT ctchIconPath, int *piIconIndex)
  356. {
  357. PIDI pidi;
  358. pidi.ptszIconPath = ptszIconPath;
  359. pidi.ctchIconPath = ctchIconPath;
  360. pidi.piIconIndex = piIconIndex;
  361. pidi.iIconIndex = *piIconIndex;
  362. return DialogBoxParam(SC::GetHinst(), MAKEINTRESOURCE(IDD_PICKICON), hwnd,
  363. PickIcon_DlgProc, (LPARAM)&pidi);
  364. }