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.

1623 lines
42 KiB

  1. #include "shellprv.h"
  2. #include "category.h"
  3. #include "prop.h"
  4. #include "ids.h"
  5. #include "clsobj.h"
  6. #include "comcat.h" // for IEnumGUID
  7. #include "ole2dup.h"
  8. #define GROUPID_UNSPECIFIED (-10)
  9. #define GROUPID_FOLDER (-11)
  10. #define GROUPID_OTHER (-12)
  11. #define STRINGID_FROM_GROUPID(id) ((id) == GROUPID_UNSPECIFIED)? IDS_UNSPECIFIED : (((id) == GROUPID_FOLDER)?IDS_GROUPFOLDERS: IDS_GROUPOTHERCHAR)
  12. typedef struct tagCATCACHE
  13. {
  14. GUID guid;
  15. SHCOLUMNID scid;
  16. IUnknown* punk;
  17. } CATCACHE;
  18. // {3E373E22-DA99-4cb7-A886-754EAE984CB4}
  19. static const GUID CLSID_DetailCategorizer =
  20. { 0x3e373e22, 0xda99, 0x4cb7, { 0xa8, 0x86, 0x75, 0x4e, 0xae, 0x98, 0x4c, 0xb4 } };
  21. class CTimeCategorizer : public ICategorizer,
  22. public IShellExtInit
  23. {
  24. public:
  25. // IUnknown
  26. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  27. STDMETHODIMP_(ULONG) AddRef(void);
  28. STDMETHODIMP_(ULONG) Release(void);
  29. // ICategorizer
  30. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  31. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  32. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  33. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  34. // IShellExtInit
  35. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  36. CTimeCategorizer(const SHCOLUMNID* pscid, IShellFolder2* psf);
  37. CTimeCategorizer();
  38. private:
  39. ~CTimeCategorizer();
  40. long _cRef;
  41. IShellFolder2* _psf;
  42. SHCOLUMNID _scid;
  43. };
  44. class CSizeCategorizer : public ICategorizer,
  45. public IShellExtInit
  46. {
  47. public:
  48. // IUnknown
  49. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  50. STDMETHODIMP_(ULONG) AddRef(void);
  51. STDMETHODIMP_(ULONG) Release(void);
  52. // ICategorizer
  53. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  54. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  55. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  56. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  57. // IShellExtInit
  58. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  59. CSizeCategorizer(IShellFolder2* psf);
  60. CSizeCategorizer(BOOL fLarge);
  61. private:
  62. ~CSizeCategorizer();
  63. long _cRef;
  64. IShellFolder2* _psf;
  65. BOOL _fLarge;
  66. };
  67. class CDriveTypeCategorizer : public ICategorizer,
  68. public IShellExtInit
  69. {
  70. public:
  71. // IUnknown
  72. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  73. STDMETHODIMP_(ULONG) AddRef(void);
  74. STDMETHODIMP_(ULONG) Release(void);
  75. // ICategorizer
  76. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  77. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  78. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  79. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  80. // IShellExtInit
  81. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  82. CDriveTypeCategorizer(IShellFolder2* psf);
  83. CDriveTypeCategorizer();
  84. private:
  85. ~CDriveTypeCategorizer();
  86. long _cRef;
  87. IShellFolder2* _psf;
  88. };
  89. class CAlphaCategorizer : public ICategorizer
  90. {
  91. public:
  92. // IUnknown
  93. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  94. STDMETHODIMP_(ULONG) AddRef(void);
  95. STDMETHODIMP_(ULONG) Release(void);
  96. // ICategorizer
  97. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  98. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  99. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  100. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  101. CAlphaCategorizer(IShellFolder2* psf);
  102. private:
  103. ~CAlphaCategorizer();
  104. long _cRef;
  105. IShellFolder2* _psf;
  106. };
  107. class CFreeSpaceCategorizer : public ICategorizer,
  108. public IShellExtInit
  109. {
  110. public:
  111. // IUnknown
  112. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  113. STDMETHODIMP_(ULONG) AddRef(void);
  114. STDMETHODIMP_(ULONG) Release(void);
  115. // ICategorizer
  116. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  117. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  118. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  119. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  120. // IShellExtInit
  121. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  122. CFreeSpaceCategorizer();
  123. private:
  124. ~CFreeSpaceCategorizer();
  125. long _cRef;
  126. IShellFolder2* _psf;
  127. };
  128. class CDetailCategorizer : public ICategorizer
  129. {
  130. public:
  131. // IUnknown
  132. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  133. STDMETHODIMP_(ULONG) AddRef(void);
  134. STDMETHODIMP_(ULONG) Release(void);
  135. // ICategorizer
  136. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  137. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  138. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  139. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  140. CDetailCategorizer(IShellFolder2* psf, const SHCOLUMNID& scid);
  141. private:
  142. ~CDetailCategorizer();
  143. long _cRef;
  144. IShellFolder2* _psf;
  145. SHCOLUMNID _scid;
  146. HHASHTABLE _hash;
  147. HDPA _hdpaKeys;
  148. };
  149. class CEnumCategoryGUID : public IEnumGUID
  150. {
  151. public:
  152. // IUnknown
  153. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  154. STDMETHODIMP_(ULONG) AddRef(void);
  155. STDMETHODIMP_(ULONG) Release(void);
  156. // IEnumIDList
  157. STDMETHODIMP Next(ULONG celt, GUID *rgelt, ULONG *pceltFetched);
  158. STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; }
  159. STDMETHODIMP Reset() { _iIndex = 0; return S_OK;}
  160. STDMETHODIMP Clone(IEnumGUID **ppenum) { return E_NOTIMPL; };
  161. CEnumCategoryGUID(HDSA hdsa);
  162. private:
  163. long _cRef;
  164. HDSA _hda;
  165. int _iIndex;
  166. };
  167. CEnumCategoryGUID::CEnumCategoryGUID(HDSA hda): _cRef(1)
  168. {
  169. _hda = hda;
  170. }
  171. HRESULT CEnumCategoryGUID::QueryInterface(REFIID riid, void **ppv)
  172. {
  173. static const QITAB qit[] =
  174. {
  175. QITABENT(CEnumCategoryGUID, IEnumGUID),
  176. { 0 },
  177. };
  178. return QISearch(this, qit, riid, ppv);
  179. }
  180. ULONG CEnumCategoryGUID::AddRef()
  181. {
  182. return InterlockedIncrement(&_cRef);
  183. }
  184. ULONG CEnumCategoryGUID::Release()
  185. {
  186. if (InterlockedDecrement(&_cRef))
  187. return _cRef;
  188. delete this;
  189. return 0;
  190. }
  191. STDMETHODIMP CEnumCategoryGUID::Next(ULONG celt, GUID *rgelt, ULONG *pceltFetched)
  192. {
  193. HRESULT hr = S_FALSE;
  194. if (celt > 1)
  195. return E_INVALIDARG;
  196. if (_hda == NULL)
  197. return hr;
  198. while (hr != S_OK &&
  199. _iIndex < DSA_GetItemCount(_hda))
  200. {
  201. CATCACHE* pcat = (CATCACHE*)DSA_GetItemPtr(_hda, _iIndex);
  202. // Is this a scid map entry instead of an external categorizer?
  203. if (pcat->scid.fmtid == GUID_NULL)
  204. {
  205. // Nope. then we can enum it.
  206. if (pceltFetched)
  207. *pceltFetched = 1;
  208. *rgelt = pcat->guid;
  209. hr = S_OK;
  210. }
  211. _iIndex++;
  212. }
  213. return hr;
  214. }
  215. class CCategoryProvider : public ICategoryProvider, public IDefCategoryProvider
  216. {
  217. public:
  218. // IUnknown
  219. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  220. STDMETHODIMP_(ULONG) AddRef(void);
  221. STDMETHODIMP_(ULONG) Release(void);
  222. // ICategoryProvider
  223. STDMETHODIMP CanCategorizeOnSCID(SHCOLUMNID* pscid);
  224. STDMETHODIMP GetDefaultCategory(GUID* pguid, SHCOLUMNID* pscid);
  225. STDMETHODIMP GetCategoryForSCID(SHCOLUMNID* pscid, GUID* pguid);
  226. STDMETHODIMP EnumCategories(IEnumGUID** penum);
  227. STDMETHODIMP GetCategoryName(GUID* pguid, LPWSTR pszName, UINT cch);
  228. STDMETHODIMP CreateCategory(GUID* pguid, REFIID riid, void** ppv);
  229. // IDefCategoryProvider
  230. STDMETHODIMP Initialize(const GUID* pguid, const SHCOLUMNID* pscid, const SHCOLUMNID* pscidExlude, HKEY hkey, const CATLIST* pcl, IShellFolder* psf);
  231. CCategoryProvider();
  232. private:
  233. ~CCategoryProvider();
  234. BOOL _BuildCategoryList(HKEY hkey, const CATLIST* pcl);
  235. friend int DestroyCache(void *pv, void *unused);
  236. HRESULT CreateInstance(GUID* pguid, REFIID riid, void** ppv);
  237. long _cRef;
  238. LPITEMIDLIST _pidlFolder;
  239. IShellFolder2* _psf;
  240. HDSA _hdaCat;
  241. GUID _guidDefault;
  242. SHCOLUMNID _scidDefault;
  243. HDSA _hdaExcludeSCIDs;
  244. };
  245. STDAPI CCategoryProvider_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  246. {
  247. HRESULT hr = E_OUTOFMEMORY;
  248. CCategoryProvider* p = new CCategoryProvider();
  249. if (p)
  250. {
  251. hr = p->QueryInterface(riid, ppv);
  252. p->Release();
  253. }
  254. return hr;
  255. }
  256. BOOL CCategoryProvider::_BuildCategoryList(HKEY hkey, const CATLIST* pcl)
  257. {
  258. int i = 0;
  259. _hdaCat = DSA_Create(sizeof(CATCACHE), 3);
  260. if (!_hdaCat)
  261. return FALSE;
  262. // Enumerate static
  263. while(!IsEqualGUID(*pcl[i].pguid, GUID_NULL))
  264. {
  265. CATCACHE cc = {0};
  266. cc.guid = *pcl[i].pguid;
  267. if (pcl[i].pscid)
  268. {
  269. cc.scid = *pcl[i].pscid;
  270. }
  271. DSA_AppendItem(_hdaCat, (void*)&cc);
  272. i++;
  273. }
  274. // Enumerate hkey
  275. TCHAR szHandlerCLSID[GUIDSTR_MAX];
  276. int iHandler = 0;
  277. while (ERROR_SUCCESS == RegEnumKey(hkey, iHandler++, szHandlerCLSID, ARRAYSIZE(szHandlerCLSID)))
  278. {
  279. CLSID clsid;
  280. if (SUCCEEDED(SHCLSIDFromString(szHandlerCLSID, &clsid)))
  281. {
  282. CATCACHE cc = {0};
  283. cc.guid = clsid;
  284. DSA_AppendItem(_hdaCat, (void*)&cc);
  285. i++;
  286. }
  287. }
  288. return TRUE;
  289. }
  290. CCategoryProvider::CCategoryProvider() : _cRef(1)
  291. {
  292. DllAddRef();
  293. }
  294. int DestroyCache(void *pv, void *unused)
  295. {
  296. CATCACHE* pcat = (CATCACHE*)pv;
  297. ATOMICRELEASE(pcat->punk);
  298. return 1;
  299. }
  300. CCategoryProvider::~CCategoryProvider()
  301. {
  302. ATOMICRELEASE(_psf);
  303. ILFree(_pidlFolder);
  304. if (_hdaExcludeSCIDs)
  305. {
  306. DSA_Destroy(_hdaExcludeSCIDs);
  307. }
  308. if (_hdaCat)
  309. {
  310. DSA_DestroyCallback(_hdaCat, DestroyCache, NULL);
  311. }
  312. DllRelease();
  313. }
  314. HRESULT CCategoryProvider::QueryInterface(REFIID riid, void **ppv)
  315. {
  316. static const QITAB qit[] =
  317. {
  318. QITABENT(CCategoryProvider, IDefCategoryProvider),
  319. QITABENT(CCategoryProvider, ICategoryProvider),
  320. { 0 },
  321. };
  322. return QISearch(this, qit, riid, ppv);
  323. }
  324. ULONG CCategoryProvider::AddRef()
  325. {
  326. return InterlockedIncrement(&_cRef);
  327. }
  328. ULONG CCategoryProvider::Release()
  329. {
  330. if (InterlockedDecrement(&_cRef))
  331. return _cRef;
  332. delete this;
  333. return 0;
  334. }
  335. HRESULT CCategoryProvider::Initialize(const GUID* pguid, const SHCOLUMNID* pscid, const SHCOLUMNID* pscidExclude, HKEY hkey, const CATLIST* pcl, IShellFolder* psf)
  336. {
  337. if (!psf)
  338. return E_INVALIDARG;
  339. HRESULT hr = SHGetIDListFromUnk(psf, &_pidlFolder);
  340. if (SUCCEEDED(hr))
  341. {
  342. if (pcl && !_BuildCategoryList(hkey, pcl))
  343. return E_OUTOFMEMORY;
  344. if (pguid)
  345. _guidDefault = *pguid;
  346. if (pscid)
  347. _scidDefault = *pscid;
  348. if (pscidExclude)
  349. {
  350. _hdaExcludeSCIDs = DSA_Create(sizeof(SHCOLUMNID), 3);
  351. if (_hdaExcludeSCIDs)
  352. {
  353. int i = 0;
  354. while(pscidExclude[i].fmtid != GUID_NULL)
  355. {
  356. DSA_AppendItem(_hdaExcludeSCIDs, (void*)&pscidExclude[i]);
  357. i++;
  358. }
  359. }
  360. }
  361. hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf));
  362. }
  363. return hr;
  364. }
  365. STDMETHODIMP CCategoryProvider::CanCategorizeOnSCID(SHCOLUMNID* pscid)
  366. {
  367. if (_hdaExcludeSCIDs)
  368. {
  369. for (int i=0; i < DSA_GetItemCount(_hdaExcludeSCIDs); i++)
  370. {
  371. SHCOLUMNID* pscidExclude = (SHCOLUMNID*)DSA_GetItemPtr(_hdaExcludeSCIDs, i);
  372. if (IsEqualSCID(*pscidExclude, *pscid))
  373. return S_FALSE;
  374. }
  375. }
  376. return S_OK;
  377. }
  378. STDMETHODIMP CCategoryProvider::GetDefaultCategory(GUID* pguid, SHCOLUMNID* pscid)
  379. {
  380. *pguid = _guidDefault;
  381. *pscid = _scidDefault;
  382. if (_guidDefault == GUID_NULL && _scidDefault.fmtid == GUID_NULL)
  383. return S_FALSE;
  384. return S_OK;
  385. }
  386. STDMETHODIMP CCategoryProvider::GetCategoryForSCID(SHCOLUMNID* pscid, GUID* pguid)
  387. {
  388. HRESULT hr = S_FALSE;
  389. if (_hdaCat == NULL || pscid == NULL)
  390. return hr;
  391. int c = DSA_GetItemCount(_hdaCat);
  392. for (int i = 0; i < c; i++)
  393. {
  394. CATCACHE* pcc = (CATCACHE*)DSA_GetItemPtr(_hdaCat, i);
  395. ASSERT(pcc != NULL);
  396. if (IsEqualSCID(pcc->scid, *pscid))
  397. {
  398. *pguid = pcc->guid;
  399. hr = S_OK;
  400. break;
  401. }
  402. }
  403. return hr;
  404. }
  405. STDMETHODIMP CCategoryProvider::EnumCategories(IEnumGUID** penum)
  406. {
  407. HRESULT hr = E_NOINTERFACE;
  408. if (_hdaCat)
  409. {
  410. *penum = (IEnumGUID*)new CEnumCategoryGUID(_hdaCat);
  411. if (!*penum)
  412. hr = E_OUTOFMEMORY;
  413. hr = S_OK;
  414. }
  415. return hr;
  416. }
  417. STDMETHODIMP CCategoryProvider::GetCategoryName(GUID* pguid, LPWSTR pszName, UINT cch)
  418. {
  419. ICategorizer* pcat;
  420. HRESULT hr = CreateCategory(pguid, IID_PPV_ARG(ICategorizer, &pcat));
  421. if (SUCCEEDED(hr))
  422. {
  423. hr = pcat->GetDescription(pszName, cch);
  424. pcat->Release();
  425. }
  426. return hr;
  427. }
  428. HRESULT CCategoryProvider::CreateInstance(GUID* pguid, REFIID riid, void** ppv)
  429. {
  430. IShellExtInit* psei;
  431. // These come from HKCR hence must go through approval
  432. HRESULT hr = SHExtCoCreateInstance(NULL, pguid, NULL, IID_PPV_ARG(IShellExtInit, &psei));
  433. if (SUCCEEDED(hr))
  434. {
  435. psei->Initialize(_pidlFolder, NULL, NULL);
  436. hr = psei->QueryInterface(riid, ppv);
  437. psei->Release();
  438. }
  439. return hr;
  440. }
  441. STDMETHODIMP CCategoryProvider::CreateCategory(GUID* pguid, REFIID riid, void** ppv)
  442. {
  443. HRESULT hr = E_NOINTERFACE;
  444. if (_hdaCat != NULL)
  445. {
  446. int c = DSA_GetItemCount(_hdaCat);
  447. for (int i = 0; i < c; i++)
  448. {
  449. CATCACHE* pcc = (CATCACHE*)DSA_GetItemPtr(_hdaCat, i);
  450. ASSERT(pcc != NULL);
  451. if (IsEqualGUID(pcc->guid, *pguid))
  452. {
  453. if (!pcc->punk)
  454. {
  455. hr = CreateInstance(pguid, IID_PPV_ARG(IUnknown, &pcc->punk));
  456. }
  457. if (pcc->punk)
  458. {
  459. hr = pcc->punk->QueryInterface(riid, ppv);
  460. }
  461. break;
  462. }
  463. }
  464. }
  465. if (FAILED(hr))
  466. {
  467. // Not in the cache? Just try a create
  468. hr = CreateInstance(pguid, riid, ppv);
  469. }
  470. return hr;
  471. }
  472. STDAPI CCategoryProvider_Create(const GUID* pguid, const SHCOLUMNID* pscid, HKEY hkey, const CATLIST* pcl, IShellFolder* psf, REFIID riid, void **ppv)
  473. {
  474. HRESULT hr;
  475. CCategoryProvider *pdext = new CCategoryProvider();
  476. if (pdext)
  477. {
  478. hr = pdext->Initialize(pguid, pscid, NULL, hkey, pcl, psf);
  479. if (SUCCEEDED(hr))
  480. hr = pdext->QueryInterface(riid, ppv);
  481. pdext->Release();
  482. }
  483. else
  484. {
  485. *ppv = NULL;
  486. hr = E_OUTOFMEMORY;
  487. }
  488. return hr;
  489. }
  490. /////////////////////////////////////////////////////////
  491. // Time Categorizer
  492. STDAPI CTimeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  493. {
  494. HRESULT hr = E_OUTOFMEMORY;
  495. CTimeCategorizer* p = new CTimeCategorizer();
  496. if (p)
  497. {
  498. hr = p->QueryInterface(riid, ppv);
  499. p->Release();
  500. }
  501. return hr;
  502. }
  503. STDAPI CTimeCategorizer_Create(IShellFolder2* psf2, const SHCOLUMNID* pscid, REFIID riid, void **ppv)
  504. {
  505. HRESULT hr = E_OUTOFMEMORY;
  506. CTimeCategorizer* p = new CTimeCategorizer(pscid, psf2);
  507. if (p)
  508. {
  509. hr = p->QueryInterface(riid, ppv);
  510. p->Release();
  511. }
  512. return hr;
  513. }
  514. CTimeCategorizer::CTimeCategorizer(const SHCOLUMNID* pscid, IShellFolder2* psf) : _cRef(1)
  515. {
  516. _psf = psf;
  517. ASSERT(psf);
  518. psf->AddRef();
  519. _scid = *pscid;
  520. }
  521. CTimeCategorizer::CTimeCategorizer() : _cRef(1)
  522. {
  523. _scid = SCID_WRITETIME;
  524. }
  525. CTimeCategorizer::~CTimeCategorizer()
  526. {
  527. ATOMICRELEASE(_psf);
  528. }
  529. HRESULT CTimeCategorizer::QueryInterface(REFIID riid, void **ppv)
  530. {
  531. static const QITAB qit[] =
  532. {
  533. QITABENT(CTimeCategorizer, ICategorizer),
  534. QITABENT(CTimeCategorizer, IShellExtInit),
  535. { 0 },
  536. };
  537. return QISearch(this, qit, riid, ppv);
  538. }
  539. ULONG CTimeCategorizer::AddRef()
  540. {
  541. return InterlockedIncrement(&_cRef);
  542. }
  543. ULONG CTimeCategorizer::Release()
  544. {
  545. if (InterlockedDecrement(&_cRef))
  546. return _cRef;
  547. delete this;
  548. return 0;
  549. }
  550. HRESULT CTimeCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
  551. {
  552. ATOMICRELEASE(_psf);
  553. return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
  554. }
  555. HRESULT CTimeCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  556. {
  557. LoadString(HINST_THISDLL, IDS_GROUPBYTIME, pszDesc, cch);
  558. return S_OK;
  559. }
  560. static const int mpcdymoAccum[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
  561. int GetDaysForMonth(int yr, int mo)
  562. {
  563. int cdy;
  564. if (yr == 1752 && mo == 9)
  565. return 19;
  566. cdy = mpcdymoAccum[mo] - mpcdymoAccum[mo - 1];
  567. if (mo == 2 && (yr & 03) == 0 && (yr <= 1750 || yr % 100 != 0 || yr % 400 == 0))
  568. cdy++;
  569. return cdy;
  570. }
  571. int GetDaysForLastMonth(int year, int month)
  572. {
  573. if (month == 1)
  574. {
  575. year--;
  576. month = 12;
  577. }
  578. else
  579. month--;
  580. return GetDaysForMonth(year, month);
  581. }
  582. HRESULT CTimeCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
  583. {
  584. SYSTEMTIME stCur;
  585. GetLocalTime(&stCur);
  586. for (UINT i = 0; i < cidl; i++)
  587. {
  588. FILETIME ft;
  589. // Get the time data
  590. if (SUCCEEDED(GetDateProperty(_psf, apidl[i], &_scid, &ft)))
  591. {
  592. // Convert it to a usable format
  593. SYSTEMTIME stFile;
  594. FileTimeToLocalFileTime(&ft, &ft);
  595. FileTimeToSystemTime(&ft, &stFile);
  596. if (stFile.wYear == stCur.wYear)
  597. {
  598. if (stFile.wMonth > stCur.wMonth)
  599. {
  600. if (stFile.wMonth == stCur.wMonth + 1)
  601. {
  602. rgCategoryIds[i] = IDS_NEXTMONTH;
  603. }
  604. else
  605. {
  606. rgCategoryIds[i] = IDS_LATERTHISYEAR;
  607. }
  608. }
  609. else if (stFile.wMonth == stCur.wMonth)
  610. {
  611. if (stFile.wDay == stCur.wDay + 1)
  612. {
  613. rgCategoryIds[i] = IDS_TOMORROW;
  614. }
  615. else if (stFile.wDay == stCur.wDay + 2)
  616. {
  617. rgCategoryIds[i] = IDS_TWODAYSFROMNOW;
  618. }
  619. else if (stFile.wDay == stCur.wDay)
  620. {
  621. rgCategoryIds[i] = IDS_TODAY;
  622. }
  623. else if (stFile.wDay == stCur.wDay - 1)
  624. {
  625. rgCategoryIds[i] = IDS_YESTERDAY;
  626. }
  627. else if (stFile.wDayOfWeek < stCur.wDayOfWeek &&
  628. stFile.wDay < stCur.wDay &&
  629. stCur.wDay - stCur.wDayOfWeek > 0 &&
  630. stFile.wDay >= stCur.wDay - stCur.wDayOfWeek)
  631. {
  632. rgCategoryIds[i] = IDS_EARLIERTHISWEEK;
  633. }
  634. else if (stFile.wDayOfWeek > stCur.wDayOfWeek &&
  635. stFile.wDay > stCur.wDay &&
  636. stFile.wDay <= stCur.wDay + (7 - stCur.wDayOfWeek))
  637. {
  638. rgCategoryIds[i] = IDS_LATERTHISWEEK;
  639. }
  640. else if (stFile.wDay > stCur.wDay)
  641. {
  642. rgCategoryIds[i] = IDS_LATERTHISMONTH;
  643. }
  644. else
  645. {
  646. int fileDays = GetDaysForLastMonth(stFile.wYear, stFile.wMonth - 1) + stFile.wDay;
  647. int curDays = GetDaysForLastMonth(stCur.wYear, stCur.wMonth - 1) + stCur.wDay;
  648. if (fileDays < (curDays - stCur.wDayOfWeek) &&
  649. fileDays > (curDays - stCur.wDayOfWeek - 7))
  650. {
  651. rgCategoryIds[i] = IDS_LASTWEEK;
  652. }
  653. else if (fileDays < (curDays - stCur.wDayOfWeek - 7) &&
  654. fileDays > (curDays - stCur.wDayOfWeek - 14))
  655. {
  656. rgCategoryIds[i] = IDS_TWOWEEKSAGO;
  657. }
  658. else
  659. {
  660. rgCategoryIds[i] = IDS_EARLIERTHISMONTH;
  661. }
  662. }
  663. }
  664. else if (stFile.wMonth == stCur.wMonth - 1 ||
  665. (stFile.wMonth == 12 &&
  666. stCur.wMonth == 1))
  667. {
  668. rgCategoryIds[i] = IDS_LASTMONTH;
  669. }
  670. else if (stFile.wMonth == stCur.wMonth - 2 ||
  671. (stFile.wMonth == 12 && stCur.wMonth == 2) ||
  672. (stFile.wMonth == 11 && stCur.wMonth == 1))
  673. {
  674. rgCategoryIds[i] = IDS_TWOMONTHSAGO;
  675. }
  676. else
  677. {
  678. rgCategoryIds[i] = IDS_EARLIERTHISYEAR;
  679. }
  680. }
  681. else if (stFile.wYear == stCur.wYear - 1)
  682. {
  683. rgCategoryIds[i] = IDS_LASTYEAR;
  684. }
  685. else if (stFile.wYear == stCur.wYear - 2)
  686. {
  687. rgCategoryIds[i] = IDS_TWOYEARSAGO;
  688. }
  689. else if (stFile.wYear < stCur.wYear - 2)
  690. {
  691. rgCategoryIds[i] = IDS_LONGTIMEAGO;
  692. }
  693. else if (stFile.wYear == stCur.wYear + 1)
  694. {
  695. rgCategoryIds[i] = IDS_NEXTYEAR;
  696. }
  697. else if (stFile.wYear > stCur.wYear + 2)
  698. {
  699. rgCategoryIds[i] = IDS_SOMETIMETHISMILLENNIA;
  700. }
  701. else if (stFile.wYear > (stCur.wYear / 1000) * 1000 + 1000) // 2050 / 1000 = 2. 2 * 1000 = 2000. 2000 + 1000 = 3000 i.e. next millennium
  702. {
  703. rgCategoryIds[i] = IDS_SOMEFUTUREDATE;
  704. }
  705. }
  706. else
  707. {
  708. rgCategoryIds[i] = IDS_UNSPECIFIED;
  709. }
  710. }
  711. return S_OK;
  712. }
  713. HRESULT CTimeCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  714. {
  715. LoadString(HINST_THISDLL, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
  716. return S_OK;
  717. }
  718. HRESULT CTimeCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  719. {
  720. if (dwCategoryId1 == dwCategoryId2)
  721. return ResultFromShort(0);
  722. else if (dwCategoryId1 == IDS_GROUPFOLDERS)
  723. return ResultFromShort(-1);
  724. else if (dwCategoryId2 == IDS_GROUPFOLDERS)
  725. return ResultFromShort(1);
  726. else if (dwCategoryId1 < dwCategoryId2)
  727. return ResultFromShort(-1);
  728. else
  729. return ResultFromShort(1);
  730. }
  731. /////////////////////////////////////////////////////////
  732. // Size Categorizer
  733. STDAPI CDriveSizeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  734. {
  735. HRESULT hr = E_OUTOFMEMORY;
  736. CSizeCategorizer* p = new CSizeCategorizer(TRUE);
  737. if (p)
  738. {
  739. hr = p->QueryInterface(riid, ppv);
  740. p->Release();
  741. }
  742. return hr;
  743. }
  744. STDAPI CSizeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  745. {
  746. HRESULT hr = E_OUTOFMEMORY;
  747. CSizeCategorizer* p = new CSizeCategorizer(FALSE);
  748. if (p)
  749. {
  750. hr = p->QueryInterface(riid, ppv);
  751. p->Release();
  752. }
  753. return hr;
  754. }
  755. STDAPI CSizeCategorizer_Create(IShellFolder2* psf2, REFIID riid, void **ppv)
  756. {
  757. HRESULT hr = E_OUTOFMEMORY;
  758. CSizeCategorizer* p = new CSizeCategorizer(psf2);
  759. if (p)
  760. {
  761. hr = p->QueryInterface(riid, ppv);
  762. p->Release();
  763. }
  764. return hr;
  765. }
  766. CSizeCategorizer::CSizeCategorizer(IShellFolder2* psf) : _cRef(1)
  767. {
  768. _psf = psf;
  769. ASSERT(psf);
  770. psf->AddRef();
  771. }
  772. CSizeCategorizer::CSizeCategorizer(BOOL fLarge) : _cRef(1), _fLarge(fLarge)
  773. {
  774. }
  775. CSizeCategorizer::~CSizeCategorizer()
  776. {
  777. ATOMICRELEASE(_psf);
  778. }
  779. HRESULT CSizeCategorizer::QueryInterface(REFIID riid, void **ppv)
  780. {
  781. static const QITAB qit[] =
  782. {
  783. QITABENT(CSizeCategorizer, ICategorizer),
  784. QITABENT(CSizeCategorizer, IShellExtInit),
  785. { 0 },
  786. };
  787. return QISearch(this, qit, riid, ppv);
  788. }
  789. ULONG CSizeCategorizer::AddRef()
  790. {
  791. return InterlockedIncrement(&_cRef);
  792. }
  793. ULONG CSizeCategorizer::Release()
  794. {
  795. if (InterlockedDecrement(&_cRef))
  796. return _cRef;
  797. delete this;
  798. return 0;
  799. }
  800. HRESULT CSizeCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
  801. {
  802. ATOMICRELEASE(_psf);
  803. return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
  804. }
  805. HRESULT CSizeCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  806. {
  807. LoadString(HINST_THISDLL, IDS_GROUPBYSIZE, pszDesc, cch);
  808. return S_OK;
  809. }
  810. const static ULONGLONG s_rgSizesSmall[] =
  811. {
  812. // 130mb 16mb 1mb 100k 10l
  813. 134217728, 16777216, 1048576, 131072, 32768, 0
  814. };
  815. const static ULONGLONG s_rgSizesLarge[] =
  816. {
  817. // 80gig 25gig 10gig 2gig 500mb
  818. 80000000000, 25000000000, 10000000000, 2000000000, 500000000, 0
  819. };
  820. HRESULT CSizeCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
  821. {
  822. if (_psf == NULL)
  823. return E_ACCESSDENIED; // Not initialized yet.
  824. for (UINT i = 0; i < cidl; i++)
  825. {
  826. const ULONGLONG* pll = _fLarge? s_rgSizesLarge : s_rgSizesSmall;
  827. // Get the size data
  828. ULONGLONG ullSize;
  829. if (SUCCEEDED(GetLongProperty(_psf, apidl[i], _fLarge?&SCID_CAPACITY:&SCID_SIZE, &ullSize)))
  830. {
  831. if (ullSize >= pll[0])
  832. rgCategoryIds[i] = IDS_GIGANTIC;
  833. if (ullSize < pll[0])
  834. rgCategoryIds[i] = IDS_HUGE;
  835. if (ullSize < pll[1]) // Under 16mb
  836. rgCategoryIds[i] = IDS_LARGE;
  837. if (ullSize < pll[2]) // Under 1mb
  838. rgCategoryIds[i] = IDS_MEDIUM;
  839. if (ullSize < pll[3]) // Under 100k
  840. rgCategoryIds[i] = IDS_SMALL;
  841. if (ullSize < pll[4]) // Under 10k
  842. rgCategoryIds[i] = IDS_TINY;
  843. if (ullSize == pll[5]) // Zero sized files
  844. {
  845. if (SHGetAttributes(_psf, apidl[i], SFGAO_FOLDER))
  846. {
  847. rgCategoryIds[i] = IDS_FOLDERS;
  848. }
  849. else
  850. {
  851. rgCategoryIds[i] = IDS_ZERO;
  852. }
  853. }
  854. }
  855. else
  856. {
  857. rgCategoryIds[i] = IDS_UNSPECIFIED;
  858. }
  859. }
  860. return S_OK;
  861. }
  862. HRESULT CSizeCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  863. {
  864. LoadString(HINST_THISDLL, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
  865. return S_OK;
  866. }
  867. HRESULT CSizeCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  868. {
  869. if (dwCategoryId1 == dwCategoryId2)
  870. return ResultFromShort(0);
  871. else if (dwCategoryId1 == IDS_GROUPFOLDERS)
  872. return ResultFromShort(-1);
  873. else if (dwCategoryId2 == IDS_GROUPFOLDERS)
  874. return ResultFromShort(1);
  875. else if (dwCategoryId1 < dwCategoryId2)
  876. return ResultFromShort(-1);
  877. else
  878. return ResultFromShort(1);
  879. }
  880. /////////////////////////////////////////////////////////
  881. // Type Categorizer
  882. STDAPI CDriveTypeCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  883. {
  884. CDriveTypeCategorizer *p = new CDriveTypeCategorizer();
  885. if (!p)
  886. return E_OUTOFMEMORY;
  887. HRESULT hr = p->QueryInterface(riid, ppv);
  888. p->Release();
  889. return hr;
  890. }
  891. CDriveTypeCategorizer::CDriveTypeCategorizer(IShellFolder2* psf) :
  892. _cRef(1)
  893. {
  894. _psf = psf;
  895. ASSERT(psf);
  896. psf->AddRef();
  897. }
  898. CDriveTypeCategorizer::CDriveTypeCategorizer() :
  899. _cRef(1)
  900. {
  901. }
  902. CDriveTypeCategorizer::~CDriveTypeCategorizer()
  903. {
  904. ATOMICRELEASE(_psf);
  905. }
  906. HRESULT CDriveTypeCategorizer::QueryInterface(REFIID riid, void **ppv)
  907. {
  908. static const QITAB qit[] =
  909. {
  910. QITABENT(CDriveTypeCategorizer, ICategorizer),
  911. QITABENT(CDriveTypeCategorizer, IShellExtInit),
  912. { 0 },
  913. };
  914. return QISearch(this, qit, riid, ppv);
  915. }
  916. ULONG CDriveTypeCategorizer::AddRef()
  917. {
  918. return InterlockedIncrement(&_cRef);
  919. }
  920. ULONG CDriveTypeCategorizer::Release()
  921. {
  922. if (InterlockedDecrement(&_cRef))
  923. return _cRef;
  924. delete this;
  925. return 0;
  926. }
  927. HRESULT CDriveTypeCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
  928. {
  929. ATOMICRELEASE(_psf);
  930. return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
  931. }
  932. HRESULT CDriveTypeCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  933. {
  934. LoadString(HINST_THISDLL, IDS_GROUPBYDRIVETYPE, pszDesc, cch);
  935. return S_OK;
  936. }
  937. const struct { DWORD dwDescriptionId; UINT uIDGroup; } c_drives_mapping[] =
  938. {
  939. { SHDID_COMPUTER_FIXED , IDS_DRIVES_FIXED_GROUP },
  940. { SHDID_COMPUTER_DRIVE35 , IDS_DRIVES_REMOVABLE_GROUP },
  941. { SHDID_COMPUTER_REMOVABLE , IDS_DRIVES_REMOVABLE_GROUP },
  942. { SHDID_COMPUTER_CDROM , IDS_DRIVES_REMOVABLE_GROUP },
  943. { SHDID_COMPUTER_NETDRIVE , IDS_DRIVES_NETDRIVE_GROUP },
  944. { SHDID_COMPUTER_OTHER , IDS_DRIVES_OTHER_GROUP },
  945. { SHDID_COMPUTER_DRIVE525 , IDS_DRIVES_REMOVABLE_GROUP },
  946. { SHDID_COMPUTER_RAMDISK , IDS_DRIVES_OTHER_GROUP },
  947. { SHDID_COMPUTER_IMAGING , IDS_DRIVES_IMAGING_GROUP },
  948. { SHDID_COMPUTER_AUDIO , IDS_DRIVES_AUDIO_GROUP },
  949. { SHDID_COMPUTER_SHAREDDOCS, IDS_DRIVES_SHAREDDOCS_GROUP},
  950. };
  951. HRESULT CDriveTypeCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
  952. {
  953. HRESULT hr;
  954. if (_psf == NULL)
  955. {
  956. hr = E_ACCESSDENIED; // Not initialized yet.
  957. }
  958. else
  959. {
  960. for (UINT i = 0; i < cidl; i++)
  961. {
  962. rgCategoryIds[i] = IDS_DRIVES_OTHER_GROUP;
  963. VARIANT v;
  964. // Get the type data
  965. hr = _psf->GetDetailsEx(apidl[i], &SCID_DESCRIPTIONID, &v);
  966. if (SUCCEEDED(hr))
  967. {
  968. SHDESCRIPTIONID did;
  969. if (VariantToBuffer(&v, &did, sizeof(did)))
  970. {
  971. for (int j = 0; j < ARRAYSIZE(c_drives_mapping); j++)
  972. {
  973. if (did.dwDescriptionId == c_drives_mapping[j].dwDescriptionId)
  974. {
  975. rgCategoryIds[i] = c_drives_mapping[j].uIDGroup;
  976. break;
  977. }
  978. }
  979. }
  980. VariantClear(&v);
  981. }
  982. }
  983. hr = S_OK;
  984. }
  985. return hr;
  986. }
  987. HRESULT CDriveTypeCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  988. {
  989. LoadString(HINST_THISDLL, dwCategoryId, pci->wszName, ARRAYSIZE(pci->wszName));
  990. return S_OK;
  991. }
  992. HRESULT CDriveTypeCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  993. {
  994. if (dwCategoryId1 == dwCategoryId2)
  995. return ResultFromShort(0);
  996. else if (dwCategoryId1 < dwCategoryId2)
  997. return ResultFromShort(-1);
  998. else
  999. return ResultFromShort(1);
  1000. }
  1001. /////////////////////////////////////////////////////////
  1002. // FreeSpace Categorizer
  1003. CFreeSpaceCategorizer::CFreeSpaceCategorizer() : _cRef(1)
  1004. {
  1005. }
  1006. CFreeSpaceCategorizer::~CFreeSpaceCategorizer()
  1007. {
  1008. ATOMICRELEASE(_psf);
  1009. }
  1010. HRESULT CFreeSpaceCategorizer::QueryInterface(REFIID riid, void **ppv)
  1011. {
  1012. static const QITAB qit[] =
  1013. {
  1014. QITABENT(CFreeSpaceCategorizer, ICategorizer),
  1015. QITABENT(CFreeSpaceCategorizer, IShellExtInit),
  1016. { 0 },
  1017. };
  1018. return QISearch(this, qit, riid, ppv);
  1019. }
  1020. ULONG CFreeSpaceCategorizer::AddRef()
  1021. {
  1022. return InterlockedIncrement(&_cRef);
  1023. }
  1024. ULONG CFreeSpaceCategorizer::Release()
  1025. {
  1026. if (InterlockedDecrement(&_cRef))
  1027. return _cRef;
  1028. delete this;
  1029. return 0;
  1030. }
  1031. HRESULT CFreeSpaceCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
  1032. {
  1033. ATOMICRELEASE(_psf);
  1034. return SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidlFolder, &_psf));
  1035. }
  1036. HRESULT CFreeSpaceCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  1037. {
  1038. LoadString(HINST_THISDLL, IDS_GROUPBYFREESPACE, pszDesc, cch);
  1039. return S_OK;
  1040. }
  1041. HRESULT CFreeSpaceCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST* apidl, DWORD* rgCategoryIds)
  1042. {
  1043. if (_psf == NULL)
  1044. return E_ACCESSDENIED; // Not initialized yet.
  1045. for (UINT i = 0; i < cidl; i++)
  1046. {
  1047. rgCategoryIds[i] = IDS_UNSPECIFIED;
  1048. // Get the total size and free space
  1049. ULONGLONG ullSize;
  1050. if (SUCCEEDED(GetLongProperty(_psf, apidl[i], &SCID_CAPACITY, &ullSize)))
  1051. {
  1052. ULONGLONG ullFree;
  1053. if (SUCCEEDED(GetLongProperty(_psf, apidl[i], &SCID_FREESPACE, &ullFree)))
  1054. {
  1055. // Prevent divide by zero
  1056. if (ullSize == 0)
  1057. {
  1058. rgCategoryIds[i] = IDS_UNSPECIFIED;
  1059. }
  1060. else
  1061. {
  1062. // Turning this into a percent. DWORD cast is ok.
  1063. rgCategoryIds[i] = (static_cast<DWORD>((ullFree * 100) / ullSize) / 10) * 10;
  1064. }
  1065. }
  1066. }
  1067. }
  1068. return S_OK;
  1069. }
  1070. HRESULT CFreeSpaceCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  1071. {
  1072. if (dwCategoryId == IDS_UNSPECIFIED)
  1073. {
  1074. LoadString(HINST_THISDLL, IDS_UNSPECIFIED, pci->wszName, ARRAYSIZE(pci->wszName));
  1075. return S_OK;
  1076. }
  1077. else
  1078. {
  1079. TCHAR szName[MAX_PATH];
  1080. LoadString(HINST_THISDLL, IDS_FREESPACEPERCENT, szName, ARRAYSIZE(szName));
  1081. wnsprintf(pci->wszName, ARRAYSIZE(pci->wszName), szName, dwCategoryId);
  1082. return S_OK;
  1083. }
  1084. return E_FAIL;
  1085. }
  1086. HRESULT CFreeSpaceCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  1087. {
  1088. if (dwCategoryId1 == IDS_UNSPECIFIED)
  1089. return ResultFromShort(1);
  1090. else if (dwCategoryId2 == IDS_UNSPECIFIED)
  1091. return ResultFromShort(-1);
  1092. else if (dwCategoryId1 == dwCategoryId2)
  1093. return ResultFromShort(0);
  1094. else if (dwCategoryId1 < dwCategoryId2)
  1095. return ResultFromShort(-1);
  1096. else
  1097. return ResultFromShort(1);
  1098. }
  1099. STDAPI CFreeSpaceCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  1100. {
  1101. HRESULT hr = E_OUTOFMEMORY;
  1102. CFreeSpaceCategorizer* p = new CFreeSpaceCategorizer();
  1103. if (p)
  1104. {
  1105. hr = p->QueryInterface(riid, ppv);
  1106. p->Release();
  1107. }
  1108. return hr;
  1109. }
  1110. /////////////////////////////////////////////////////////
  1111. // Detail Categorizer
  1112. STDAPI CDetailCategorizer_Create(const SHCOLUMNID& pscid, IShellFolder2* psf2, REFIID riid, void **ppv)
  1113. {
  1114. HRESULT hr = E_OUTOFMEMORY;
  1115. CDetailCategorizer* p = new CDetailCategorizer(psf2, pscid);
  1116. if (p)
  1117. {
  1118. hr = p->QueryInterface(riid, ppv);
  1119. p->Release();
  1120. }
  1121. return hr;
  1122. }
  1123. CDetailCategorizer::CDetailCategorizer(IShellFolder2* psf, const SHCOLUMNID& scid) : _cRef(1)
  1124. {
  1125. _psf = psf;
  1126. psf->AddRef();
  1127. _scid = scid;
  1128. _hash = CreateHashItemTable(10, sizeof(DWORD));
  1129. _hdpaKeys = DPA_Create(10);
  1130. }
  1131. CDetailCategorizer::~CDetailCategorizer()
  1132. {
  1133. _psf->Release();
  1134. DestroyHashItemTable(_hash);
  1135. DPA_Destroy(_hdpaKeys);
  1136. }
  1137. HRESULT CDetailCategorizer::QueryInterface(REFIID riid, void **ppv)
  1138. {
  1139. static const QITAB qit[] =
  1140. {
  1141. QITABENT(CDetailCategorizer, ICategorizer),
  1142. { 0 },
  1143. };
  1144. return QISearch(this, qit, riid, ppv);
  1145. }
  1146. ULONG CDetailCategorizer::AddRef()
  1147. {
  1148. return InterlockedIncrement(&_cRef);
  1149. }
  1150. ULONG CDetailCategorizer::Release()
  1151. {
  1152. if (InterlockedDecrement(&_cRef))
  1153. return _cRef;
  1154. delete this;
  1155. return 0;
  1156. }
  1157. HRESULT CDetailCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  1158. {
  1159. return E_FAIL;
  1160. }
  1161. HRESULT CDetailCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
  1162. {
  1163. if (!_hash || !_hdpaKeys)
  1164. return E_OUTOFMEMORY;
  1165. for (UINT i = 0; i < cidl; i++)
  1166. {
  1167. VARIANT v;
  1168. rgCategoryIds[i] = GROUPID_UNSPECIFIED;
  1169. HRESULT hr = _psf->GetDetailsEx(apidl[i], &_scid, &v);
  1170. if (hr == S_OK) // GetDetails returns S_FALSE for failure.
  1171. {
  1172. WCHAR szValue[MAX_PATH];
  1173. if (SUCCEEDED(SHFormatForDisplay(_scid.fmtid, _scid.pid, (PROPVARIANT*)&v, PUIFFDF_DEFAULT, szValue, ARRAYSIZE(szValue))))
  1174. {
  1175. LPCTSTR pszKey = FindHashItem(_hash, szValue);
  1176. if (pszKey)
  1177. {
  1178. rgCategoryIds[i] = (DWORD)GetHashItemData(_hash, pszKey, 0);
  1179. }
  1180. else
  1181. {
  1182. pszKey = AddHashItem(_hash, szValue);
  1183. if (pszKey)
  1184. {
  1185. rgCategoryIds[i] = DPA_AppendPtr(_hdpaKeys, (void*)pszKey);
  1186. SetHashItemData(_hash, pszKey, 0, rgCategoryIds[i]);
  1187. }
  1188. }
  1189. }
  1190. VariantClear(&v);
  1191. }
  1192. }
  1193. return S_OK;
  1194. }
  1195. HRESULT CDetailCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  1196. {
  1197. HRESULT hr = E_OUTOFMEMORY;
  1198. if (_hash|| _hdpaKeys)
  1199. {
  1200. if (dwCategoryId == GROUPID_UNSPECIFIED || dwCategoryId == GROUPID_FOLDER)
  1201. {
  1202. LoadString(HINST_THISDLL, STRINGID_FROM_GROUPID(dwCategoryId), pci->wszName, ARRAYSIZE(pci->wszName));
  1203. hr = S_OK;
  1204. }
  1205. else
  1206. {
  1207. LPCTSTR pszKey = (LPCTSTR)DPA_FastGetPtr(_hdpaKeys, dwCategoryId);
  1208. if (pszKey)
  1209. {
  1210. LPCTSTR psz = FindHashItem(_hash, pszKey);
  1211. if (psz)
  1212. {
  1213. StrCpyN(pci->wszName, psz, ARRAYSIZE(pci->wszName));
  1214. hr = S_OK;
  1215. }
  1216. }
  1217. else
  1218. {
  1219. hr = E_INVALIDARG;
  1220. }
  1221. }
  1222. }
  1223. return hr;
  1224. }
  1225. HRESULT CDetailCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  1226. {
  1227. if (dwCategoryId1 == dwCategoryId2)
  1228. {
  1229. return ResultFromShort(0);
  1230. }
  1231. else if (dwCategoryId1 == GROUPID_UNSPECIFIED ||
  1232. dwCategoryId2 == GROUPID_UNSPECIFIED)
  1233. {
  1234. return ResultFromShort((dwCategoryId1 == GROUPID_UNSPECIFIED)? 1 : -1);
  1235. }
  1236. else if (dwCategoryId1 == GROUPID_FOLDER)
  1237. {
  1238. return ResultFromShort(-1);
  1239. }
  1240. else if (dwCategoryId2 == GROUPID_FOLDER)
  1241. {
  1242. return ResultFromShort(1);
  1243. }
  1244. else
  1245. {
  1246. LPCTSTR pszKey1 = (LPCTSTR)DPA_FastGetPtr(_hdpaKeys, dwCategoryId1);
  1247. LPCTSTR pszKey2 = (LPCTSTR)DPA_FastGetPtr(_hdpaKeys, dwCategoryId2);
  1248. LPCTSTR psz1 = FindHashItem(_hash, pszKey1);
  1249. LPCTSTR psz2 = FindHashItem(_hash, pszKey2);
  1250. return ResultFromShort(lstrcmpi(psz1, psz2));
  1251. }
  1252. }
  1253. /////////////////////////////////////////////////////////
  1254. // Alphanumeric Categorizer
  1255. STDAPI CAlphaCategorizer_Create(IShellFolder2* psf2, REFIID riid, void **ppv)
  1256. {
  1257. HRESULT hr = E_OUTOFMEMORY;
  1258. CAlphaCategorizer* p = new CAlphaCategorizer(psf2);
  1259. if (p)
  1260. {
  1261. hr = p->QueryInterface(riid, ppv);
  1262. p->Release();
  1263. }
  1264. return hr;
  1265. }
  1266. CAlphaCategorizer::CAlphaCategorizer(IShellFolder2* psf) : _cRef(1)
  1267. {
  1268. _psf = psf;
  1269. ASSERT(psf);
  1270. psf->AddRef();
  1271. }
  1272. CAlphaCategorizer::~CAlphaCategorizer()
  1273. {
  1274. _psf->Release();
  1275. }
  1276. HRESULT CAlphaCategorizer::QueryInterface(REFIID riid, void **ppv)
  1277. {
  1278. static const QITAB qit[] =
  1279. {
  1280. QITABENT(CAlphaCategorizer, ICategorizer),
  1281. { 0 },
  1282. };
  1283. return QISearch(this, qit, riid, ppv);
  1284. }
  1285. ULONG CAlphaCategorizer::AddRef()
  1286. {
  1287. return InterlockedIncrement(&_cRef);
  1288. }
  1289. ULONG CAlphaCategorizer::Release()
  1290. {
  1291. if (InterlockedDecrement(&_cRef))
  1292. return _cRef;
  1293. delete this;
  1294. return 0;
  1295. }
  1296. HRESULT CAlphaCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  1297. {
  1298. LoadString(HINST_THISDLL, IDS_GROUPALPHABETICALLY, pszDesc, cch);
  1299. return S_OK;
  1300. }
  1301. HRESULT CAlphaCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds)
  1302. {
  1303. if (_psf == NULL)
  1304. return E_ACCESSDENIED; // Not initialized yet.
  1305. for (UINT i = 0; i < cidl; i++)
  1306. {
  1307. TCHAR szName[MAX_PATH];
  1308. HRESULT hr = DisplayNameOf(_psf, apidl[i], SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1309. if (SUCCEEDED(hr))
  1310. {
  1311. if (StrChr(TEXT("~`!@#$%^&*()_-+=1234567890<,>.;:'[]{}|"), szName[0]) != NULL)
  1312. {
  1313. rgCategoryIds[i] = GROUPID_OTHER;
  1314. }
  1315. else
  1316. {
  1317. CharUpperBuff(szName, 1);
  1318. rgCategoryIds[i] = (DWORD)szName[0];
  1319. }
  1320. }
  1321. if (FAILED(hr))
  1322. rgCategoryIds[i] = GROUPID_UNSPECIFIED;
  1323. }
  1324. return S_OK;
  1325. }
  1326. HRESULT CAlphaCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  1327. {
  1328. if (GROUPID_UNSPECIFIED == dwCategoryId ||
  1329. GROUPID_OTHER == dwCategoryId ||
  1330. GROUPID_FOLDER == dwCategoryId)
  1331. {
  1332. LoadString(HINST_THISDLL, STRINGID_FROM_GROUPID(dwCategoryId), pci->wszName, ARRAYSIZE(pci->wszName));
  1333. return S_OK;
  1334. }
  1335. else
  1336. {
  1337. pci->wszName[0] = (WCHAR)dwCategoryId;
  1338. pci->wszName[1] = 0;
  1339. return S_OK;
  1340. }
  1341. }
  1342. HRESULT CAlphaCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  1343. {
  1344. if (dwCategoryId1 == dwCategoryId2)
  1345. {
  1346. return ResultFromShort(0);
  1347. }
  1348. else if (IDS_UNSPECIFIED == dwCategoryId1 || IDS_GROUPOTHERCHAR == dwCategoryId1)
  1349. {
  1350. return ResultFromShort(1);
  1351. }
  1352. else if (IDS_UNSPECIFIED == dwCategoryId2 || IDS_GROUPOTHERCHAR == dwCategoryId2)
  1353. {
  1354. return ResultFromShort(-1);
  1355. }
  1356. else if (dwCategoryId1 == IDS_GROUPFOLDERS)
  1357. return ResultFromShort(-1);
  1358. else if (dwCategoryId2 == IDS_GROUPFOLDERS)
  1359. return ResultFromShort(1);
  1360. else
  1361. {
  1362. TCHAR szName1[2] = {(WCHAR)dwCategoryId1, 0};
  1363. TCHAR szName2[2] = {(WCHAR)dwCategoryId2, 0};
  1364. return ResultFromShort(lstrcmpi(szName1, szName2));
  1365. }
  1366. }