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.

507 lines
15 KiB

  1. #include "shellprv.h"
  2. #include "shcombox.h"
  3. #include "filetype.h"
  4. #include "recdocs.h"
  5. #include "ids.h"
  6. // Adds the specified item to a comboboxex window
  7. HRESULT AddCbxItemToComboBox(HWND hwndComboEx, PCCBXITEM pItem, INT_PTR *pnPosAdded)
  8. {
  9. ASSERT(hwndComboEx);
  10. // Convert to COMBOBOXEXITEM.
  11. COMBOBOXEXITEM cei;
  12. cei.mask = pItem->mask;
  13. cei.iItem = pItem->iItem;
  14. cei.pszText = (LPTSTR)pItem->szText;
  15. cei.cchTextMax = ARRAYSIZE(pItem->szText);
  16. cei.iImage = pItem->iImage;
  17. cei.iSelectedImage = pItem->iSelectedImage;
  18. cei.iOverlay = pItem->iOverlay;
  19. cei.iIndent = pItem->iIndent;
  20. cei.lParam = pItem->lParam;
  21. int nPos = (int)::SendMessage(hwndComboEx, CBEM_INSERTITEM, 0, (LPARAM)&cei);
  22. *pnPosAdded = nPos;
  23. return nPos < 0 ? E_FAIL : S_OK;
  24. }
  25. // Adds the specified item to a comboboxex window, and invokes
  26. // a notification callback function if successful.
  27. HRESULT AddCbxItemToComboBoxCallback(IN HWND hwndComboEx, IN PCBXITEM pItem, IN ADDCBXITEMCALLBACK pfn, IN LPARAM lParam)
  28. {
  29. INT_PTR iPos = -1;
  30. if (pfn && E_ABORT == pfn(CBXCB_ADDING, pItem, lParam))
  31. return E_ABORT;
  32. HRESULT hr = AddCbxItemToComboBox(hwndComboEx, pItem, &iPos);
  33. if (pfn && S_OK == hr)
  34. {
  35. ((CBXITEM*)pItem)->iItem = iPos;
  36. pfn(CBXCB_ADDED, pItem, lParam);
  37. }
  38. return hr;
  39. }
  40. // image list indices known
  41. void MakeCbxItemKnownImage(CBXITEM* pcbi, LPCTSTR pszDisplayName, void *pvData,
  42. int iImage, int iSelectedImage, INT_PTR nPos, int iIndent)
  43. {
  44. ZeroMemory(pcbi, sizeof(*pcbi));
  45. lstrcpyn(pcbi->szText, pszDisplayName, ARRAYSIZE(pcbi->szText));
  46. pcbi->lParam = (LPARAM)pvData;
  47. pcbi->iIndent = iIndent;
  48. pcbi->iItem = nPos;
  49. pcbi->mask = (CBEIF_TEXT | CBEIF_INDENT | CBEIF_LPARAM);
  50. if (-1 != iImage)
  51. {
  52. pcbi->mask |= CBEIF_IMAGE;
  53. pcbi->iImage = iImage;
  54. }
  55. if (-1 != iSelectedImage)
  56. {
  57. pcbi->mask |= CBEIF_SELECTEDIMAGE;
  58. pcbi->iSelectedImage = iSelectedImage;
  59. }
  60. }
  61. // Retrieves the system image list indices for the specified ITEMIDLIST
  62. HRESULT _GetPidlIcon(LPCITEMIDLIST pidl, int *piImage, int *piSelectedImage)
  63. {
  64. IShellFolder *psfParent;
  65. LPCITEMIDLIST pidlChild;
  66. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild);
  67. if (SUCCEEDED(hr))
  68. {
  69. *piImage = SHMapPIDLToSystemImageListIndex(psfParent, pidlChild, NULL);
  70. *piSelectedImage = *piImage;
  71. psfParent->Release();
  72. }
  73. return hr;
  74. }
  75. // image icon image list indices unknown
  76. STDAPI_(void) MakeCbxItem(CBXITEM* pcbi, LPCTSTR pszDisplayName, void *pvData, LPCITEMIDLIST pidlIcon, INT_PTR nPos, int iIndent)
  77. {
  78. int iImage = -1;
  79. int iSelectedImage = -1;
  80. if (pidlIcon)
  81. _GetPidlIcon(pidlIcon, &iImage, &iSelectedImage);
  82. MakeCbxItemKnownImage(pcbi, pszDisplayName, pvData, iImage, iSelectedImage, nPos, iIndent);
  83. }
  84. HRESULT _MakeFileTypeCbxItem(
  85. OUT CBXITEM* pcbi,
  86. IN LPCTSTR pszDisplayName,
  87. IN LPCTSTR pszExt,
  88. IN LPCITEMIDLIST pidlIcon,
  89. IN INT_PTR nPos,
  90. IN int iIndent)
  91. {
  92. HRESULT hr = E_OUTOFMEMORY;
  93. void *pvData = NULL;
  94. LPITEMIDLIST pidlToFree = NULL;
  95. if (!pidlIcon)
  96. {
  97. TCHAR szFileName[MAX_PATH] = TEXT("C:\\notexist"); // This is bogus and that's ok
  98. StrCatBuff(szFileName, pszExt, ARRAYSIZE(szFileName));
  99. pidlIcon = pidlToFree = SHSimpleIDListFromPath(szFileName);
  100. }
  101. if (pidlIcon && Str_SetPtr((LPTSTR *)&pvData, pszExt))
  102. {
  103. MakeCbxItem(pcbi, pszDisplayName, pvData, pidlIcon, nPos, iIndent);
  104. hr = S_OK;
  105. }
  106. ILFree(pidlToFree); // may be NULL
  107. return hr;
  108. }
  109. // Enumerates children of the indicated special shell item id.
  110. HRESULT EnumSpecialItemIDs(int csidl, DWORD dwSHCONTF, LPFNPIDLENUM_CB pfn, void *pvData)
  111. {
  112. LPITEMIDLIST pidlFolder;
  113. if (SUCCEEDED(SHGetFolderLocation(NULL, csidl, NULL, 0, &pidlFolder)))
  114. {
  115. IShellFolder *psf;
  116. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlFolder, &psf))))
  117. {
  118. IEnumIDList * penum;
  119. if (S_OK == psf->EnumObjects(NULL, dwSHCONTF, &penum))
  120. {
  121. LPITEMIDLIST pidl;
  122. BOOL bContinue = TRUE;
  123. while (bContinue && (S_OK == penum->Next(1, &pidl, NULL)))
  124. {
  125. LPITEMIDLIST pidlFull = ILCombine(pidlFolder, pidl);
  126. if (pidlFull)
  127. {
  128. if (FAILED(pfn(pidlFull, pvData)))
  129. bContinue = FALSE;
  130. ILFree(pidlFull);
  131. }
  132. ILFree(pidl);
  133. }
  134. penum->Release();
  135. }
  136. psf->Release();
  137. }
  138. ILFree(pidlFolder);
  139. }
  140. return S_OK;
  141. }
  142. STDAPI_(HIMAGELIST) GetSystemImageListSmallIcons()
  143. {
  144. HIMAGELIST himlSmall;
  145. Shell_GetImageLists(NULL, &himlSmall);
  146. return himlSmall;
  147. }
  148. HRESULT _MakeLocalDrivesCbxItem(CBXITEM* pItem, LPCITEMIDLIST pidl)
  149. {
  150. TCHAR szPath[MAX_PATH];
  151. HRESULT hr = S_FALSE;
  152. ULONG ulAttrs = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_NONENUMERATED;
  153. if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, SIZECHARS(szPath), &ulAttrs)) &&
  154. ((SFGAO_FOLDER | SFGAO_FILESYSTEM) == (ulAttrs & (SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_NONENUMERATED))) &&
  155. (GetDriveType(szPath) == DRIVE_FIXED))
  156. {
  157. TCHAR szDisplayName[MAX_PATH];
  158. SHGetNameAndFlags(pidl, SHGDN_NORMAL, szDisplayName, SIZECHARS(szDisplayName), NULL);
  159. LPTSTR pszPath = NULL;
  160. Str_SetPtr(&pszPath, szPath);
  161. MakeCbxItem(pItem, szDisplayName, (void *)pszPath, pidl, LISTINSERT_LAST, NO_ITEM_INDENT);
  162. hr = S_OK;
  163. }
  164. return hr;
  165. }
  166. typedef struct
  167. {
  168. HWND hwndComboBox;
  169. ADDCBXITEMCALLBACK pfn;
  170. LPARAM lParam;
  171. } ENUMITEMPARAM;
  172. HRESULT _PopulateLocalDrivesCB(LPCITEMIDLIST pidl, void *pv)
  173. {
  174. CBXITEM item;
  175. HRESULT hr = _MakeLocalDrivesCbxItem(&item, pidl);
  176. if (hr == S_OK)
  177. {
  178. ENUMITEMPARAM *peip = (ENUMITEMPARAM *) pv;
  179. item.iID = CSIDL_DRIVES;
  180. hr = AddCbxItemToComboBoxCallback(peip->hwndComboBox, &item, peip->pfn, peip->lParam);
  181. }
  182. return hr;
  183. }
  184. STDAPI PopulateLocalDrivesCombo(HWND hwndComboBoxEx, ADDCBXITEMCALLBACK pfn, LPARAM lParam)
  185. {
  186. ENUMITEMPARAM eip;
  187. eip.hwndComboBox = hwndComboBoxEx;
  188. eip.pfn = pfn;
  189. eip.lParam = lParam;
  190. ::SendMessage(hwndComboBoxEx, CB_RESETCONTENT, 0, 0);
  191. return EnumSpecialItemIDs(CSIDL_DRIVES, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, _PopulateLocalDrivesCB, &eip);
  192. }
  193. // File Associations selector combo methods
  194. HRESULT _AddFileType(IN HWND hwndComboBox, IN LPCTSTR pszDisplayName, IN LPCTSTR pszExt, IN LPCITEMIDLIST pidlIcon, IN int iIndent,
  195. IN OPTIONAL ADDCBXITEMCALLBACK pfn, IN OPTIONAL LPARAM lParam);
  196. HRESULT _AddFileTypes(HWND hwndComboBox, ADDCBXITEMCALLBACK pfn, LPARAM lParam)
  197. {
  198. HRESULT hr = S_OK;
  199. DWORD dwSubKey = 0;
  200. TCHAR szExtension[MAX_PATH]; // string containing the classes key
  201. DWORD dwExtension;
  202. BOOL bFoundFirstExt = FALSE;
  203. // Enumerate extensions from registry to get file types
  204. dwExtension = ARRAYSIZE(szExtension);
  205. while (hr != E_ABORT && SHEnumKeyEx(HKEY_CLASSES_ROOT, dwSubKey, szExtension, &dwExtension) != ERROR_NO_MORE_ITEMS)
  206. {
  207. if (*szExtension == TEXT('.')) // find the file type identifier and description from the extension
  208. {
  209. IQueryAssociations *pqa;
  210. if (SUCCEEDED(AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa))))
  211. {
  212. if (SUCCEEDED(pqa->Init(0, szExtension, NULL, NULL)))
  213. {
  214. TCHAR szDesc[MAX_PATH];
  215. DWORD dwAttributes = 0;
  216. DWORD dwSize = sizeof(dwAttributes);
  217. BOOL fAdd;
  218. if (SUCCEEDED(pqa->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwAttributes, &dwSize)))
  219. {
  220. fAdd = !(dwAttributes & FTA_Exclude);
  221. }
  222. else
  223. {
  224. dwSize = MAX_PATH;
  225. fAdd = SUCCEEDED(pqa->GetString(NULL, ASSOCSTR_DEFAULTICON, NULL, NULL, &dwSize));
  226. }
  227. if (fAdd)
  228. {
  229. dwSize = ARRAYSIZE(szDesc);
  230. pqa->GetString(NULL, ASSOCSTR_FRIENDLYDOCNAME, NULL, szDesc, &dwSize);
  231. hr = _AddFileType(hwndComboBox, szDesc, szExtension, NULL, NO_ITEM_INDENT, pfn, lParam);
  232. }
  233. }
  234. pqa->Release();
  235. }
  236. bFoundFirstExt = TRUE;
  237. }
  238. else if (bFoundFirstExt) // stop after first non-ext key (if sorted registry)
  239. break;
  240. dwSubKey++;
  241. dwExtension = ARRAYSIZE(szExtension);
  242. }
  243. if (hr != E_ABORT && LoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, szExtension, ARRAYSIZE(szExtension)))
  244. {
  245. LPITEMIDLIST pidlIcon = SHCloneSpecialIDList(NULL, CSIDL_RECENT, FALSE);
  246. if (pidlIcon)
  247. {
  248. hr = _AddFileType(hwndComboBox, szExtension, TEXT("."), pidlIcon, NO_ITEM_INDENT, pfn, lParam);
  249. ILFree(pidlIcon);
  250. }
  251. }
  252. return hr;
  253. }
  254. HRESULT _AddFileType(HWND hwndComboBox, LPCTSTR pszDisplayName, LPCTSTR pszExt, LPCITEMIDLIST pidlIcon, int iIndent, ADDCBXITEMCALLBACK pfn, LPARAM lParam)
  255. {
  256. HRESULT hr = S_OK;
  257. BOOL bExists = FALSE;
  258. LRESULT lRet = ::SendMessage(hwndComboBox, CB_FINDSTRINGEXACT, 0, (LPARAM) pszDisplayName);
  259. LRESULT nIndex = lRet;
  260. // Is the string already in the list?
  261. if (CB_ERR != nIndex)
  262. {
  263. // Yes, so we want to combine our extension with the current extension or extension list
  264. // and erase the old one. Then we can continue to add it below.
  265. LPTSTR pszOldExt = NULL;
  266. lRet = SendMessage(hwndComboBox, CB_GETITEMDATA, nIndex, 0);
  267. if (!(0 == lRet || CB_ERR == lRet))
  268. {
  269. pszOldExt = (LPTSTR)lRet;
  270. UINT cchLen = lstrlen(pszOldExt) + 1 + lstrlen(pszExt) + 1;
  271. LPTSTR pszNewExt = (LPTSTR)LocalReAlloc(pszOldExt, sizeof(TCHAR) * cchLen, LMEM_ZEROINIT | LMEM_MOVEABLE);
  272. if (pszNewExt)
  273. {
  274. StrCat(pszNewExt, TEXT(";"));
  275. StrCat(pszNewExt, pszExt);
  276. lRet = ::SendMessage(hwndComboBox, CB_SETITEMDATA, (WPARAM)nIndex, (LPARAM)pszNewExt);
  277. }
  278. bExists = TRUE;
  279. }
  280. }
  281. if (!bExists)
  282. {
  283. // No, so we can add it.
  284. TCHAR szString[MAX_URL_STRING];
  285. INT_PTR nPos = 0;
  286. INT_PTR nLast = CB_ERR;
  287. lRet = ::SendMessage(hwndComboBox, CB_GETCOUNT, 0, 0);
  288. if (lRet == CB_ERR)
  289. return E_FAIL;
  290. nLast = lRet - 1;
  291. *szString = 0;
  292. lRet = ::SendMessage(hwndComboBox, CB_GETLBTEXT, (WPARAM)nLast, (LPARAM)szString);
  293. if (lRet == CB_ERR)
  294. return E_FAIL;
  295. // Base case, does his the new string need to be inserted into the end?
  296. if ((-1 == nLast) || (0 > StrCmp(szString, pszDisplayName)))
  297. {
  298. // Yes, so add it to the end.
  299. CBXITEM item;
  300. hr = _MakeFileTypeCbxItem(&item, pszDisplayName, pszExt, pidlIcon, (nLast + 1), iIndent);
  301. if (SUCCEEDED(hr))
  302. hr = AddCbxItemToComboBoxCallback(hwndComboBox, &item, pfn, lParam);
  303. }
  304. else
  305. {
  306. #ifdef DEBUG
  307. INT_PTR nCycleDetector = nLast + 5;
  308. #endif // DEBUG
  309. BOOL bDisplayName = TRUE;
  310. do
  311. {
  312. // Determine ordered insertion point:
  313. INT_PTR nTest = nPos + ((nLast - nPos) / 2);
  314. bDisplayName = CB_ERR != ::SendMessage(hwndComboBox, CB_GETLBTEXT, (WPARAM)nTest, (LPARAM)szString);
  315. if (bDisplayName)
  316. {
  317. // Does the string need to before nTest?
  318. if (0 > StrCmp(pszDisplayName, szString))
  319. nLast = nTest; // Yes
  320. else
  321. {
  322. if (nPos == nTest)
  323. nPos++;
  324. else
  325. nPos = nTest; // No
  326. }
  327. #ifdef DEBUG
  328. ASSERT(nCycleDetector); // Make sure we converge.
  329. nCycleDetector--;
  330. #endif // DEBUG
  331. }
  332. } while (bDisplayName && nLast - nPos);
  333. if (bDisplayName)
  334. {
  335. CBXITEM item;
  336. hr = _MakeFileTypeCbxItem(&item, pszDisplayName, pszExt, pidlIcon, nPos, iIndent);
  337. if (SUCCEEDED(hr))
  338. hr = AddCbxItemToComboBoxCallback(hwndComboBox, &item, pfn, lParam);
  339. }
  340. }
  341. }
  342. return hr;
  343. }
  344. STDAPI PopulateFileAssocCombo(HWND hwndComboBoxEx, ADDCBXITEMCALLBACK pfn, LPARAM lParam)
  345. {
  346. ASSERT(hwndComboBoxEx);
  347. ::SendMessage(hwndComboBoxEx, CB_RESETCONTENT, 0, 0);
  348. HRESULT hr = _AddFileTypes(hwndComboBoxEx, pfn, lParam);
  349. if (E_ABORT == hr)
  350. return hr;
  351. // Now add this to the top of the list.
  352. CBXITEM item;
  353. TCHAR szDisplayName[MAX_PATH];
  354. LoadString(HINST_THISDLL, IDS_SNS_ALL_FILE_TYPES, szDisplayName, ARRAYSIZE(szDisplayName));
  355. MakeCbxItem(&item, szDisplayName, (void *)FILEASSOCIATIONSID_ALLFILETYPES, NULL, LISTINSERT_FIRST, NO_ITEM_NOICON_INDENT);
  356. return AddCbxItemToComboBoxCallback(hwndComboBoxEx, &item, pfn, lParam);
  357. }
  358. void *_getFileAssocComboData(HWND hwndComboBox)
  359. {
  360. LRESULT nSelected = ::SendMessage(hwndComboBox, CB_GETCURSEL, 0, 0);
  361. if (-1 == nSelected)
  362. return NULL;
  363. LRESULT itemData = ::SendMessage(hwndComboBox, CB_GETITEMDATA, nSelected, 0);
  364. if (itemData == CB_ERR)
  365. itemData = NULL;
  366. return (LPVOID)itemData;
  367. }
  368. DWORD _getFileAssocComboID(HWND hwndComboBox)
  369. {
  370. DWORD dwID = 0;
  371. void *pvData = _getFileAssocComboData(hwndComboBox);
  372. // Is this an ID?
  373. if (pvData && ((DWORD_PTR)pvData <= FILEASSOCIATIONSID_MAX))
  374. {
  375. // Yes, so let's get it.
  376. dwID = PtrToUlong(pvData);
  377. }
  378. return dwID;
  379. }
  380. LONG GetFileAssocComboSelItemText(IN HWND hwndComboBox, OUT LPTSTR *ppszText)
  381. {
  382. ASSERT(hwndComboBox);
  383. ASSERT(ppszText);
  384. *ppszText = NULL;
  385. int nSel = (LONG)::SendMessage(hwndComboBox, CB_GETCURSEL, 0, 0);
  386. if (nSel >= 0)
  387. {
  388. DWORD dwID = _getFileAssocComboID(hwndComboBox);
  389. if (dwID > FILEASSOCIATIONSID_FILE_PATH)
  390. {
  391. *ppszText = StrDup(TEXT(".*"));
  392. }
  393. else
  394. {
  395. LPTSTR pszText = (LPTSTR)_getFileAssocComboData(hwndComboBox);
  396. if (pszText)
  397. {
  398. *ppszText = StrDup((LPCTSTR)pszText);
  399. }
  400. }
  401. }
  402. if (!*ppszText)
  403. {
  404. nSel = -1;
  405. }
  406. return nSel;
  407. }
  408. LRESULT DeleteFileAssocComboItem(IN LPNMHDR pnmh)
  409. {
  410. PNMCOMBOBOXEX pnmce = (PNMCOMBOBOXEX)pnmh;
  411. if (pnmce->ceItem.lParam)
  412. {
  413. // Is this a pidl?
  414. if ((pnmce->ceItem.lParam) > FILEASSOCIATIONSID_MAX)
  415. {
  416. // Yes, so let's free it.
  417. Str_SetPtr((LPTSTR *)&pnmce->ceItem.lParam, NULL);
  418. }
  419. }
  420. return 1L;
  421. }