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.

5486 lines
170 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "clsobj.h"
  4. #include "ids.h"
  5. #include <cowsite.h>
  6. #include "datautil.h"
  7. #include "idhidden.h"
  8. #include "prop.h"
  9. #include "stgutil.h"
  10. #include "sfstorage.h"
  11. #include "util.h"
  12. #include "fstreex.h"
  13. #include "basefvcb.h"
  14. #include "category.h"
  15. #include "mergfldr.h"
  16. #include "filefldr.h"
  17. #include "idldata.h"
  18. #include "defcm.h"
  19. #define TF_AUGM 0x10000000
  20. // pidl wrapper contains a TAG word for validation and then
  21. // the count for the number of packaged pidl object.
  22. //
  23. // each pidl has a hidden payload which is the name space
  24. // index that it originated from.
  25. #pragma pack(1)
  26. typedef struct
  27. {
  28. USHORT cb; // pidl wrap length
  29. USHORT dwFlags; // flags
  30. ULONG ulTag; // signature
  31. ULONG ulVersion ; // AugMergeISF pidl version
  32. ULONG cSrcs; // Number of source _Namespace objects backing this composite pidl
  33. } AUGM_IDWRAP;
  34. typedef UNALIGNED AUGM_IDWRAP *PAUGM_IDWRAP;
  35. typedef struct
  36. {
  37. HIDDENITEMID hid;
  38. UINT uSrcID; // src _Namespace
  39. } AUGM_NAMESPACE;
  40. typedef UNALIGNED AUGM_NAMESPACE *PAUGM_NAMESPACE;
  41. #pragma pack()
  42. #define AUGM_NS_CURRENTVERSION 0
  43. #define AUGM_WRAPVERSION_1_0 MAKELONG(1, 0)
  44. #define AUGM_WRAPVERSION_2_0 MAKELONG(2, 0)
  45. #define AUGM_WRAPCURRENTVERSION AUGM_WRAPVERSION_2_0
  46. #define AUGM_WRAPTAG MAKELONG(MAKEWORD('A','u'), MAKEWORD('g','M'))
  47. #define CB_IDLIST_TERMINATOR sizeof(USHORT)
  48. // dwFlags field flags
  49. #define AUGMF_ISSIMPLE 0x0001
  50. // helpers.
  51. HRESULT CMergedFldrContextMenu_CreateInstance(HWND hwnd, CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl, IContextMenu *pcmCommon, IContextMenu *pcmUser, IContextMenu **ppcm);
  52. HRESULT CMergedFldrEnum_CreateInstance(CMergedFolder*pmf, DWORD grfFlags, IEnumIDList **ppenum);
  53. HRESULT CMergedFldrDropTarget_CreateInstance(CMergedFolder*pmf, HWND hwnd, IDropTarget **ppdt);
  54. HRESULT CMergedFolderViewCB_CreateInstance(CMergedFolder *pmf, IShellFolderViewCB **ppsfvcb);
  55. // Helper function that spans all objects
  56. BOOL AffectAllUsers(HWND hwnd)
  57. {
  58. BOOL bRet = FALSE; // default to NO
  59. if (hwnd)
  60. {
  61. TCHAR szMessage[255];
  62. TCHAR szTitle[20];
  63. if (LoadString(HINST_THISDLL, IDS_ALLUSER_WARNING, szMessage, ARRAYSIZE(szMessage)) > 0 &&
  64. LoadString(HINST_THISDLL, IDS_WARNING, szTitle, ARRAYSIZE(szTitle)) > 0)
  65. {
  66. bRet = (IDYES == MessageBox(hwnd, szMessage, szTitle, MB_YESNO | MB_ICONINFORMATION));
  67. }
  68. }
  69. else
  70. bRet = TRUE; // NULL hwnd implies NO UI, say "yes"
  71. return bRet;
  72. }
  73. // CMergedFoldersource _Namespace descriptor.
  74. //
  75. // Objects of class CMergedFldrNamespace are created by CMergedFolderin
  76. // the AddNameSpace() method impl, and are maintained in the collection
  77. // CMergedFolder::_hdpaNamespaces.
  78. //
  79. class CMergedFldrNamespace
  80. {
  81. public:
  82. CMergedFldrNamespace();
  83. ~CMergedFldrNamespace();
  84. IShellFolder* Folder() const
  85. { return _psf; }
  86. REFGUID GetGUID() const
  87. { return _guid; }
  88. ULONG FolderAttrib() const
  89. { return _dwAttrib; }
  90. LPCITEMIDLIST GetIDList() const
  91. { return _pidl; }
  92. HRESULT GetLocation(LPWSTR pszBuffer, INT cchBuffer)
  93. { StrCpyN(pszBuffer, _szLocation, cchBuffer); return S_OK; };
  94. LPCWSTR GetDropFolder()
  95. { return _szDrop; };
  96. ULONG FixItemAttributes(ULONG attrib)
  97. { return (attrib & _dwItemAttribMask) | _dwItemAttrib; }
  98. DWORD GetDropEffect(void) const
  99. { return _dwDropEffect; }
  100. int GetDefaultOverlayIndex() const
  101. { return _iDefaultOverlayIndex; }
  102. int GetConflictOverlayIndex() const
  103. { return _iConflictOverlayIndex; }
  104. int GetNamespaceOverlayIndex(LPCITEMIDLIST pidl);
  105. HRESULT SetNamespace(const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib);
  106. HRESULT SetDropFolder(LPCWSTR pszDrop);
  107. HRESULT RegisterNotify(HWND, UINT, ULONG);
  108. HRESULT UnregisterNotify();
  109. BOOL SetOwner(IUnknown *punk);
  110. protected:
  111. ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive);
  112. void _ReleaseNamespace();
  113. IShellFolder* _psf; // IShellFolder interface pointer
  114. GUID _guid; // optional GUID for specialized UI handling
  115. LPITEMIDLIST _pidl; // optional pidl
  116. ULONG _dwAttrib; // optional flags
  117. UINT _uChangeReg; // Shell change notify registration ID.
  118. WCHAR _szLocation[MAX_PATH]; // Location to use for object
  119. WCHAR _szDrop[MAX_PATH]; // folder that gets the forced drop effect
  120. DWORD _dwItemAttrib; // OR mask for the attributes
  121. DWORD _dwItemAttribMask; // AND mask for the attributes
  122. DWORD _dwDropEffect; // default drop effect for this folder
  123. int _iDefaultOverlayIndex; // overlay icon index for default
  124. int _iConflictOverlayIndex; // overlay icon index if the name exists in another namespace
  125. };
  126. CMergedFldrNamespace::CMergedFldrNamespace() :
  127. _dwItemAttribMask(-1)
  128. {
  129. }
  130. inline CMergedFldrNamespace::~CMergedFldrNamespace()
  131. {
  132. UnregisterNotify();
  133. _ReleaseNamespace();
  134. }
  135. HRESULT CMergedFldrNamespace::SetNamespace(const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib)
  136. {
  137. _ReleaseNamespace();
  138. // store the IShellFolder object if we have one
  139. if (psf)
  140. {
  141. _psf = psf;
  142. _psf->AddRef();
  143. }
  144. else if (pidl)
  145. {
  146. SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, &_psf));
  147. }
  148. // get the IDLIST that this namespace represnets
  149. if (pidl)
  150. {
  151. _pidl = ILClone(pidl); // we have a PIDL passed to us.
  152. }
  153. else
  154. {
  155. _pidl = NULL;
  156. IPersistFolder3 *ppf3;
  157. if (SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
  158. {
  159. PERSIST_FOLDER_TARGET_INFO pfti;
  160. if (SUCCEEDED(ppf3->GetFolderTargetInfo(&pfti)))
  161. {
  162. _pidl = pfti.pidlTargetFolder;
  163. }
  164. ppf3->Release();
  165. }
  166. // if it doesnt have IPersistFolder3 or if there's no target folder then
  167. // fall back to IPersistFolder2
  168. if (!_pidl)
  169. {
  170. SHGetIDListFromUnk(psf, &_pidl);
  171. }
  172. }
  173. if (!_psf || !_pidl)
  174. return E_FAIL;
  175. // now fill out the information about the namespace, including getting the display
  176. // information from the registry
  177. _guid = pguidUIObject ? *pguidUIObject : GUID_NULL;
  178. _dwAttrib = dwAttrib;
  179. _szLocation[0] = TEXT('\0');
  180. _dwItemAttrib = 0; // item attribute become a NOP
  181. _dwItemAttribMask = (DWORD)-1;
  182. _dwDropEffect = 0; // default behaviour
  183. _iDefaultOverlayIndex = -1;
  184. _iConflictOverlayIndex = -1;
  185. // format a key to the property bag stored in the registry, then create the
  186. // property bag which we then query against.
  187. TCHAR szKey[MAX_PATH], szGUID[GUIDSTR_MAX+1];
  188. SHStringFromGUID(_guid, szGUID, ARRAYSIZE(szGUID));
  189. wsprintf(szKey, TEXT("CLSID\\%s\\MergedFolder"), szGUID);
  190. IPropertyBag *ppb;
  191. if (SUCCEEDED(SHCreatePropertyBagOnRegKey(HKEY_CLASSES_ROOT, szKey, STGM_READ, IID_PPV_ARG(IPropertyBag, &ppb))))
  192. {
  193. TCHAR szLocalized[100];
  194. if (SUCCEEDED(SHPropertyBag_ReadStr(ppb, L"Location", szLocalized, ARRAYSIZE(szLocalized))))
  195. {
  196. SHLoadIndirectString(szLocalized, _szLocation, ARRAYSIZE(_szLocation), NULL);
  197. }
  198. SHPropertyBag_ReadDWORD(ppb, L"Attributes", &_dwItemAttrib);
  199. SHPropertyBag_ReadDWORD(ppb, L"AttributeMask", &_dwItemAttribMask);
  200. SHPropertyBag_ReadDWORD(ppb, L"DropEffect", &_dwDropEffect);
  201. TCHAR szIconLocation[MAX_PATH];
  202. szIconLocation[0] = 0;
  203. SHPropertyBag_ReadStr(ppb, L"DefaultOverlayIcon", szIconLocation, ARRAYSIZE(szIconLocation));
  204. _iDefaultOverlayIndex = SHGetIconOverlayIndex(szIconLocation, PathParseIconLocation(szIconLocation));
  205. szIconLocation[0] = 0;
  206. SHPropertyBag_ReadStr(ppb, L"ConflictOverlayIcon", szIconLocation, ARRAYSIZE(szIconLocation));
  207. _iConflictOverlayIndex = SHGetIconOverlayIndex(szIconLocation, PathParseIconLocation(szIconLocation));
  208. ppb->Release();
  209. }
  210. if (!SHGetPathFromIDList(_pidl, _szDrop))
  211. {
  212. _szDrop[0] = 0;
  213. }
  214. return S_OK;
  215. }
  216. HRESULT CMergedFldrNamespace::SetDropFolder(LPCWSTR pszDrop)
  217. {
  218. StrCpyN(_szDrop, pszDrop, ARRAYSIZE(_szDrop));
  219. return S_OK;
  220. }
  221. void CMergedFldrNamespace::_ReleaseNamespace()
  222. {
  223. ATOMICRELEASE(_psf);
  224. ILFree(_pidl);
  225. _pidl = NULL;
  226. _guid = GUID_NULL;
  227. _dwAttrib = 0L;
  228. }
  229. ULONG CMergedFldrNamespace::_RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive)
  230. {
  231. SHChangeNotifyEntry fsne = { 0 };
  232. fsne.fRecursive = fRecursive;
  233. fsne.pidl = pidl;
  234. return SHChangeNotifyRegister(hwnd, uFlags | SHCNRF_NewDelivery, dwEvents, nMsg, 1, &fsne);
  235. }
  236. // Register change notification for the _Namespace
  237. HRESULT CMergedFldrNamespace::RegisterNotify(HWND hwnd, UINT uMsg, ULONG lEvents)
  238. {
  239. if (0 == _uChangeReg)
  240. {
  241. _uChangeReg = _RegisterNotify(hwnd, uMsg, _pidl, lEvents,
  242. SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt,
  243. TRUE);
  244. }
  245. return 0 != _uChangeReg ? S_OK : E_FAIL;
  246. }
  247. // Unregister change notification for the _Namespace
  248. HRESULT CMergedFldrNamespace::UnregisterNotify()
  249. {
  250. if (_uChangeReg)
  251. {
  252. ::SHChangeNotifyDeregister(_uChangeReg);
  253. _uChangeReg = 0;
  254. }
  255. return S_OK;
  256. }
  257. inline BOOL CMergedFldrNamespace::SetOwner(IUnknown *punkOwner)
  258. {
  259. if (!_psf)
  260. return FALSE;
  261. IUnknown_SetOwner(_psf, punkOwner);
  262. return TRUE;
  263. }
  264. int CMergedFldrNamespace::GetNamespaceOverlayIndex(LPCITEMIDLIST pidl)
  265. {
  266. int iIndex = -1;
  267. if (_psf)
  268. {
  269. IShellIconOverlay *psio;
  270. if (SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &psio))))
  271. {
  272. psio->GetOverlayIndex(pidl, &iIndex);
  273. psio->Release();
  274. }
  275. }
  276. return iIndex;
  277. }
  278. // object which takes ownership of the IDLIST and handles wrapping and returning information from it.
  279. class CMergedFldrItem
  280. {
  281. public:
  282. ~CMergedFldrItem();
  283. BOOL Init(IShellFolder* psf, LPITEMIDLIST pidl, int iNamespace);
  284. BOOL Init(CMergedFldrItem *pmfi);
  285. BOOL SetDisplayName(LPTSTR pszDispName)
  286. { return Str_SetPtr(&_pszDisplayName, pszDispName); }
  287. ULONG GetFolderAttrib()
  288. { return _rgfAttrib; }
  289. LPTSTR GetDisplayName()
  290. { return _pszDisplayName; }
  291. LPITEMIDLIST GetIDList()
  292. { return _pidlWrap; }
  293. int GetNamespaceID()
  294. { return _iNamespace; }
  295. private:
  296. ULONG _rgfAttrib;
  297. LPTSTR _pszDisplayName;
  298. LPITEMIDLIST _pidlWrap;
  299. int _iNamespace;
  300. friend CMergedFolder;
  301. };
  302. CMergedFldrItem::~CMergedFldrItem()
  303. {
  304. Str_SetPtr(&_pszDisplayName, NULL);
  305. ILFree(_pidlWrap);
  306. }
  307. BOOL CMergedFldrItem::Init(CMergedFldrItem *pmfi)
  308. {
  309. _iNamespace = pmfi->_iNamespace;
  310. _pidlWrap = ILClone(pmfi->GetIDList());
  311. BOOL fRet = (_pidlWrap != NULL);
  312. if (fRet)
  313. {
  314. fRet = SetDisplayName(pmfi->GetDisplayName());
  315. _rgfAttrib = pmfi->GetFolderAttrib();
  316. }
  317. return fRet;
  318. }
  319. BOOL CMergedFldrItem::Init(IShellFolder* psf, LPITEMIDLIST pidl, int iNamespace)
  320. {
  321. BOOL fRet = FALSE;
  322. _pidlWrap = pidl; // evil, hold an alias
  323. _rgfAttrib = SFGAO_FOLDER | SFGAO_HIDDEN;
  324. _iNamespace = iNamespace;
  325. if (SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &_rgfAttrib)))
  326. {
  327. TCHAR szDisplayName[MAX_PATH];
  328. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szDisplayName, ARRAYSIZE(szDisplayName))))
  329. {
  330. fRet = SetDisplayName(szDisplayName);
  331. }
  332. }
  333. return fRet;
  334. }
  335. // shell folder object.
  336. CMergedFolder::CMergedFolder(CMergedFolder *pmfParent, REFCLSID clsid) :
  337. _clsid(clsid),
  338. _cRef(1),
  339. _pmfParent(pmfParent),
  340. _iColumnOffset(-1)
  341. {
  342. ASSERT(_hdpaNamespaces == NULL);
  343. if (_pmfParent)
  344. {
  345. _pmfParent->AddRef();
  346. _fDontMerge = _pmfParent->_fDontMerge;
  347. _fCDBurn = _pmfParent->_fCDBurn;
  348. _fInShellView = _pmfParent->_fInShellView;
  349. _dwDropEffect = _pmfParent->_dwDropEffect;
  350. }
  351. else
  352. {
  353. _fDontMerge = IsEqualCLSID(_clsid, CLSID_CompositeFolder);
  354. _fCDBurn = IsEqualCLSID(_clsid, CLSID_CDBurnFolder);
  355. }
  356. DllAddRef();
  357. }
  358. CMergedFolder::~CMergedFolder()
  359. {
  360. SetOwner(NULL);
  361. ILFree(_pidl);
  362. _FreeNamespaces();
  363. _FreeObjects();
  364. ATOMICRELEASE(_pmfParent);
  365. ATOMICRELEASE(_pstg);
  366. DllRelease();
  367. }
  368. // CMergedFolderglobal CreateInstance method for da class factory
  369. HRESULT CMergedFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  370. {
  371. // aggregation checking is handled in class factory
  372. HRESULT hr = E_OUTOFMEMORY;
  373. CMergedFolder* pmf = new CMergedFolder(NULL, CLSID_MergedFolder);
  374. if (pmf)
  375. {
  376. hr = pmf->QueryInterface(riid, ppv);
  377. pmf->Release();
  378. }
  379. return hr;
  380. }
  381. HRESULT CCompositeFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  382. {
  383. // aggregation checking is handled in class factory
  384. HRESULT hr = E_OUTOFMEMORY;
  385. CMergedFolder* pmf = new CMergedFolder(NULL, CLSID_CompositeFolder);
  386. if (pmf)
  387. {
  388. hr = pmf->QueryInterface(riid, ppv);
  389. pmf->Release();
  390. }
  391. return hr;
  392. }
  393. #ifdef TESTING_COMPOSITEFOLDER
  394. COMPFOLDERINIT s_rgcfiTripleD[] = {
  395. {CFITYPE_CSIDL, CSIDL_DRIVES, L"Drives"},
  396. {CFITYPE_PIDL, (int)&c_idlDesktop, L"Desktop"},
  397. {CFITYPE_PATH, (int)L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", L"MyDocs"}
  398. };
  399. STDAPI CTripleD_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  400. {
  401. ICompositeFolder *pcf;
  402. HRESULT hr = CCompositeFolder_CreateInstance(punkOuter, IID_PPV_ARG(ICompositeFolder, &pcf));
  403. if (SUCCEEDED(hr))
  404. {
  405. hr = pcf->InitComposite(0x8877, CLSID_TripleD, CFINITF_FLAT, ARRAYSIZE(s_rgcfiTripleD), s_rgcfiTripleD);
  406. if (SUCCEEDED(hr))
  407. {
  408. hr = pcf->QueryInterface(riid, ppv);
  409. }
  410. pcf->Release();
  411. }
  412. return hr;
  413. }
  414. #endif //TESTING_COMPOSITEFOLDER
  415. STDMETHODIMP CMergedFolder::QueryInterface(REFIID riid, void **ppv)
  416. {
  417. static const QITAB qit[] = {
  418. QITABENTMULTI(CMergedFolder, IShellFolder, IAugmentedShellFolder),
  419. QITABENT (CMergedFolder, IAugmentedShellFolder),
  420. QITABENT (CMergedFolder, IAugmentedShellFolder2),
  421. QITABENT (CMergedFolder, IAugmentedShellFolder3),
  422. QITABENT (CMergedFolder, IShellFolder2),
  423. QITABENT (CMergedFolder, IShellService),
  424. QITABENT (CMergedFolder, ITranslateShellChangeNotify),
  425. QITABENT (CMergedFolder, IStorage),
  426. QITABENT (CMergedFolder, IShellIconOverlay),
  427. QITABENTMULTI(CMergedFolder, IPersist, IPersistFolder2),
  428. QITABENTMULTI(CMergedFolder, IPersistFolder, IPersistFolder2),
  429. QITABENT (CMergedFolder, IPersistFolder2),
  430. QITABENT (CMergedFolder, IPersistPropertyBag),
  431. QITABENT (CMergedFolder, ICompositeFolder),
  432. QITABENT (CMergedFolder, IItemNameLimits),
  433. { 0 },
  434. };
  435. if (IsEqualIID(CLSID_MergedFolder, riid))
  436. {
  437. *ppv = this;
  438. AddRef();
  439. return S_OK;
  440. }
  441. return QISearch(this, qit, riid, ppv);
  442. }
  443. STDMETHODIMP_(ULONG) CMergedFolder::AddRef()
  444. {
  445. return InterlockedIncrement(&_cRef);
  446. }
  447. STDMETHODIMP_(ULONG) CMergedFolder::Release()
  448. {
  449. if (InterlockedDecrement(&_cRef))
  450. return _cRef;
  451. delete this;
  452. return 0;
  453. }
  454. // get the count from the enumerated items.
  455. int CMergedFolder::_ObjectCount() const
  456. {
  457. return _hdpaObjects ? DPA_GetPtrCount(_hdpaObjects) : 0;
  458. }
  459. CMergedFldrItem *CMergedFolder::_GetObject(int i)
  460. {
  461. return _hdpaObjects ? (CMergedFldrItem *)DPA_GetPtr(_hdpaObjects, i) : NULL;
  462. }
  463. int CMergedFolder::_NamespaceCount() const
  464. {
  465. return _hdpaNamespaces ? DPA_GetPtrCount(_hdpaNamespaces) : 0;
  466. }
  467. // Retrieves a pointer to a source _Namespace descriptor associated with
  468. // the specified lookup index.
  469. HRESULT CMergedFolder::_Namespace(int iIndex, CMergedFldrNamespace **ppns)
  470. {
  471. *ppns = NULL;
  472. if ((iIndex >= 0) && (iIndex < _NamespaceCount()))
  473. *ppns = _Namespace(iIndex);
  474. return *ppns ? S_OK : E_INVALIDARG;
  475. }
  476. // given an index for the name space return it.
  477. CMergedFldrNamespace* CMergedFolder::_Namespace(int iNamespace)
  478. {
  479. if (!_hdpaNamespaces)
  480. return NULL;
  481. return (CMergedFldrNamespace*)DPA_GetPtr(_hdpaNamespaces, iNamespace);
  482. }
  483. // Determine whether pidls from the two namespaces should be merged
  484. // The NULL namespace is a wildcard that always merges (if merging is permitted at all)
  485. BOOL CMergedFolder::_ShouldMergeNamespaces(CMergedFldrNamespace *pns1, CMergedFldrNamespace *pns2)
  486. {
  487. // Early-out: Identical namespaces can be merged (even if merging
  488. // is globally disabled)
  489. if (pns1 == pns2)
  490. {
  491. return TRUE;
  492. }
  493. // Early-out: Merging globally disabled
  494. if (_fDontMerge)
  495. {
  496. return FALSE;
  497. }
  498. // Early-out: Merging globally enabled
  499. if (!_fPartialMerge)
  500. {
  501. return TRUE;
  502. }
  503. if (!pns1 || !pns2)
  504. {
  505. return TRUE; // wildcard
  506. }
  507. if (!(pns1->FolderAttrib() & ASFF_MERGESAMEGUID))
  508. {
  509. // this namespace will merge with anybody!
  510. return TRUE;
  511. }
  512. // Source namespace will merge only with namespaces of the same GUID
  513. // See if destination namespace has the same GUID
  514. return IsEqualGUID(pns1->GetGUID(), pns2->GetGUID());
  515. }
  516. // Determine whether pidls from the two namespaces should be merged
  517. // NAmespace -1 is a wildcard that always merges (if merging is permitted at all)
  518. BOOL CMergedFolder::_ShouldMergeNamespaces(int iNS1, int iNS2)
  519. {
  520. // Early-out: Merging globally disabled
  521. if (_fDontMerge)
  522. {
  523. return FALSE;
  524. }
  525. // Early-out: Merging globally enabled
  526. if (!_fPartialMerge)
  527. {
  528. return TRUE;
  529. }
  530. if (iNS1 < 0 || iNS2 < 0)
  531. {
  532. return TRUE; // wildcard
  533. }
  534. return _ShouldMergeNamespaces(_Namespace(iNS1), _Namespace(iNS2));
  535. }
  536. // check to see if the IDLIST we are given is a wrapped one.
  537. HRESULT CMergedFolder::_IsWrap(LPCITEMIDLIST pidl)
  538. {
  539. HRESULT hr = E_INVALIDARG;
  540. if (pidl)
  541. {
  542. ASSERT(IS_VALID_PIDL(pidl));
  543. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidl;
  544. if ((pWrap->cb >= sizeof(AUGM_IDWRAP)) &&
  545. (pWrap->ulTag == AUGM_WRAPTAG) &&
  546. (pWrap->ulVersion == AUGM_WRAPVERSION_2_0))
  547. {
  548. hr = S_OK;
  549. }
  550. else if (ILFindHiddenID(pidl, IDLHID_PARENTFOLDER))
  551. {
  552. hr = S_OK;
  553. }
  554. }
  555. return hr;
  556. }
  557. // STRRET_OFFSET has no meaning in context of the pidl wrapper.
  558. // We can either calculate the offset into the wrapper, or allocate
  559. // a wide char for the name. For expedience, we'll allocate the name.
  560. HRESULT CMergedFolder::_FixStrRetOffset(LPCITEMIDLIST pidl, STRRET *psr)
  561. {
  562. HRESULT hr = S_OK;
  563. if (psr->uType == STRRET_OFFSET)
  564. {
  565. UINT cch = lstrlenA(STRRET_OFFPTR(pidl, psr));
  566. LPWSTR pwszName = (LPWSTR)SHAlloc((cch + 1) * sizeof(WCHAR));
  567. if (pwszName)
  568. {
  569. SHAnsiToUnicode(STRRET_OFFPTR(pidl, psr), pwszName, cch + 1);
  570. pwszName[cch] = 0;
  571. psr->pOleStr = pwszName;
  572. psr->uType = STRRET_WSTR;
  573. }
  574. else
  575. hr = E_OUTOFMEMORY;
  576. }
  577. return hr;
  578. }
  579. // is the object a folder?
  580. BOOL CMergedFolder::_IsFolder(LPCITEMIDLIST pidl)
  581. {
  582. ULONG rgf = SFGAO_FOLDER | SFGAO_STREAM;
  583. return SUCCEEDED(GetAttributesOf(1, &pidl, &rgf)) && (SFGAO_FOLDER == (rgf & (SFGAO_FOLDER | SFGAO_STREAM)));
  584. }
  585. // does this IDLIST contain the common item.
  586. BOOL CMergedFolder::_ContainsCommonItem(LPCITEMIDLIST pidlWrap)
  587. {
  588. BOOL bCommonItem = FALSE;
  589. LPITEMIDLIST pidl;
  590. CMergedFldrNamespace *pns;
  591. for (UINT i = 0; !bCommonItem && SUCCEEDED(_GetSubPidl(pidlWrap, i, NULL, &pidl, &pns)); i++)
  592. {
  593. bCommonItem = (pns->FolderAttrib() & ASFF_COMMON);
  594. ILFree(pidl);
  595. }
  596. return bCommonItem;
  597. }
  598. // the number of source _Namespace pidls in the wrap.
  599. ULONG CMergedFolder::_GetSourceCount(LPCITEMIDLIST pidl)
  600. {
  601. if (SUCCEEDED(_IsWrap(pidl)))
  602. {
  603. if (ILFindHiddenID(pidl, IDLHID_PARENTFOLDER))
  604. {
  605. return 1;
  606. }
  607. else
  608. {
  609. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidl;
  610. return pWrap->cSrcs;
  611. }
  612. }
  613. return 0;
  614. }
  615. // Creates an IDLIST for CMergedFolder that wraps a single source pidl.
  616. HRESULT CMergedFolder::_CreateWrap(LPCITEMIDLIST pidlSrc, UINT nSrcID, LPITEMIDLIST *ppidlWrap)
  617. {
  618. *ppidlWrap = NULL; // incase of failure
  619. LPITEMIDLIST pidlSrcWithID;
  620. HRESULT hr = SHILClone(pidlSrc, &pidlSrcWithID);
  621. if (SUCCEEDED(hr))
  622. {
  623. hr = E_OUTOFMEMORY;
  624. if (!ILFindHiddenID(pidlSrcWithID, IDLHID_PARENTFOLDER))
  625. {
  626. AUGM_NAMESPACE ans = { {sizeof(ans), AUGM_NS_CURRENTVERSION, IDLHID_PARENTFOLDER} , nSrcID };
  627. pidlSrcWithID = ILAppendHiddenID((LPITEMIDLIST)pidlSrcWithID, &ans.hid);
  628. }
  629. if (pidlSrcWithID)
  630. {
  631. UINT cbAlloc = sizeof(AUGM_IDWRAP) + CB_IDLIST_TERMINATOR + // header for our IDLIST
  632. pidlSrcWithID->mkid.cb + CB_IDLIST_TERMINATOR; // wrapped IDLIST
  633. AUGM_IDWRAP *pWrap = (AUGM_IDWRAP *)_ILCreate(cbAlloc);
  634. if (pWrap)
  635. {
  636. // fill out wrapped header
  637. pWrap->cb = (USHORT)(cbAlloc - CB_IDLIST_TERMINATOR);
  638. pWrap->dwFlags = 0;
  639. pWrap->ulTag = AUGM_WRAPTAG;
  640. pWrap->ulVersion = AUGM_WRAPVERSION_2_0;
  641. pWrap->cSrcs = 1;
  642. // copy the IDLIST with the hidden data into the wrapping object
  643. LPITEMIDLIST pidl = (LPITEMIDLIST)((BYTE *)pWrap + sizeof(AUGM_IDWRAP));
  644. memcpy(pidl, pidlSrcWithID, pidlSrcWithID->mkid.cb);
  645. *ppidlWrap = (LPITEMIDLIST)pWrap;
  646. hr = S_OK;
  647. }
  648. ILFree(pidlSrcWithID);
  649. }
  650. }
  651. return hr;
  652. }
  653. // does the wrapped IDLIST we are passed contain the given source ID?
  654. BOOL CMergedFolder::_ContainsSrcID(LPCITEMIDLIST pidl, UINT uSrcID)
  655. {
  656. UINT uID;
  657. for (UINT nSrc = 0; SUCCEEDED(_GetSubPidl(pidl, nSrc, &uID, NULL, NULL)); nSrc++)
  658. {
  659. if (uID == uSrcID)
  660. return TRUE;
  661. }
  662. return FALSE;
  663. }
  664. // returns new pidl in *ppidl free of nSrcID
  665. HRESULT CMergedFolder::_WrapRemoveIDList(LPITEMIDLIST pidlWrap, UINT nSrcID, LPITEMIDLIST *ppidl)
  666. {
  667. ASSERT(IS_VALID_WRITE_PTR(ppidl, LPITEMIDLIST));
  668. *ppidl = NULL;
  669. HRESULT hr = _IsWrap(pidlWrap);
  670. if (SUCCEEDED(hr))
  671. {
  672. UINT uID;
  673. LPITEMIDLIST pidl;
  674. for (UINT i = 0; SUCCEEDED(hr) && SUCCEEDED(_GetSubPidl(pidlWrap, i, &uID, &pidl, NULL)); i++)
  675. {
  676. if (uID != nSrcID)
  677. hr = _WrapAddIDList(pidl, uID, ppidl);
  678. ILFree(pidl);
  679. }
  680. }
  681. return hr;
  682. }
  683. HRESULT CMergedFolder::_WrapRemoveIDListAbs(LPITEMIDLIST pidlWrapAbs, UINT nSrcID, LPITEMIDLIST *ppidlAbs)
  684. {
  685. ASSERT(ppidlAbs);
  686. HRESULT hr = E_OUTOFMEMORY;
  687. *ppidlAbs = ILCloneParent(pidlWrapAbs);
  688. if (*ppidlAbs)
  689. {
  690. LPITEMIDLIST pidlLast;
  691. hr = _WrapRemoveIDList(ILFindLastID(pidlWrapAbs), nSrcID, &pidlLast);
  692. if (SUCCEEDED(hr))
  693. {
  694. // shilappend frees pidlLast
  695. hr = SHILAppend(pidlLast, ppidlAbs);
  696. }
  697. }
  698. return hr;
  699. }
  700. // Adds a source pidl to *ppidlWrap (IN/OUT param!)
  701. HRESULT CMergedFolder::_WrapAddIDList(LPCITEMIDLIST pidlSrc, UINT nSrcID, LPITEMIDLIST* ppidlWrap)
  702. {
  703. HRESULT hr;
  704. if (!*ppidlWrap)
  705. {
  706. // called as a create, rather than append
  707. hr = _CreateWrap(pidlSrc, nSrcID, ppidlWrap);
  708. }
  709. else
  710. {
  711. // check to see if we already have the ID in this IDLIST we are wrapping onto.
  712. LPITEMIDLIST pidlSrcWithID;
  713. hr = SHILClone(pidlSrc, &pidlSrcWithID);
  714. if (SUCCEEDED(hr))
  715. {
  716. hr = E_OUTOFMEMORY;
  717. if (!ILFindHiddenID(pidlSrcWithID, IDLHID_PARENTFOLDER))
  718. {
  719. AUGM_NAMESPACE ans = { {sizeof(ans), AUGM_NS_CURRENTVERSION, IDLHID_PARENTFOLDER} , nSrcID };
  720. pidlSrcWithID = ILAppendHiddenID((LPITEMIDLIST)pidlSrcWithID, &ans.hid);
  721. }
  722. // ok, we have an IDLIST that we can use to append to this object.
  723. if (pidlSrcWithID)
  724. {
  725. BOOL fOtherSrcIDsExist = TRUE;
  726. // check to see if this ID already exists within the wrap idlist.
  727. if (*ppidlWrap && _ContainsSrcID(*ppidlWrap, nSrcID))
  728. {
  729. LPITEMIDLIST pidlFree = *ppidlWrap;
  730. if (SUCCEEDED(_WrapRemoveIDList(pidlFree, nSrcID, ppidlWrap)))
  731. {
  732. ILFree(pidlFree);
  733. }
  734. fOtherSrcIDsExist = (*ppidlWrap != NULL);
  735. }
  736. if (fOtherSrcIDsExist)
  737. {
  738. // now compute the new size of the IDLIST. (*ppidlWrap has been updated);
  739. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)*ppidlWrap;
  740. SHORT cbOld = pWrap->cb;
  741. SHORT cbNew = cbOld + (pidlSrcWithID->mkid.cb + CB_IDLIST_TERMINATOR); // extra terminator is appended.
  742. pWrap = (PAUGM_IDWRAP)SHRealloc(pWrap, cbNew + CB_IDLIST_TERMINATOR);
  743. if (pWrap)
  744. {
  745. // copy the new idlist and its hidden payload (ensure we are terminated)
  746. memcpy(((BYTE*)pWrap)+ cbOld, pidlSrcWithID, cbNew-cbOld);
  747. *((UNALIGNED SHORT*)(((BYTE*)pWrap)+ cbNew)) = 0;
  748. pWrap->cb += cbNew-cbOld;
  749. pWrap->cSrcs++;
  750. hr = S_OK;
  751. }
  752. *ppidlWrap = (LPITEMIDLIST)pWrap;
  753. }
  754. else
  755. {
  756. hr = _CreateWrap(pidlSrc, nSrcID, ppidlWrap);
  757. }
  758. ILFree(pidlSrcWithID);
  759. }
  760. }
  761. }
  762. return hr;
  763. }
  764. // used to itterate through the sub pidls in the wrapped pidl
  765. // all out params optional
  766. //
  767. // out:
  768. // *ppidl alias into pidlWrap (nested pidl)
  769. HRESULT CMergedFolder::_GetSubPidl(LPCITEMIDLIST pidlWrap, int i, UINT *pnSrcID, LPITEMIDLIST *ppidl, CMergedFldrNamespace **ppns)
  770. {
  771. if (pnSrcID)
  772. *pnSrcID = -1;
  773. if (ppidl)
  774. *ppidl = NULL;
  775. if (ppns)
  776. *ppns = NULL;
  777. HRESULT hr = _IsWrap(pidlWrap);
  778. if (SUCCEEDED(hr))
  779. {
  780. if ((UINT)i < _GetSourceCount(pidlWrap))
  781. {
  782. PAUGM_NAMESPACE pans = (PAUGM_NAMESPACE)ILFindHiddenID(pidlWrap, IDLHID_PARENTFOLDER);
  783. if (!pans)
  784. {
  785. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidlWrap;
  786. LPITEMIDLIST pidlSrc = (LPITEMIDLIST)(((BYTE *)pWrap) + sizeof(AUGM_IDWRAP));
  787. while (i--)
  788. {
  789. // advance to next item
  790. SHORT cb = pidlSrc->mkid.cb;
  791. pidlSrc = (LPITEMIDLIST)(((BYTE *)pidlSrc) + cb + CB_IDLIST_TERMINATOR);
  792. }
  793. if (pnSrcID || ppns)
  794. {
  795. PAUGM_NAMESPACE pans = (PAUGM_NAMESPACE)ILFindHiddenID(pidlSrc, IDLHID_PARENTFOLDER);
  796. ASSERTMSG((pans != NULL), "Failed to find hidden _Namespace in pidlWrap");
  797. if (pans && pnSrcID)
  798. *pnSrcID = pans->uSrcID;
  799. if (pans && ppns)
  800. hr = _Namespace(pans->uSrcID, ppns);
  801. }
  802. if (SUCCEEDED(hr) && ppidl)
  803. {
  804. hr = SHILClone(pidlSrc, ppidl);
  805. }
  806. }
  807. else
  808. {
  809. if (pnSrcID)
  810. *pnSrcID = pans->uSrcID;
  811. if (ppns)
  812. hr = _Namespace(pans->uSrcID, ppns);
  813. if (SUCCEEDED(hr) && ppidl)
  814. {
  815. hr = SHILClone(pidlWrap, ppidl);
  816. }
  817. }
  818. }
  819. else
  820. {
  821. hr = E_INVALIDARG;
  822. }
  823. }
  824. if (SUCCEEDED(hr) && ppidl)
  825. {
  826. // we need to strip away the hidden id that marks this guy as merged.
  827. // this is because the pidl we're returning is supposed to be a child of one of our
  828. // namespaces we're merging, so it should know absolutely nothing about being merged.
  829. // these guys used to slip through and cause problems.
  830. ILRemoveHiddenID(*ppidl, IDLHID_PARENTFOLDER);
  831. }
  832. ASSERT(!ppidl || (ILFindLastID(*ppidl) == *ppidl));
  833. return hr;
  834. }
  835. // function to compare two opaque pidls.
  836. // this is helpful since in the non-merged case, there's some difficulty
  837. // getting defview to contain items with the same name. we need a way to
  838. // compare two pidls to say "yes defview, these pidls are actually different!"
  839. // note that the actual order doesn't matter, as long as the comparison
  840. // is consistent (since this is used in sorting functions).
  841. int CMergedFolder::_CompareArbitraryPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  842. {
  843. UINT iRet;
  844. UINT cbItem1 = ILGetSize(pidl1);
  845. UINT cbItem2 = ILGetSize(pidl2);
  846. if (cbItem1 != cbItem2)
  847. {
  848. iRet = (cbItem1 < cbItem2) ? 1 : -1;
  849. }
  850. else
  851. {
  852. iRet = memcmp(pidl1, pidl2, cbItem1);
  853. ASSERTMSG(iRet != 0, "no two pidls from the enumerators should be EXACTLY alike!");
  854. }
  855. return iRet;
  856. }
  857. int CMergedFolder::_Compare(void *pv1, void *pv2, LPARAM lParam)
  858. {
  859. int iRet = -1;
  860. CMergedFldrItem* pmfiEnum1 = (CMergedFldrItem*)pv1;
  861. CMergedFldrItem* pmfiEnum2 = (CMergedFldrItem*)pv2;
  862. if (pmfiEnum1 && pmfiEnum2)
  863. {
  864. // Are these two items of different types?
  865. if (BOOLIFY(pmfiEnum1->GetFolderAttrib() & SFGAO_FOLDER) ^ BOOLIFY(pmfiEnum2->GetFolderAttrib() & SFGAO_FOLDER))
  866. {
  867. // Yes. Then Folders sort before items.
  868. iRet = BOOLIFY(pmfiEnum1->GetFolderAttrib() & SFGAO_FOLDER) ? 1 : -1;
  869. }
  870. else // They are of the same type. Then compare by name
  871. {
  872. iRet = lstrcmpi(pmfiEnum1->GetDisplayName(), pmfiEnum2->GetDisplayName());
  873. if (iRet == 0)
  874. {
  875. CMergedFolder *pmf = (CMergedFolder *) lParam;
  876. if (!pmf->_ShouldMergeNamespaces(pmfiEnum1->GetNamespaceID(), pmfiEnum2->GetNamespaceID()))
  877. {
  878. // these items cannot be merged,
  879. // force iRet to be nonzero. the only reason why this comparison
  880. // has to be well-defined is so we can pass our ASSERTs that the
  881. // list is sorted using this comparison function.
  882. iRet = _CompareArbitraryPidls(pmfiEnum1->GetIDList(), pmfiEnum2->GetIDList());
  883. }
  884. }
  885. }
  886. }
  887. return iRet;
  888. }
  889. void *CMergedFolder::_Merge(UINT uMsg, void *pv1, void *pv2, LPARAM lParam)
  890. {
  891. CMergedFolder*pmf = (CMergedFolder*)lParam;
  892. void * pvRet = pv1;
  893. switch (uMsg)
  894. {
  895. case DPAMM_MERGE:
  896. {
  897. UINT nSrcID;
  898. LPITEMIDLIST pidl;
  899. CMergedFldrItem* pitemSrc = (CMergedFldrItem*)pv2;
  900. if (SUCCEEDED(pmf->_GetSubPidl(pitemSrc->GetIDList(), 0, &nSrcID, &pidl, NULL)))
  901. {
  902. // add pidl from src to dest
  903. CMergedFldrItem* pitemDest = (CMergedFldrItem*)pv1;
  904. pmf->_WrapAddIDList(pidl, nSrcID, &pitemDest->_pidlWrap);
  905. ILFree(pidl);
  906. }
  907. }
  908. break;
  909. case DPAMM_INSERT:
  910. {
  911. CMergedFldrItem* pmfiNew = new CMergedFldrItem;
  912. if (pmfiNew)
  913. {
  914. CMergedFldrItem* pmfiSrc = (CMergedFldrItem*)pv1;
  915. if (!pmfiNew->Init(pmfiSrc))
  916. {
  917. delete pmfiNew;
  918. pmfiNew = NULL;
  919. }
  920. }
  921. pvRet = pmfiNew;
  922. }
  923. break;
  924. default:
  925. ASSERT(0);
  926. }
  927. return pvRet;
  928. }
  929. typedef struct
  930. {
  931. LPTSTR pszDisplayName;
  932. BOOL fFolder;
  933. CMergedFolder *self;
  934. int iNamespace;
  935. } SEARCH_FOR_PIDL;
  936. int CALLBACK CMergedFolder::_SearchByName(void *p1, void *p2, LPARAM lParam)
  937. {
  938. SEARCH_FOR_PIDL* psfp = (SEARCH_FOR_PIDL*)p1;
  939. CMergedFldrItem* pmfiEnum = (CMergedFldrItem*)p2;
  940. // Are they of different types?
  941. if (BOOLIFY(pmfiEnum->GetFolderAttrib() & SFGAO_FOLDER) ^ psfp->fFolder)
  942. {
  943. // Yes.
  944. return psfp->fFolder ? 1 : -1;
  945. }
  946. // They are of the same type. Then compare by name
  947. int iRc = StrCmpI(psfp->pszDisplayName, pmfiEnum->GetDisplayName());
  948. if (iRc)
  949. return iRc;
  950. // They are the same name. But if they're not allowed to merge, then
  951. // they're really different.
  952. if (!psfp->self->_ShouldMergeNamespaces(pmfiEnum->GetNamespaceID(), psfp->iNamespace))
  953. {
  954. // Sort by namespace ID
  955. return psfp->iNamespace - pmfiEnum->GetNamespaceID();
  956. }
  957. // I guess they're really equal
  958. return 0;
  959. }
  960. // IPersistFolder::Initialize()
  961. STDMETHODIMP CMergedFolder::Initialize(LPCITEMIDLIST pidl)
  962. {
  963. #if 0
  964. IBindCtx *pbc;
  965. if (SUCCEEDED(SHCreateSkipBindCtx(SAFECAST(this, IAugmentedShellFolder2*), &pbc)))
  966. {
  967. IPropertyBag *pbag;
  968. if (SUCCEEDED(SHBindToObjectEx(NULL, pidl, pbc, IID_PPV_ARG(IPropertyBag, &pbag))))
  969. {
  970. Load(pbag, NULL); // ignore result here
  971. pbag->Release();
  972. }
  973. pbc->Release();
  974. }
  975. #endif
  976. return Pidl_Set(&_pidl, pidl) ? S_OK : E_OUTOFMEMORY;
  977. }
  978. // IPersistFolder2::GetCurFolder()
  979. STDMETHODIMP CMergedFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  980. {
  981. if (_pidl)
  982. return SHILClone(_pidl, ppidl);
  983. else
  984. {
  985. *ppidl = NULL;
  986. return S_FALSE;
  987. }
  988. }
  989. // IPersistPropertyBag
  990. void CMergedFolder::_GetKeyForProperty(LPWSTR pszName, LPWSTR pszValue, LPWSTR pszBuffer, INT cchBuffer)
  991. {
  992. StrCpyW(pszBuffer, L"MergedFolder\\");
  993. StrCatBuffW(pszBuffer, pszName, cchBuffer);
  994. StrCatBuffW(pszBuffer, pszValue, cchBuffer);
  995. }
  996. HRESULT CMergedFolder::_AddNameSpaceFromPropertyBag(IPropertyBag *ppb, LPWSTR pszName)
  997. {
  998. WCHAR szKey[MAX_PATH];
  999. // get the path of the folder
  1000. WCHAR szPath[MAX_PATH];
  1001. LPITEMIDLIST pidl = NULL;
  1002. _GetKeyForProperty(pszName, L"Path", szKey, ARRAYSIZE(szKey));
  1003. HRESULT hr = SHPropertyBag_ReadStr(ppb, szKey, szPath, ARRAYSIZE(szPath));
  1004. if (SUCCEEDED(hr))
  1005. {
  1006. // we picked a path from the property bag, so lets convert
  1007. // that to an IDLIST so we can do something with it.
  1008. hr = SHILCreateFromPath(szPath, &pidl, NULL);
  1009. }
  1010. else
  1011. {
  1012. // attempt to determine the CSIDL for the folder we are going
  1013. // to show, if that works then convert it to an IDLIST
  1014. // so that we can pass it to AddNamespace.
  1015. _GetKeyForProperty(pszName, L"CSIDL", szKey, ARRAYSIZE(szKey));
  1016. int csidl;
  1017. hr = SHPropertyBag_ReadDWORD(ppb, szKey, (DWORD*)&csidl);
  1018. if (SUCCEEDED(hr))
  1019. {
  1020. hr = SHGetSpecialFolderLocation(NULL, csidl, &pidl);
  1021. }
  1022. }
  1023. if (SUCCEEDED(hr) && pidl)
  1024. {
  1025. // we succeeded in getting a location for the folder we
  1026. // are going to add, so lets pick up the rest of the
  1027. // information on that object.
  1028. GUID guid;
  1029. GUID *pguid = NULL;
  1030. _GetKeyForProperty(pszName, L"GUID", szKey, ARRAYSIZE(szKey));
  1031. pguid = SUCCEEDED(SHPropertyBag_ReadGUID(ppb, szKey, &guid)) ? &guid:NULL;
  1032. DWORD dwFlags = 0;
  1033. _GetKeyForProperty(pszName, L"Flags", szKey, ARRAYSIZE(szKey));
  1034. SHPropertyBag_ReadDWORD(ppb, szKey, &dwFlags);
  1035. hr = AddNameSpace(pguid, NULL, pidl, dwFlags);
  1036. }
  1037. ILFree(pidl);
  1038. return hr;
  1039. }
  1040. HRESULT CMergedFolder::Load(IPropertyBag* ppb, IErrorLog *pErrLog)
  1041. {
  1042. SHPropertyBag_ReadGUID(ppb, L"MergedFolder\\CLSID", &_clsid); // get the folders CLSID
  1043. SHPropertyBag_ReadDWORD(ppb, L"MergedFolder\\DropEffect", &_dwDropEffect);
  1044. _fInShellView = SHPropertyBag_ReadBOOLDefRet(ppb, L"MergedFolder\\ShellView", FALSE);
  1045. WCHAR sz[MAX_PATH];
  1046. if (SUCCEEDED(SHPropertyBag_ReadStr(ppb, L"MergedFolder\\Folders", sz, ARRAYSIZE(sz))))
  1047. {
  1048. LPWSTR pszName = sz;
  1049. while (pszName && *pszName)
  1050. {
  1051. LPWSTR pszNext = StrChrW(pszName, L',');
  1052. if (pszNext)
  1053. {
  1054. *pszNext = 0;
  1055. pszNext++;
  1056. }
  1057. _AddNameSpaceFromPropertyBag(ppb, pszName);
  1058. pszName = pszNext;
  1059. }
  1060. }
  1061. return S_OK;
  1062. }
  1063. // IShellFolder
  1064. STDMETHODIMP CMergedFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
  1065. {
  1066. *ppenumIDList = NULL;
  1067. HRESULT hr = E_FAIL;
  1068. if (_hdpaNamespaces)
  1069. {
  1070. _FreeObjects();
  1071. hr = CMergedFldrEnum_CreateInstance(this, grfFlags, ppenumIDList);
  1072. }
  1073. if (SUCCEEDED(hr) && _fInShellView)
  1074. {
  1075. Register(NULL, 0, 0);
  1076. }
  1077. return hr;
  1078. }
  1079. HRESULT CMergedFolder::_CreateWithCLSID(CLSID clsid, CMergedFolder **ppmf)
  1080. {
  1081. *ppmf = new CMergedFolder(this, clsid);
  1082. return *ppmf ? S_OK : E_OUTOFMEMORY;
  1083. }
  1084. BOOL CMergedFolder::_ShouldSuspend(REFGUID rguid)
  1085. {
  1086. return FALSE;
  1087. }
  1088. // create a new CMergedFolder from the first element in pidlWrap
  1089. // this is our private init method, IPersistFolder::Initialize() is how we
  1090. // get inited at our junction point.
  1091. HRESULT CMergedFolder::_New(LPCITEMIDLIST pidlWrap, CMergedFolder **ppmf)
  1092. {
  1093. ASSERT(ppmf);
  1094. *ppmf = NULL;
  1095. HRESULT hr = E_OUTOFMEMORY; // assume the worst
  1096. // just want the first element in pidlWrap
  1097. LPITEMIDLIST pidlFirst = ILCloneFirst(pidlWrap);
  1098. if (pidlFirst)
  1099. {
  1100. if (_IsFolder(pidlFirst))
  1101. {
  1102. hr = _CreateWithCLSID(_clsid, ppmf);
  1103. if (SUCCEEDED(hr) && _pidl)
  1104. {
  1105. hr = SHILCombine(_pidl, pidlFirst, &(*ppmf)->_pidl);
  1106. if (FAILED(hr))
  1107. {
  1108. (*ppmf)->Release();
  1109. *ppmf = NULL;
  1110. }
  1111. }
  1112. }
  1113. else
  1114. {
  1115. hr = E_NOINTERFACE;
  1116. }
  1117. ILFree(pidlFirst);
  1118. }
  1119. return hr;
  1120. }
  1121. void CMergedFolder::_AddAllOtherNamespaces(LPITEMIDLIST *ppidl)
  1122. {
  1123. TCHAR szName[MAX_PATH];
  1124. if (SUCCEEDED(DisplayNameOf(static_cast<CSFStorage *>(this), *ppidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName))))
  1125. {
  1126. CMergedFldrNamespace *pns;
  1127. for (int n = 0; pns = _Namespace(n); n++)
  1128. {
  1129. if (FAILED(_GetSubPidl(*ppidl, n, NULL, NULL, NULL)))
  1130. {
  1131. IBindCtx *pbc;
  1132. WIN32_FIND_DATA wfd = {0};
  1133. wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  1134. if (SUCCEEDED(SHCreateFileSysBindCtx(&wfd, &pbc)))
  1135. {
  1136. LPITEMIDLIST pidlNamespace;
  1137. if (SUCCEEDED(pns->Folder()->ParseDisplayName(NULL, pbc, szName, NULL, &pidlNamespace, NULL)))
  1138. {
  1139. _WrapAddIDList(pidlNamespace, n, ppidl);
  1140. ILFree(pidlNamespace);
  1141. }
  1142. pbc->Release();
  1143. }
  1144. }
  1145. }
  1146. }
  1147. }
  1148. STDMETHODIMP CMergedFolder::BindToObject(LPCITEMIDLIST pidlWrap, LPBC pbc, REFIID riid, void **ppv)
  1149. {
  1150. ASSERT(IS_VALID_PIDL(pidlWrap));
  1151. *ppv = NULL;
  1152. HRESULT hr = E_OUTOFMEMORY;
  1153. LPITEMIDLIST pidlRewrappedFirst;
  1154. if (_fDontMerge)
  1155. {
  1156. // this doesn't contain a wrap consisting of all namespaces but only one instead
  1157. LPITEMIDLIST pidlWrapFirst = ILCloneFirst(pidlWrap);
  1158. if (pidlWrapFirst)
  1159. {
  1160. TCHAR szName[MAX_PATH];
  1161. hr = DisplayNameOf(reinterpret_cast<IShellFolder *>(this), pidlWrapFirst, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1162. if (SUCCEEDED(hr))
  1163. {
  1164. // we want to round-trip the name so the non-merged pidl gets remerged
  1165. // during a bind (so you dont get just one namespace from here on down)
  1166. hr = ParseDisplayName(NULL, NULL, szName, NULL, &pidlRewrappedFirst, NULL);
  1167. }
  1168. ILFree(pidlWrapFirst);
  1169. }
  1170. }
  1171. else
  1172. {
  1173. pidlRewrappedFirst = ILCloneFirst(pidlWrap);
  1174. if (pidlRewrappedFirst)
  1175. {
  1176. hr = S_OK;
  1177. if (_fCDBurn && _IsFolder(pidlRewrappedFirst))
  1178. {
  1179. // in the cdburn case we need to fake up the other namespaces in the pidl we're about to bind to.
  1180. // this is so when we navigate into a subfolder that only exists on the CD and not the staging area,
  1181. // if a file is later added to the staging area it'll still be merged in.
  1182. _AddAllOtherNamespaces(&pidlRewrappedFirst);
  1183. }
  1184. }
  1185. }
  1186. if (SUCCEEDED(hr))
  1187. {
  1188. // just in case
  1189. ASSERT(SUCCEEDED(_IsWrap(pidlRewrappedFirst)));
  1190. LPCITEMIDLIST pidlNext = _ILNext(pidlWrap);
  1191. CMergedFolder *pmf;
  1192. hr = _New(pidlRewrappedFirst, &pmf);
  1193. if (SUCCEEDED(hr))
  1194. {
  1195. LPITEMIDLIST pidlSrc;
  1196. CMergedFldrNamespace *pns;
  1197. for (UINT i = 0; SUCCEEDED(_GetSubPidl(pidlRewrappedFirst, i, NULL, &pidlSrc, &pns)); i++)
  1198. {
  1199. hr = E_OUTOFMEMORY;
  1200. ASSERT(ILFindLastID(pidlSrc) == pidlSrc);
  1201. LPITEMIDLIST pidlSrcFirst = ILCloneFirst(pidlSrc);
  1202. if (pidlSrcFirst)
  1203. {
  1204. IShellFolder *psf;
  1205. if (SUCCEEDED(pns->Folder()->BindToObject(pidlSrcFirst, pbc, IID_PPV_ARG(IShellFolder, &psf))))
  1206. {
  1207. LPITEMIDLIST pidlAbs = ILCombine(pns->GetIDList(), pidlSrcFirst);
  1208. if (pidlAbs)
  1209. {
  1210. CMergedFldrNamespace *pnsNew = new CMergedFldrNamespace();
  1211. if (pnsNew)
  1212. {
  1213. hr = pnsNew->SetNamespace(&(pns->GetGUID()), psf, pidlAbs, pns->FolderAttrib());
  1214. if (SUCCEEDED(hr))
  1215. {
  1216. // propagate the drop folder down to the child.
  1217. hr = pnsNew->SetDropFolder(pns->GetDropFolder());
  1218. if (SUCCEEDED(hr))
  1219. {
  1220. hr = pmf->_SimpleAddNamespace(pnsNew);
  1221. if (SUCCEEDED(hr))
  1222. {
  1223. // success, _SimpleAddNamespace took ownership
  1224. pnsNew = NULL;
  1225. }
  1226. }
  1227. }
  1228. if (pnsNew)
  1229. delete pnsNew;
  1230. }
  1231. else
  1232. {
  1233. hr = E_OUTOFMEMORY;
  1234. }
  1235. ILFree(pidlAbs);
  1236. }
  1237. psf->Release();
  1238. }
  1239. ILFree(pidlSrcFirst);
  1240. }
  1241. ILFree(pidlSrc);
  1242. }
  1243. // it's possible to go through the loop without adding any namespaces.
  1244. // usually it's when BindToObject above fails -- this can happen if somebody
  1245. // puts a junction point in the merged folder (like a zip file). in that case
  1246. // we're in trouble.
  1247. if (ILIsEmpty(pidlNext))
  1248. hr = pmf->QueryInterface(riid, ppv);
  1249. else
  1250. hr = pmf->BindToObject(pidlNext, pbc, riid, ppv);
  1251. pmf->Release();
  1252. }
  1253. if (FAILED(hr) && ILIsEmpty(pidlNext))
  1254. {
  1255. // maybe it's an interface that we don't support ourselves (IStream?).
  1256. // we cant merge interfaces that we don't know about so lets just
  1257. // assume we'll pick the interface up from the default namespace in
  1258. // the wrapped pidl.
  1259. LPITEMIDLIST pidlSrc;
  1260. CMergedFldrNamespace *pns;
  1261. hr = _NamespaceForItem(pidlRewrappedFirst, ASFF_DEFNAMESPACE_BINDSTG, ASFF_DEFNAMESPACE_BINDSTG, NULL, &pidlSrc, &pns);
  1262. if (SUCCEEDED(hr))
  1263. {
  1264. hr = pns->Folder()->BindToObject(pidlSrc, pbc, riid, ppv);
  1265. ILFree(pidlSrc);
  1266. }
  1267. }
  1268. ILFree(pidlRewrappedFirst);
  1269. }
  1270. if (SUCCEEDED(hr) && _fInShellView)
  1271. {
  1272. Register(NULL, 0, 0);
  1273. }
  1274. return hr;
  1275. }
  1276. STDMETHODIMP CMergedFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  1277. {
  1278. return BindToObject(pidl, pbc, riid, ppv);
  1279. }
  1280. HRESULT CMergedFolder::_CompareSingleLevelIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1281. {
  1282. IShellFolder *psf1;
  1283. LPITEMIDLIST pidlItem1;
  1284. CMergedFldrNamespace *pns1;
  1285. HRESULT hr = _NamespaceForItem(pidl1, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf1, &pidlItem1, &pns1);
  1286. if (SUCCEEDED(hr))
  1287. {
  1288. IShellFolder *psf2;
  1289. LPITEMIDLIST pidlItem2;
  1290. CMergedFldrNamespace *pns2;
  1291. hr = _NamespaceForItem(pidl2, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf2, &pidlItem2, &pns2);
  1292. if (SUCCEEDED(hr))
  1293. {
  1294. // Same _Namespace? Just forward the request.
  1295. if (psf1 == psf2)
  1296. {
  1297. hr = psf1->CompareIDs(lParam, pidlItem1, pidlItem2);
  1298. }
  1299. else if ((pns1->FolderAttrib() & ASFF_SORTDOWN) ^ (pns2->FolderAttrib() & ASFF_SORTDOWN))
  1300. {
  1301. // One namespace marked ASFF_SORTDOWN and one not? The SORTDOWN one
  1302. // comes second.
  1303. hr = ResultFromShort((pns1->FolderAttrib() & ASFF_SORTDOWN) ? 1 : -1);
  1304. }
  1305. else
  1306. {
  1307. if (!_IsSimple(pidl1) && !_IsSimple(pidl2))
  1308. {
  1309. // Comparison heuristics:
  1310. // (1) folders take precedence over nonfolders, (2) alphanum comparison
  1311. int iFolder1 = SHGetAttributes(psf1, pidlItem1, SFGAO_FOLDER) ? 1 : 0;
  1312. int iFolder2 = SHGetAttributes(psf2, pidlItem2, SFGAO_FOLDER) ? 1 : 0;
  1313. hr = ResultFromShort(iFolder2 - iFolder1);
  1314. }
  1315. else
  1316. {
  1317. // if a pidl is simple, compare based on name only.
  1318. hr = ResultFromShort(0);
  1319. }
  1320. if (ResultFromShort(0) == hr)
  1321. {
  1322. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  1323. if (SUCCEEDED(DisplayNameOf(psf1, pidlItem1, SHGDN_INFOLDER, szName1, ARRAYSIZE(szName1))) &&
  1324. SUCCEEDED(DisplayNameOf(psf2, pidlItem2, SHGDN_INFOLDER, szName2, ARRAYSIZE(szName2))))
  1325. {
  1326. int iRet = StrCmp(szName1, szName2); // Comparisons are by name with items of the same type.
  1327. if ((iRet == 0) &&
  1328. SUCCEEDED(DisplayNameOf(psf1, pidlItem1, SHGDN_FORPARSING | SHGDN_INFOLDER, szName1, ARRAYSIZE(szName1))) &&
  1329. SUCCEEDED(DisplayNameOf(psf2, pidlItem2, SHGDN_FORPARSING | SHGDN_INFOLDER, szName2, ARRAYSIZE(szName2))))
  1330. {
  1331. iRet = lstrcmp(szName1, szName2); // minimal behavior change for xpsp1: fall back to parsing name if theres still a tie.
  1332. if ((iRet == 0) && !_ShouldMergeNamespaces(pns1, pns2))
  1333. {
  1334. ASSERTMSG(!_fInShellView, "we shouldn't be in this code path for the start menu");
  1335. // different namespaces must compare differently in the non-merged case.
  1336. iRet = _CompareArbitraryPidls(pidlItem1, pidlItem2);
  1337. }
  1338. }
  1339. hr = ResultFromShort(iRet);
  1340. }
  1341. }
  1342. }
  1343. ILFree(pidlItem2);
  1344. }
  1345. ILFree(pidlItem1);
  1346. }
  1347. return hr;
  1348. }
  1349. STDMETHODIMP CMergedFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1350. {
  1351. // just in case
  1352. //ASSERT(!pidl1 || SUCCEEDED(_IsWrap(pidl1)));
  1353. //ASSERT(!pidl2 || SUCCEEDED(_IsWrap(pidl2)));
  1354. HRESULT hr = E_FAIL;
  1355. LPITEMIDLIST pidlFirst1 = pidl1 ? ILCloneFirst(pidl1) : NULL;
  1356. LPITEMIDLIST pidlFirst2 = pidl2 ? ILCloneFirst(pidl2) : NULL;
  1357. if (pidlFirst1 && pidlFirst2)
  1358. {
  1359. hr = _CompareSingleLevelIDs(lParam, pidlFirst1, pidlFirst2);
  1360. }
  1361. ILFree(pidlFirst1);
  1362. ILFree(pidlFirst2);
  1363. // if there was an exact match then lets compare the trailing elements of the IDLIST
  1364. // if there are some (by binding down) etc.
  1365. if (ResultFromShort(0) == hr)
  1366. {
  1367. IShellFolder *psf;
  1368. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  1369. if (SUCCEEDED(hr))
  1370. {
  1371. hr = ILCompareRelIDs(psf, pidl1, pidl2, lParam);
  1372. psf->Release();
  1373. }
  1374. }
  1375. if (!_IsSimple(pidl1) && !_IsSimple(pidl2))
  1376. {
  1377. // if we're still the same, compare the number
  1378. // of namespaces in the pidl.
  1379. int nCount1, nCount2;
  1380. if (ResultFromShort(0) == hr)
  1381. {
  1382. nCount1 = pidl1 ? _GetSourceCount(pidl1) : 0;
  1383. nCount2 = pidl2 ? _GetSourceCount(pidl2) : 0;
  1384. hr = ResultFromShort(nCount1 - nCount2);
  1385. }
  1386. // next compare the namespaces themselves.
  1387. // basically we're only concerned with the two-namespace case, so if both pidls have
  1388. // elements from 0 or 2 namespaces theyre equal; we're worried about when one pidl has
  1389. // 1 sub-pidl in namespace 0 and the other one has 1 sub-pidl in namespace 1.
  1390. // we dont worry about 3+ namespaces and those permutations.
  1391. if ((ResultFromShort(0) == hr) && (nCount1 == 1) && (nCount2 == 1))
  1392. {
  1393. GUID guid1 = GUID_NULL, guid2 = GUID_NULL;
  1394. GetNameSpaceID(pidl1, &guid1);
  1395. GetNameSpaceID(pidl2, &guid2);
  1396. hr = ResultFromShort(memcmp(&guid1, &guid2, sizeof(GUID)));
  1397. }
  1398. }
  1399. return hr;
  1400. }
  1401. STDMETHODIMP CMergedFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  1402. {
  1403. *ppv = NULL;
  1404. HRESULT hr;
  1405. if (IsEqualIID(riid, IID_IDropTarget))
  1406. {
  1407. hr = CMergedFldrDropTarget_CreateInstance(this, hwnd, (IDropTarget**)ppv);
  1408. }
  1409. else if (IsEqualIID(riid, IID_IShellView))
  1410. {
  1411. IShellFolderViewCB *psfvcb;
  1412. hr = CMergedFolderViewCB_CreateInstance(this, &psfvcb);
  1413. if (SUCCEEDED(hr))
  1414. {
  1415. SFV_CREATE csfv = {0};
  1416. csfv.cbSize = sizeof(csfv);
  1417. csfv.pshf = SAFECAST(this, IAugmentedShellFolder2*);
  1418. csfv.psfvcb = psfvcb;
  1419. hr = SHCreateShellFolderView(&csfv, (IShellView **)ppv);
  1420. psfvcb->Release();
  1421. }
  1422. }
  1423. else if (_fInShellView && IsEqualIID(riid, IID_ICategoryProvider))
  1424. {
  1425. IShellFolder *psf;
  1426. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  1427. if (SUCCEEDED(hr))
  1428. {
  1429. BEGIN_CATEGORY_LIST(s_Categories)
  1430. CATEGORY_ENTRY_SCIDMAP(SCID_WHICHFOLDER, CLSID_MergedCategorizer)
  1431. END_CATEGORY_LIST()
  1432. hr = CCategoryProvider_Create(&CLSID_MergedCategorizer, &SCID_WHICHFOLDER, NULL, s_Categories, psf, riid, ppv);
  1433. psf->Release();
  1434. }
  1435. }
  1436. else if (_fInShellView && IsEqualIID(riid, IID_IContextMenu))
  1437. {
  1438. // this is pretty much what filefldr does to create its background
  1439. // context menu. we don't want to let one of our namespaces take over for the background
  1440. // context menu because then the context menu will think it's in an unmerged namespace.
  1441. // for example the new menu would then work with the storage of the child namespace
  1442. // and couldn't select the new item after its done since it has an unmerged pidl
  1443. // and the view has a merged one.
  1444. IShellFolder *psfToPass;
  1445. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfToPass));
  1446. if (SUCCEEDED(hr))
  1447. {
  1448. HKEY hkNoFiles;
  1449. RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), &hkNoFiles);
  1450. // initialize with our merged pidl.
  1451. IContextMenuCB *pcmcb = new CDefBackgroundMenuCB(_pidl);
  1452. if (pcmcb)
  1453. {
  1454. hr = CDefFolderMenu_Create2Ex(_pidl, hwnd, 0, NULL, psfToPass, pcmcb,
  1455. 1, &hkNoFiles, (IContextMenu **)ppv);
  1456. pcmcb->Release();
  1457. }
  1458. if (hkNoFiles) // CDefFolderMenu_Create can handle NULL ok
  1459. RegCloseKey(hkNoFiles);
  1460. psfToPass->Release();
  1461. }
  1462. }
  1463. else
  1464. {
  1465. CMergedFldrNamespace *pns;
  1466. hr = _FindNamespace(ASFF_DEFNAMESPACE_VIEWOBJ, ASFF_DEFNAMESPACE_VIEWOBJ, NULL, &pns);
  1467. if (SUCCEEDED(hr))
  1468. {
  1469. hr = pns->Folder()->CreateViewObject(hwnd, riid, ppv);
  1470. }
  1471. }
  1472. return hr;
  1473. }
  1474. STDMETHODIMP CMergedFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  1475. {
  1476. // attribs of the namespace root.
  1477. // scope this to the sub-namespaces?
  1478. if (!cidl || !apidl)
  1479. {
  1480. *rgfInOut &= SFGAO_FOLDER | SFGAO_FILESYSTEM |
  1481. SFGAO_LINK | SFGAO_DROPTARGET |
  1482. SFGAO_CANRENAME | SFGAO_CANDELETE |
  1483. SFGAO_CANLINK | SFGAO_CANCOPY |
  1484. SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
  1485. return S_OK;
  1486. }
  1487. HRESULT hr = S_OK;
  1488. for (UINT i = 0; SUCCEEDED(hr) && (i < cidl); i++)
  1489. {
  1490. ULONG ulAttribs = *rgfInOut;
  1491. IShellFolder* psf;
  1492. LPITEMIDLIST pidlItem;
  1493. CMergedFldrNamespace *pns;
  1494. hr = _NamespaceForItem(apidl[0], ASFF_DEFNAMESPACE_ATTRIB, ASFF_DEFNAMESPACE_ATTRIB, &psf, &pidlItem, &pns);
  1495. if (SUCCEEDED(hr))
  1496. {
  1497. ulAttribs |= SFGAO_FOLDER;
  1498. hr = psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlItem, &ulAttribs);
  1499. if (SUCCEEDED(hr))
  1500. {
  1501. ulAttribs = pns->FixItemAttributes(ulAttribs);
  1502. if (_fInShellView || !(*rgfInOut & SFGAO_FOLDER))
  1503. {
  1504. ulAttribs &= ~SFGAO_CANLINK; // avoid people creating links to our pidls
  1505. }
  1506. if (*rgfInOut & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK))
  1507. {
  1508. // allow per-type guys to do what they want.
  1509. IQueryAssociations *pqa;
  1510. DWORD dwDefEffect = DROPEFFECT_NONE;
  1511. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlItem, IID_X_PPV_ARG(IQueryAssociations, NULL, &pqa))))
  1512. {
  1513. DWORD cb = sizeof(dwDefEffect);
  1514. pqa->GetData(0, ASSOCDATA_VALUE, L"DefaultDropEffect", &dwDefEffect, &cb);
  1515. pqa->Release();
  1516. }
  1517. ulAttribs |= dwDefEffect & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK);
  1518. }
  1519. }
  1520. ILFree(pidlItem);
  1521. }
  1522. // keep only the attributes common to all pidls.
  1523. *rgfInOut &= ulAttribs;
  1524. }
  1525. return hr;
  1526. }
  1527. STDMETHODIMP CMergedFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgf, void **ppv)
  1528. {
  1529. *ppv = NULL;
  1530. HRESULT hr = E_NOTIMPL;
  1531. if (IsEqualGUID(riid, IID_IContextMenu))
  1532. {
  1533. hr = _GetContextMenu(hwnd, cidl, apidl, riid, ppv);
  1534. }
  1535. else if (IsEqualGUID(riid, IID_IDropTarget) && _IsFolder(apidl[0]))
  1536. {
  1537. IShellFolder *psf;
  1538. hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
  1539. if (SUCCEEDED(hr))
  1540. {
  1541. hr = psf->CreateViewObject(hwnd, riid, ppv);
  1542. psf->Release();
  1543. }
  1544. }
  1545. else if ((IsEqualIID(riid, IID_IExtractImage) ||
  1546. IsEqualIID(riid, IID_IExtractLogo)) && _IsFolder(apidl[0]))
  1547. {
  1548. IShellFolder *psfThis;
  1549. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfThis));
  1550. if (SUCCEEDED(hr))
  1551. {
  1552. hr = CFolderExtractImage_Create(psfThis, apidl[0], riid, ppv);
  1553. psfThis->Release();
  1554. }
  1555. }
  1556. else if (IsEqualIID(riid, IID_IDataObject) && _pidl)
  1557. {
  1558. hr = SHCreateFileDataObject(_pidl, cidl, apidl, NULL, (IDataObject **)ppv);
  1559. }
  1560. else
  1561. {
  1562. hr = E_OUTOFMEMORY;
  1563. // Forward to default _Namespace for UI object
  1564. LPITEMIDLIST *apidlItems = new LPITEMIDLIST[cidl];
  1565. if (apidlItems)
  1566. {
  1567. hr = E_FAIL; // assume failure
  1568. UINT cidlItems = 0;
  1569. IShellFolder *psf, *psfKeep; // not ref counted
  1570. LPITEMIDLIST pidlItem;
  1571. for (UINT i = 0; i < cidl; i++)
  1572. {
  1573. if (SUCCEEDED(_NamespaceForItem(apidl[i], ASFF_DEFNAMESPACE_UIOBJ, ASFF_DEFNAMESPACE_UIOBJ, &psf, &pidlItem, NULL)))
  1574. {
  1575. // only keep the ones that match the default namespace for UI object
  1576. // if they dont match, too bad.
  1577. apidlItems[cidlItems++] = pidlItem;
  1578. psfKeep = psf;
  1579. }
  1580. }
  1581. if (cidlItems)
  1582. {
  1583. hr = psfKeep->GetUIObjectOf(hwnd, cidlItems, (LPCITEMIDLIST *)apidlItems, riid, NULL, ppv);
  1584. }
  1585. for (UINT j = 0; j < cidlItems; j++)
  1586. {
  1587. ILFree(apidlItems[j]);
  1588. }
  1589. delete [] apidlItems;
  1590. }
  1591. }
  1592. return hr;
  1593. }
  1594. // in:
  1595. // pidl optional, NULL means get default
  1596. // out:
  1597. // *ppidl if pidl is != NULL
  1598. HRESULT CMergedFolder::_GetFolder2(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlInner, IShellFolder2 **ppsf)
  1599. {
  1600. if (ppidlInner)
  1601. *ppidlInner = NULL;
  1602. HRESULT hr;
  1603. if (NULL == pidl)
  1604. {
  1605. CMergedFldrNamespace *pns;
  1606. hr = _FindNamespace(ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, NULL, &pns);
  1607. if (FAILED(hr))
  1608. {
  1609. pns = _Namespace(0);
  1610. hr = pns ? S_OK : E_FAIL;
  1611. }
  1612. if (SUCCEEDED(hr))
  1613. hr = pns->Folder()->QueryInterface(IID_PPV_ARG(IShellFolder2, ppsf));
  1614. }
  1615. else
  1616. {
  1617. IShellFolder* psf;
  1618. hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf, ppidlInner, NULL);
  1619. if (SUCCEEDED(hr))
  1620. {
  1621. hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, ppsf));
  1622. if (FAILED(hr) && ppidlInner)
  1623. {
  1624. ILFree(*ppidlInner);
  1625. }
  1626. }
  1627. }
  1628. return hr;
  1629. }
  1630. // extended column information, these are appended after the set from the merged folder.
  1631. #define COLID_WHICHFOLDER 0x00 // column index for the merged folder location
  1632. static struct
  1633. {
  1634. const SHCOLUMNID *pscid;
  1635. UINT iTitle;
  1636. UINT cchCol;
  1637. UINT iFmt;
  1638. }
  1639. _columns[] =
  1640. {
  1641. {&SCID_WHICHFOLDER, IDS_WHICHFOLDER_COL, 20, LVCFMT_LEFT},
  1642. };
  1643. // column handler helpers
  1644. BOOL CMergedFolder::_IsOurColumn(UINT iCol)
  1645. {
  1646. return ((_iColumnOffset != -1) && ((iCol >= _iColumnOffset) && ((iCol - _iColumnOffset) < ARRAYSIZE(_columns))));
  1647. }
  1648. HRESULT CMergedFolder::_GetWhichFolderColumn(LPCITEMIDLIST pidl, LPWSTR pszBuffer, INT cchBuffer)
  1649. {
  1650. CMergedFldrNamespace *pns;
  1651. HRESULT hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_ATTRIB, ASFF_DEFNAMESPACE_ATTRIB, NULL, NULL, &pns);
  1652. if (SUCCEEDED(hr))
  1653. {
  1654. hr = pns->GetLocation(pszBuffer, cchBuffer);
  1655. }
  1656. return hr;
  1657. }
  1658. STDMETHODIMP CMergedFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  1659. {
  1660. // do we have a column offset, or is this within the range of our the ISFs columns
  1661. HRESULT hr = E_FAIL;
  1662. if (!_IsOurColumn(iColumn))
  1663. {
  1664. IShellFolder2 *psf2;
  1665. LPITEMIDLIST pidlItem;
  1666. // get the column value from the folder.
  1667. hr = _GetFolder2(pidl, &pidlItem, &psf2);
  1668. if (SUCCEEDED(hr))
  1669. {
  1670. hr = psf2->GetDetailsOf(pidlItem, iColumn, pDetails);
  1671. psf2->Release();
  1672. ILFree(pidlItem);
  1673. }
  1674. // we failed and we don't know the column offset to handle
  1675. if (FAILED(hr) && (_iColumnOffset == -1))
  1676. _iColumnOffset = iColumn;
  1677. }
  1678. if (FAILED(hr) && _IsOurColumn(iColumn))
  1679. {
  1680. iColumn -= _iColumnOffset;
  1681. pDetails->str.uType = STRRET_CSTR; // we are returning strings
  1682. pDetails->str.cStr[0] = 0;
  1683. WCHAR szTemp[MAX_PATH];
  1684. if (!pidl)
  1685. {
  1686. pDetails->fmt = _columns[iColumn].iFmt;
  1687. pDetails->cxChar = _columns[iColumn].cchCol;
  1688. LoadString(HINST_THISDLL, _columns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp));
  1689. hr = StringToStrRet(szTemp, &(pDetails->str));
  1690. }
  1691. else if (SUCCEEDED(_IsWrap(pidl)))
  1692. {
  1693. if (iColumn == COLID_WHICHFOLDER)
  1694. {
  1695. hr = _GetWhichFolderColumn(pidl, szTemp, ARRAYSIZE(szTemp));
  1696. if (SUCCEEDED(hr))
  1697. hr = StringToStrRet(szTemp, &(pDetails->str));
  1698. }
  1699. }
  1700. }
  1701. return hr;
  1702. }
  1703. STDMETHODIMP CMergedFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  1704. {
  1705. IShellFolder2 *psf2;
  1706. HRESULT hr = _GetFolder2(NULL, NULL, &psf2);
  1707. if (SUCCEEDED(hr))
  1708. {
  1709. hr = psf2->GetDefaultColumnState(iColumn, pbState);
  1710. psf2->Release();
  1711. }
  1712. return hr;
  1713. }
  1714. STDMETHODIMP CMergedFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  1715. {
  1716. HRESULT hr;
  1717. if (IsEqualSCID(*pscid, SCID_WHICHFOLDER))
  1718. {
  1719. WCHAR szTemp[MAX_PATH];
  1720. hr = _GetWhichFolderColumn(pidl, szTemp, ARRAYSIZE(szTemp));
  1721. if (SUCCEEDED(hr))
  1722. hr = InitVariantFromStr(pv, szTemp);
  1723. }
  1724. else
  1725. {
  1726. IShellFolder2 *psf2;
  1727. LPITEMIDLIST pidlItem;
  1728. hr = _GetFolder2(pidl, &pidlItem, &psf2);
  1729. if (SUCCEEDED(hr))
  1730. {
  1731. hr = psf2->GetDetailsEx(pidlItem, pscid, pv);
  1732. psf2->Release();
  1733. ILFree(pidlItem);
  1734. }
  1735. }
  1736. return hr;
  1737. }
  1738. STDMETHODIMP CMergedFolder::MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid)
  1739. {
  1740. HRESULT hr = S_OK;
  1741. // one of our columns?
  1742. if (_IsOurColumn(iCol))
  1743. {
  1744. iCol -= _iColumnOffset;
  1745. *pscid = *_columns[iCol].pscid;
  1746. }
  1747. else
  1748. {
  1749. IShellFolder2 *psf2;
  1750. hr = _GetFolder2(NULL, NULL, &psf2);
  1751. if (SUCCEEDED(hr))
  1752. {
  1753. hr = psf2->MapColumnToSCID(iCol, pscid);
  1754. psf2->Release();
  1755. }
  1756. }
  1757. return hr;
  1758. }
  1759. // Forward to default _Namespace for display name
  1760. STDMETHODIMP CMergedFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD grfFlags, STRRET *psr)
  1761. {
  1762. IShellFolder* psf;
  1763. LPITEMIDLIST pidlItem;
  1764. HRESULT hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf, &pidlItem, NULL);
  1765. if (SUCCEEDED(hr))
  1766. {
  1767. ASSERT(ILFindLastID(pidlItem) == pidlItem);
  1768. hr = psf->GetDisplayNameOf(pidlItem, grfFlags, psr);
  1769. if (SUCCEEDED(hr))
  1770. {
  1771. hr = _FixStrRetOffset(pidlItem, psr);
  1772. #ifdef DEBUG
  1773. // If the trace flags are set, and this is not comming from an internal query,
  1774. // Then append the location where this name came from
  1775. if (!((SHGDN_FORPARSING | SHGDN_FOREDITING) & grfFlags) &&
  1776. (g_qwTraceFlags & TF_AUGM))
  1777. {
  1778. LPWSTR pwszOldName;
  1779. hr = StrRetToStrW(psr, pidlItem, &pwszOldName);
  1780. if (SUCCEEDED(hr))
  1781. {
  1782. UINT cch = lstrlenW(pwszOldName);
  1783. psr->uType = STRRET_WSTR;
  1784. psr->pOleStr = (LPWSTR)SHAlloc((cch + 50) * sizeof(WCHAR));
  1785. if (psr->pOleStr)
  1786. {
  1787. LPWSTR pwsz = psr->pOleStr + wsprintfW(psr->pOleStr, L"%s ", pwszOldName);
  1788. ULONG nSrc = _GetSourceCount(pidl);
  1789. UINT uSrc;
  1790. // Cut off after 10 to avoid buffer overflow
  1791. for (uSrc = 0; uSrc < nSrc && uSrc < 10; uSrc++)
  1792. {
  1793. UINT uID;
  1794. if (SUCCEEDED(_GetSubPidl(pidl, uSrc, &uID, NULL, NULL)))
  1795. {
  1796. pwsz += wsprintfW(pwsz, L"%c%d", uSrc ? '+' : '(', uID);
  1797. }
  1798. }
  1799. pwsz += wsprintfW(pwsz, L")");
  1800. }
  1801. else
  1802. {
  1803. hr = E_OUTOFMEMORY;
  1804. }
  1805. SHFree(pwszOldName);
  1806. }
  1807. }
  1808. #endif
  1809. }
  1810. ILFree(pidlItem);
  1811. }
  1812. else
  1813. {
  1814. if (IsSelf(1, &pidl) &&
  1815. ((grfFlags & (SHGDN_FORADDRESSBAR | SHGDN_INFOLDER | SHGDN_FORPARSING)) == SHGDN_FORPARSING))
  1816. {
  1817. IShellFolder2 *psf2;
  1818. hr = _GetFolder2(NULL, NULL, &psf2);
  1819. if (SUCCEEDED(hr))
  1820. {
  1821. hr = psf2->GetDisplayNameOf(NULL, grfFlags, psr);
  1822. psf2->Release();
  1823. }
  1824. }
  1825. else
  1826. {
  1827. hr = E_INVALIDARG;
  1828. }
  1829. }
  1830. return hr;
  1831. }
  1832. // CFSFolder helper
  1833. HRESULT _NextSegment(LPCWSTR *ppszIn, LPTSTR pszSegment, UINT cchSegment, BOOL bValidate);
  1834. void CMergedFolder::_SetSimple(LPITEMIDLIST *ppidl)
  1835. {
  1836. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)*ppidl;
  1837. if ((pWrap->cb >= sizeof(AUGM_IDWRAP)) &&
  1838. (pWrap->ulTag == AUGM_WRAPTAG) &&
  1839. (pWrap->ulVersion == AUGM_WRAPVERSION_2_0))
  1840. {
  1841. pWrap->dwFlags |= AUGMF_ISSIMPLE;
  1842. }
  1843. }
  1844. BOOL CMergedFolder::_IsSimple(LPCITEMIDLIST pidl)
  1845. {
  1846. BOOL fSimple = FALSE;
  1847. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidl;
  1848. if ((pWrap->cb >= sizeof(AUGM_IDWRAP)) &&
  1849. (pWrap->ulTag == AUGM_WRAPTAG) &&
  1850. (pWrap->ulVersion == AUGM_WRAPVERSION_2_0))
  1851. {
  1852. fSimple = (pWrap->dwFlags & AUGMF_ISSIMPLE);
  1853. }
  1854. return fSimple;
  1855. }
  1856. STDMETHODIMP CMergedFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszName,
  1857. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttrib)
  1858. {
  1859. *ppidl = NULL;
  1860. TCHAR szName[MAX_PATH];
  1861. // ISSUE: this relies on the fact that the path that's getting parsed is FS (with backslashes).
  1862. // alternatively this could be rearchitected so that the namespaces would be delegate folders
  1863. // and we could do the merge when we handle their pidl allocation.
  1864. HRESULT hr = _NextSegment((LPCWSTR *) &pwszName, szName, ARRAYSIZE(szName), TRUE);
  1865. if (SUCCEEDED(hr))
  1866. {
  1867. // let all name spaces try to parse, append them into the pidl as these are found
  1868. CMergedFldrNamespace *pns;
  1869. HRESULT hrParse = S_OK;
  1870. for (int i = 0; SUCCEEDED(hr) && (pns = _Namespace(i)); i++)
  1871. {
  1872. LPITEMIDLIST pidl;
  1873. hrParse = pns->Folder()->ParseDisplayName(hwnd, pbc, szName, NULL, &pidl, NULL);
  1874. if (SUCCEEDED(hrParse))
  1875. {
  1876. // let each name space parse, accumulate results
  1877. // into *ppidl across multiple folders
  1878. hr = _WrapAddIDList(pidl, i, ppidl);
  1879. ILFree(pidl);
  1880. }
  1881. }
  1882. if (!*ppidl)
  1883. {
  1884. if (SUCCEEDED(hr))
  1885. {
  1886. hr = hrParse;
  1887. }
  1888. ASSERT(FAILED(hr));
  1889. }
  1890. else
  1891. {
  1892. if (S_OK == SHIsFileSysBindCtx(pbc, NULL))
  1893. {
  1894. _SetSimple(ppidl);
  1895. }
  1896. ASSERT(ILFindLastID(*ppidl) == *ppidl);
  1897. }
  1898. if (SUCCEEDED(hr) && pwszName)
  1899. {
  1900. IShellFolder *psf;
  1901. hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psf));
  1902. if (SUCCEEDED(hr))
  1903. {
  1904. LPITEMIDLIST pidlNext;
  1905. hr = psf->ParseDisplayName(hwnd, pbc, pwszName, NULL, &pidlNext, pdwAttrib);
  1906. if (SUCCEEDED(hr))
  1907. {
  1908. // shilappend frees pidlNext
  1909. hr = SHILAppend(pidlNext, ppidl);
  1910. }
  1911. psf->Release();
  1912. }
  1913. if (FAILED(hr))
  1914. {
  1915. Pidl_Set(ppidl, NULL);
  1916. }
  1917. }
  1918. if (SUCCEEDED(hr) && pdwAttrib && *pdwAttrib)
  1919. {
  1920. GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttrib);
  1921. }
  1922. }
  1923. ASSERT(SUCCEEDED(hr) ? (*ppidl != NULL) : (*ppidl == NULL));
  1924. return hr;
  1925. }
  1926. STDMETHODIMP CMergedFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidlWrap,
  1927. LPCOLESTR pwszName, DWORD uFlags, LPITEMIDLIST *ppidlOut)
  1928. {
  1929. if (ppidlOut)
  1930. *ppidlOut = NULL;
  1931. HRESULT hr = E_FAIL;
  1932. IShellFolder* psf; // not ref counted
  1933. LPITEMIDLIST pidlItem;
  1934. if (!_fInShellView)
  1935. {
  1936. hr = _NamespaceForItem(pidlWrap, ASFF_COMMON, 0, &psf, &pidlItem, NULL, TRUE);
  1937. if (FAILED(hr))
  1938. {
  1939. hr = _NamespaceForItem(pidlWrap, ASFF_COMMON, ASFF_COMMON, &psf, &pidlItem, NULL, TRUE);
  1940. if (SUCCEEDED(hr))
  1941. {
  1942. hr = AffectAllUsers(hwnd) ? S_OK : E_FAIL;
  1943. if (FAILED(hr))
  1944. {
  1945. ILFree(pidlItem);
  1946. }
  1947. }
  1948. }
  1949. }
  1950. else
  1951. {
  1952. hr = _NamespaceForItem(pidlWrap, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf, &pidlItem, NULL);
  1953. }
  1954. if (SUCCEEDED(hr))
  1955. {
  1956. ASSERT(ILFindLastID(pidlItem) == pidlItem);
  1957. hr = psf->SetNameOf(hwnd, pidlItem, pwszName, uFlags, NULL);
  1958. ILFree(pidlItem);
  1959. }
  1960. if (SUCCEEDED(hr) && ppidlOut)
  1961. {
  1962. WCHAR szName[MAX_PATH];
  1963. hr = DisplayNameOf(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1964. if (SUCCEEDED(hr))
  1965. hr = ParseDisplayName(NULL, NULL, szName, NULL, ppidlOut, NULL);
  1966. }
  1967. return hr;
  1968. }
  1969. // IAugmentedShellFolder::AddNameSpace
  1970. // Adds a source _Namespace to the Merge shell folder object
  1971. STDMETHODIMP CMergedFolder::AddNameSpace(const GUID *pguidObject, IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwFlags)
  1972. {
  1973. // Check for duplicate via full display name
  1974. CMergedFldrNamespace *pns;
  1975. for (int i = 0; pns = _Namespace(i); i++)
  1976. {
  1977. if (pidl && ILIsEqual(pns->GetIDList(), pidl))
  1978. {
  1979. // If Found, then reassign attributes and return
  1980. return pns->SetNamespace(pguidObject, psf, pidl, dwFlags);
  1981. }
  1982. }
  1983. HRESULT hr;
  1984. pns = new CMergedFldrNamespace();
  1985. if (pns)
  1986. {
  1987. hr = pns->SetNamespace(pguidObject, psf, pidl, dwFlags);
  1988. if (SUCCEEDED(hr))
  1989. {
  1990. hr = _SimpleAddNamespace(pns);
  1991. if (SUCCEEDED(hr))
  1992. {
  1993. pns = NULL; // success, don't free below
  1994. }
  1995. }
  1996. if (pns)
  1997. delete pns;
  1998. }
  1999. else
  2000. hr = E_OUTOFMEMORY;
  2001. return hr;
  2002. }
  2003. HRESULT CMergedFolder::_SimpleAddNamespace(CMergedFldrNamespace *pns)
  2004. {
  2005. if (NULL == _hdpaNamespaces)
  2006. _hdpaNamespaces = DPA_Create(2);
  2007. HRESULT hr = E_OUTOFMEMORY;
  2008. if (_hdpaNamespaces && (DPA_AppendPtr(_hdpaNamespaces, pns) != -1))
  2009. {
  2010. // If there is any conditional merging going on, then remember it
  2011. if (pns->FolderAttrib() & ASFF_MERGESAMEGUID)
  2012. {
  2013. _fPartialMerge = TRUE;
  2014. }
  2015. hr = S_OK;
  2016. }
  2017. return hr;
  2018. }
  2019. STDMETHODIMP CMergedFolder::GetNameSpaceID(LPCITEMIDLIST pidl, GUID * pguidOut)
  2020. {
  2021. HRESULT hr = E_INVALIDARG;
  2022. ASSERT(IS_VALID_PIDL(pidl));
  2023. ASSERT(IS_VALID_WRITE_PTR(pguidOut, GUID));
  2024. if (pidl && pguidOut)
  2025. {
  2026. CMergedFldrNamespace *pns;
  2027. hr = _GetSubPidl(pidl, 0, NULL, NULL, &pns);
  2028. if (SUCCEEDED(hr))
  2029. {
  2030. *pguidOut = pns->GetGUID();
  2031. }
  2032. }
  2033. return hr;
  2034. }
  2035. // Retrieves data for the _Namespace identified by dwID.
  2036. STDMETHODIMP CMergedFolder::QueryNameSpace(ULONG iIndex, GUID *pguidOut, IShellFolder **ppsf)
  2037. {
  2038. CMergedFldrNamespace *pns;
  2039. HRESULT hr = _Namespace(iIndex, &pns);
  2040. if (SUCCEEDED(hr))
  2041. {
  2042. if (pguidOut)
  2043. *pguidOut = pns->GetGUID();
  2044. if (ppsf)
  2045. {
  2046. *ppsf = pns->Folder();
  2047. if (*ppsf)
  2048. (*ppsf)->AddRef();
  2049. }
  2050. }
  2051. return hr;
  2052. }
  2053. #define ASFQNSI_SUPPORTED (ASFQNSI_FLAGS | ASFQNSI_FOLDER | ASFQNSI_GUID | ASFQNSI_PIDL)
  2054. STDMETHODIMP CMergedFolder::QueryNameSpace2(ULONG iIndex, QUERYNAMESPACEINFO *pqnsi)
  2055. {
  2056. if (pqnsi->cbSize != sizeof(QUERYNAMESPACEINFO) ||
  2057. (pqnsi->dwMask & ~ASFQNSI_SUPPORTED))
  2058. {
  2059. return E_INVALIDARG;
  2060. }
  2061. CMergedFldrNamespace *pns;
  2062. HRESULT hr = _Namespace(iIndex, &pns);
  2063. if (SUCCEEDED(hr))
  2064. {
  2065. // Do PIDL first since it's the only one that can fail
  2066. // so we don't have to do cleanup
  2067. if (pqnsi->dwMask & ASFQNSI_PIDL)
  2068. {
  2069. hr = SHILClone(pns->GetIDList(), &pqnsi->pidl);
  2070. if (FAILED(hr))
  2071. return hr;
  2072. }
  2073. if (pqnsi->dwMask & ASFQNSI_FLAGS)
  2074. pqnsi->dwFlags = pns->FolderAttrib();
  2075. if (pqnsi->dwMask & ASFQNSI_FOLDER)
  2076. {
  2077. pqnsi->psf = pns->Folder();
  2078. if (pqnsi->psf)
  2079. pqnsi->psf->AddRef();
  2080. }
  2081. if (pqnsi->dwMask & ASFQNSI_GUID)
  2082. pqnsi->guidObject = pns->GetGUID();
  2083. }
  2084. return hr;
  2085. }
  2086. STDMETHODIMP CMergedFolder::EnumNameSpace(DWORD uNameSpace, DWORD *pdwID)
  2087. {
  2088. if (uNameSpace == (DWORD)-1)
  2089. {
  2090. return ResultFromShort(_NamespaceCount());
  2091. }
  2092. if (uNameSpace < (UINT)_NamespaceCount())
  2093. {
  2094. // Our namespace IDs are just ordinals
  2095. *pdwID = uNameSpace;
  2096. return S_OK;
  2097. }
  2098. return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
  2099. }
  2100. // IAugmentedShellFolder2 methods
  2101. STDMETHODIMP CMergedFolder::UnWrapIDList(LPCITEMIDLIST pidlWrap, LONG cPidls,
  2102. IShellFolder** apsf, LPITEMIDLIST* apidlFolder,
  2103. LPITEMIDLIST* apidlItems, LONG* pcFetched)
  2104. {
  2105. if (cPidls <= 0)
  2106. return E_INVALIDARG;
  2107. HRESULT hr = S_OK;
  2108. // Enumerate pidls in wrap
  2109. LPITEMIDLIST pidlItem;
  2110. CMergedFldrNamespace *pns;
  2111. LONG cFetched;
  2112. for (cFetched = 0; SUCCEEDED(hr) && (cFetched < cPidls) && SUCCEEDED(_GetSubPidl(pidlWrap, cFetched, NULL, &pidlItem, &pns)); cFetched++)
  2113. {
  2114. if (apsf)
  2115. {
  2116. apsf[cFetched] = pns->Folder();
  2117. if (apsf[cFetched])
  2118. apsf[cFetched]->AddRef();
  2119. }
  2120. if (apidlFolder)
  2121. {
  2122. hr = SHILClone(pns->GetIDList(), &apidlFolder[cFetched]);
  2123. }
  2124. if (apidlItems)
  2125. {
  2126. apidlItems[cFetched] = NULL;
  2127. if (SUCCEEDED(hr))
  2128. {
  2129. hr = SHILClone(pidlItem, &apidlItems[cFetched]);
  2130. }
  2131. }
  2132. ILFree(pidlItem);
  2133. }
  2134. if (SUCCEEDED(hr))
  2135. {
  2136. if (pcFetched)
  2137. {
  2138. *pcFetched = cFetched;
  2139. }
  2140. hr = (cFetched == cPidls) ? S_OK : S_FALSE;
  2141. }
  2142. else
  2143. {
  2144. // clean up items we've already allocated; since the caller won't free if we
  2145. // return failure
  2146. for (LONG i = 0; i < cFetched; i++)
  2147. {
  2148. if (apsf)
  2149. ATOMICRELEASE(apsf[i]);
  2150. if (apidlFolder)
  2151. ILFree(apidlFolder[i]);
  2152. if (apidlItems)
  2153. ILFree(apidlItems[i]);
  2154. }
  2155. }
  2156. return hr;
  2157. }
  2158. STDMETHODIMP CMergedFolder::SetOwner(IUnknown* punkOwner)
  2159. {
  2160. DPA_EnumCallback(_hdpaNamespaces, _SetOwnerProc, punkOwner);
  2161. return S_OK;
  2162. }
  2163. int CMergedFolder::_SetOwnerProc(void *pv, void *pvParam)
  2164. {
  2165. CMergedFldrNamespace *pns = (CMergedFldrNamespace*) pv;
  2166. return pns->SetOwner((IUnknown*)pvParam);
  2167. }
  2168. // ITranslateShellChangeNotify methods
  2169. // old translate style only
  2170. LPITEMIDLIST CMergedFolder::_ILCombineBase(LPCITEMIDLIST pidlContainingBase, LPCITEMIDLIST pidlRel)
  2171. {
  2172. // This routine differs from ILCombine in that it takes the First pidl's base, and
  2173. // cats on the last id of the second pidl. We need this so Wrapped pidls
  2174. // end up with the same base, and we get a valid full pidl.
  2175. LPITEMIDLIST pidlRet = NULL;
  2176. LPITEMIDLIST pidlBase = ILClone(pidlContainingBase);
  2177. if (pidlBase)
  2178. {
  2179. ILRemoveLastID(pidlBase);
  2180. pidlRet = ILCombine(pidlBase, pidlRel);
  2181. ILFree(pidlBase);
  2182. }
  2183. return pidlRet;
  2184. }
  2185. BOOL CMergedFolder::_IsFolderEvent(LONG lEvent)
  2186. {
  2187. return lEvent == SHCNE_MKDIR || lEvent == SHCNE_RMDIR || lEvent == SHCNE_RENAMEFOLDER;
  2188. }
  2189. BOOL GetRealPidlFromSimple(LPCITEMIDLIST pidlSimple, LPITEMIDLIST* ppidlReal)
  2190. {
  2191. // Similar to SHGetRealIDL in Function, but SHGetRealIDL does SHGDN_FORPARSING | INFOLDER.
  2192. // I need the parsing name. I can't rev SHGetRealIDL very easily, so here's this one!
  2193. TCHAR szFullName[MAX_PATH];
  2194. if (SUCCEEDED(SHGetNameAndFlags(pidlSimple, SHGDN_FORPARSING, szFullName, SIZECHARS(szFullName), NULL)))
  2195. {
  2196. *ppidlReal = ILCreateFromPath(szFullName);
  2197. }
  2198. if (*ppidlReal == NULL) // Unable to create? Then use the simple pidl. This is because it does not exist any more
  2199. { // For say, a Delete Notify
  2200. *ppidlReal = ILClone(pidlSimple);
  2201. }
  2202. return *ppidlReal != NULL;
  2203. }
  2204. // the new way we have of translating ids is better than the old way since it handles
  2205. // multi-level translation.
  2206. // the problem is that the old way is used extensively by the start menu, so the
  2207. // start menu will need to be rewritten to use the new way. when that happens,
  2208. // then the old method can be ripped out.
  2209. STDMETHODIMP CMergedFolder::TranslateIDs(
  2210. LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
  2211. LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2,
  2212. LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
  2213. LPITEMIDLIST *ppidlOut2Event2)
  2214. {
  2215. if (_fInShellView)
  2216. {
  2217. return _NewTranslateIDs(plEvent, pidl1, pidl2, ppidlOut1, ppidlOut2, plEvent2, ppidlOut1Event2, ppidlOut2Event2);
  2218. }
  2219. else
  2220. {
  2221. return _OldTranslateIDs(plEvent, pidl1, pidl2, ppidlOut1, ppidlOut2, plEvent2, ppidlOut1Event2, ppidlOut2Event2);
  2222. }
  2223. }
  2224. // old version.
  2225. HRESULT CMergedFolder::_OldTranslateIDs(
  2226. LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
  2227. LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2,
  2228. LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
  2229. LPITEMIDLIST *ppidlOut2Event2)
  2230. {
  2231. HRESULT hr = E_FAIL;
  2232. if (!plEvent)
  2233. return E_FAIL;
  2234. switch (*plEvent)
  2235. {
  2236. case SHCNE_EXTENDED_EVENT:
  2237. case SHCNE_ASSOCCHANGED:
  2238. case SHCNE_UPDATEIMAGE:
  2239. return S_OK;
  2240. case SHCNE_UPDATEDIR:
  2241. _FreeObjects();
  2242. return S_OK;
  2243. }
  2244. ASSERT(ppidlOut1);
  2245. ASSERT(ppidlOut2);
  2246. LONG lEvent = *plEvent;
  2247. *plEvent2 = (LONG)-1;
  2248. *ppidlOut1Event2 = NULL;
  2249. *ppidlOut2Event2 = NULL;
  2250. *ppidlOut1 = (LPITEMIDLIST)pidl1;
  2251. *ppidlOut2 = (LPITEMIDLIST)pidl2;
  2252. // If they are already wrapped, don't wrap twice.
  2253. if (SUCCEEDED(_IsWrap(ILFindLastID(pidl1))) ||
  2254. SUCCEEDED(_IsWrap(ILFindLastID(pidl2))))
  2255. {
  2256. // We don't want to wrap twice.
  2257. return E_FAIL;
  2258. }
  2259. if (!_hdpaNamespaces)
  2260. return E_FAIL;
  2261. if (!_hdpaObjects)
  2262. return E_FAIL;
  2263. CMergedFldrItem* pmfi;
  2264. int iIndex;
  2265. int iShellFolder1 = -1;
  2266. int iShellFolder2 = -1;
  2267. IShellFolder* psf1 = NULL;
  2268. IShellFolder* psf2 = NULL;
  2269. LPITEMIDLIST pidlReal1 = NULL;
  2270. LPITEMIDLIST pidlReal2 = NULL;
  2271. LPITEMIDLIST pidlRealRel1 = NULL;
  2272. LPITEMIDLIST pidlRealRel2 = NULL;
  2273. CMergedFldrNamespace * pns1 = NULL;
  2274. CMergedFldrNamespace * pns2 = NULL;
  2275. BOOL fFolder = _IsFolderEvent(*plEvent);
  2276. // Get the information about these Simple pidls: Are they our Children? If so, what _Namespace?
  2277. BOOL fChild1 = _IsChildIDInternal(pidl1, TRUE, &iShellFolder1);
  2278. BOOL fChild2 = _IsChildIDInternal(pidl2, TRUE, &iShellFolder2);
  2279. // Is either a child?
  2280. if (!(fChild1 || fChild2))
  2281. return hr;
  2282. // Ok, pidl1 is a child, can we get the Real pidl from the simple one?
  2283. if (pidl1 && !GetRealPidlFromSimple(pidl1, &pidlReal1))
  2284. goto Cleanup;
  2285. // Ok, pidl2 is a child, can we get the Real pidl from the simple one?
  2286. if (pidl2 && !GetRealPidlFromSimple(pidl2, &pidlReal2))
  2287. goto Cleanup;
  2288. // These are for code clarity later on. We deal with Relative pidls from here until the very end,
  2289. // when we combine the base of the in pidls with the outgoing wrapped pidls.
  2290. if (pidlReal1)
  2291. pidlRealRel1 = ILFindLastID(pidlReal1);
  2292. if (pidlReal2)
  2293. pidlRealRel2 = ILFindLastID(pidlReal2);
  2294. // Is Pidl1 in our _Namespaces?
  2295. if (iShellFolder1 != -1)
  2296. {
  2297. // Yes, lets get the non-refcounted shell folder that know's about this pidl.
  2298. pns1 = _Namespace(iShellFolder1);
  2299. psf1 = pns1->Folder(); // Non ref counted.
  2300. }
  2301. // Is Pidl2 in our _Namespaces?
  2302. if (iShellFolder2 != -1)
  2303. {
  2304. // Yes, lets get the non-refcounted shell folder that know's about this pidl.
  2305. pns2 = _Namespace(iShellFolder2);
  2306. psf2 = pns2->Folder(); // Non ref counted.
  2307. }
  2308. hr = S_OK;
  2309. switch(*plEvent)
  2310. {
  2311. case SHCNE_UPDATEITEM:
  2312. case 0: // Just look up the pidls and return.
  2313. {
  2314. DWORD rgfAttrib = SFGAO_FOLDER;
  2315. if (iShellFolder1 != -1)
  2316. {
  2317. psf1->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel1, &rgfAttrib);
  2318. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pmfi))
  2319. {
  2320. *ppidlOut1 = _ILCombineBase(pidlReal1, pmfi->GetIDList());
  2321. if (!*ppidlOut1)
  2322. hr = E_OUTOFMEMORY;
  2323. }
  2324. }
  2325. rgfAttrib = SFGAO_FOLDER;
  2326. if (iShellFolder2 != -1 && SUCCEEDED(hr))
  2327. {
  2328. psf2->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel2, &rgfAttrib);
  2329. if (S_OK == _SearchForPidl(iShellFolder2, pns2, pidlRealRel2, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pmfi))
  2330. {
  2331. *ppidlOut2 = _ILCombineBase(pidlReal2, pmfi->GetIDList());
  2332. if (!*ppidlOut2)
  2333. hr = E_OUTOFMEMORY;
  2334. }
  2335. }
  2336. }
  2337. break;
  2338. case SHCNE_CREATE:
  2339. case SHCNE_MKDIR:
  2340. {
  2341. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s", fFolder? TEXT("SHCNE_MKDIR") : TEXT("SHCNE_CREATE"));
  2342. // Is there a thing of this name already?
  2343. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1, fFolder, &iIndex, &pmfi))
  2344. {
  2345. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s needs to be merged. Converting to Rename", pmfi->GetDisplayName());
  2346. // Yes; Then we need to merge this new pidl into the wrapped pidl, and change this
  2347. // to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
  2348. // as the second arg. I have to be careful about the freeing:
  2349. // Free *ppidlOut1
  2350. // Clone pmfi->pidlWrap -> *ppidlOut1.
  2351. // Add pidl1 to pmfi->_pidlWrap.
  2352. // Clone new pmfi->_pidlWrap -> *ppidlOut2. ASSERT(*ppidlOut2 == NULL)
  2353. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2354. if (*ppidlOut1)
  2355. {
  2356. _WrapAddIDList(pidlRealRel1, iShellFolder1, &pmfi->_pidlWrap);
  2357. *ppidlOut2 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2358. if (!*ppidlOut2)
  2359. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl2");
  2360. *plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2361. }
  2362. else
  2363. {
  2364. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl1");
  2365. }
  2366. }
  2367. else
  2368. {
  2369. CMergedFldrItem* pmfiEnum = new CMergedFldrItem;
  2370. if (pmfiEnum)
  2371. {
  2372. LPITEMIDLIST pidlWrap;
  2373. if (SUCCEEDED(_CreateWrap(pidlRealRel1, (UINT)iShellFolder1, &pidlWrap)) &&
  2374. pmfiEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, iShellFolder1))
  2375. {
  2376. SEARCH_FOR_PIDL sfp;
  2377. sfp.pszDisplayName = pmfiEnum->GetDisplayName();
  2378. sfp.fFolder = fFolder;
  2379. sfp.self = this;
  2380. sfp.iNamespace = -1;
  2381. int iInsertIndex = DPA_Search(_hdpaObjects, &sfp, 0,
  2382. _SearchByName, NULL, DPAS_SORTED | DPAS_INSERTAFTER);
  2383. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Creating new unmerged %s at %d", pmfiEnum->GetDisplayName(), iInsertIndex);
  2384. if (iInsertIndex < 0)
  2385. iInsertIndex = DA_LAST;
  2386. if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, pmfiEnum) == -1)
  2387. {
  2388. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Was unable to add %s for some reason. Bailing",
  2389. pmfiEnum->GetDisplayName());
  2390. delete pmfiEnum;
  2391. }
  2392. else
  2393. {
  2394. *ppidlOut1 = _ILCombineBase(pidl1, pmfiEnum->GetIDList());
  2395. }
  2396. }
  2397. else
  2398. delete pmfiEnum;
  2399. }
  2400. }
  2401. }
  2402. break;
  2403. case SHCNE_DELETE:
  2404. case SHCNE_RMDIR:
  2405. {
  2406. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s", fFolder?
  2407. TEXT("SHCNE_RMDIR") : TEXT("SHCNE_DELETE"));
  2408. int iDeleteIndex;
  2409. // Is there a folder of this name already?
  2410. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1,
  2411. fFolder, &iDeleteIndex, &pmfi))
  2412. {
  2413. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Found %s checking merge state.", pmfi->GetDisplayName());
  2414. // Yes; Then we need to unmerge this pidl from the wrapped pidl, and change this
  2415. // to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
  2416. // as the second arg. I have to be careful about the freeing:
  2417. // Free *ppidlOut1
  2418. // Clone pmfi->GetIDList() -> *ppidlOut1.
  2419. // Remove pidl1 from pmfi->_GetIDList()
  2420. // Convert to rename, pass new wrapped as second arg.
  2421. if (_GetSourceCount(pmfi->GetIDList()) > 1)
  2422. {
  2423. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is Merged. Removing pidl, convert to rename", pmfi->GetDisplayName());
  2424. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2425. if (*ppidlOut1)
  2426. {
  2427. LPITEMIDLIST pidlFree = pmfi->GetIDList();
  2428. if (SUCCEEDED(_WrapRemoveIDList(pidlFree, iShellFolder1, &pmfi->_pidlWrap)))
  2429. {
  2430. ILFree(pidlFree);
  2431. }
  2432. *ppidlOut2 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2433. if (!*ppidlOut2)
  2434. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl2");
  2435. *plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2436. }
  2437. else
  2438. {
  2439. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl1");
  2440. }
  2441. }
  2442. else
  2443. {
  2444. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is not Merged. deleteing", pmfi->GetDisplayName());
  2445. pmfi = (CMergedFldrItem*)DPA_DeletePtr(_hdpaObjects, iDeleteIndex);
  2446. if (EVAL(pmfi))
  2447. {
  2448. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2449. delete pmfi;
  2450. }
  2451. else
  2452. {
  2453. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to get %d from DPA", iDeleteIndex);
  2454. }
  2455. }
  2456. }
  2457. }
  2458. break;
  2459. case SHCNE_RENAMEITEM:
  2460. case SHCNE_RENAMEFOLDER:
  2461. {
  2462. // REARCHITECT: (lamadio): When renaming an item in the menu, this code will split it into
  2463. // a Delete and a Create. We need to detect this situation and convert it to 1 rename. This
  2464. // will solve the problem of the lost order during a rename....
  2465. BOOL fEvent1Set = FALSE;
  2466. BOOL fFirstPidlInNamespace = FALSE;
  2467. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s", fFolder?
  2468. TEXT("SHCNE_RENAMEFOLDER") : TEXT("SHCNE_RENAMEITEM"));
  2469. // Is this item being renamed FROM the Folder?
  2470. if (iShellFolder1 != -1)
  2471. {
  2472. // Is this pidl a child of the Folder?
  2473. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1,
  2474. fFolder, &iIndex, &pmfi)) // Is it found?
  2475. {
  2476. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Old pidl %s is in the Folder", pmfi->GetDisplayName());
  2477. // Yes.
  2478. // Then we need to see if the item that it's being renamed from was Merged
  2479. // Need this for reentrancy
  2480. if (_ContainsSrcID(pmfi->GetIDList(), iShellFolder1))
  2481. {
  2482. // Was it merged?
  2483. if (_GetSourceCount(pmfi->GetIDList()) > 1) // Case 3)
  2484. {
  2485. // Yes; Then we need to unmerge that item.
  2486. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2487. if (*ppidlOut1)
  2488. {
  2489. LPITEMIDLIST pidlFree = pmfi->GetIDList();
  2490. if (SUCCEEDED(_WrapRemoveIDList(pidlFree, iShellFolder1, &pmfi->_pidlWrap)))
  2491. {
  2492. ILFree(pidlFree);
  2493. }
  2494. *ppidlOut2 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2495. if (!*ppidlOut2)
  2496. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl2");
  2497. // This We need to "Rename" the old wrapped pidl, to this new one
  2498. // that does not contain the old item.
  2499. fEvent1Set = TRUE;
  2500. }
  2501. else
  2502. {
  2503. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl1");
  2504. }
  2505. }
  2506. else
  2507. {
  2508. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is not merged. Nuking item Convert to Delete for event 1.",
  2509. pmfi->GetDisplayName());
  2510. // No, This was not a wrapped pidl. Then, convert to a delete:
  2511. pmfi = (CMergedFldrItem*)DPA_DeletePtr(_hdpaObjects, iIndex);
  2512. if (EVAL(pmfi))
  2513. {
  2514. // If we're renaming from this folder, into this folder, Then the first event stays a rename.
  2515. if (iShellFolder2 == -1)
  2516. {
  2517. fEvent1Set = TRUE;
  2518. *plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE;
  2519. }
  2520. else
  2521. {
  2522. fFirstPidlInNamespace = TRUE;
  2523. }
  2524. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2525. delete pmfi;
  2526. }
  2527. else
  2528. {
  2529. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to find Item at %d", iIndex);
  2530. }
  2531. }
  2532. }
  2533. else
  2534. {
  2535. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Skipping this because we already processed it."
  2536. "Dragging To Desktop?");
  2537. hr = E_FAIL;
  2538. }
  2539. }
  2540. else
  2541. {
  2542. // we were told to rename something that is supposed to exist in the first
  2543. // namespace, but we couldn't find it.
  2544. // we dont want to have the caller fire off some more events because of this,
  2545. // so we fail.
  2546. hr = E_FAIL;
  2547. }
  2548. }
  2549. // Is this item is being rename INTO the Start Menu?
  2550. if (iShellFolder2 != -1)
  2551. {
  2552. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: New pidl is in the Folder");
  2553. LPITEMIDLIST* ppidlNewWrapped1 = ppidlOut1;
  2554. LPITEMIDLIST* ppidlNewWrapped2 = ppidlOut2;
  2555. LONG* plNewEvent = plEvent;
  2556. if (fEvent1Set)
  2557. {
  2558. plNewEvent = plEvent2;
  2559. ppidlNewWrapped1 = ppidlOut1Event2;
  2560. ppidlNewWrapped2 = ppidlOut2Event2;
  2561. }
  2562. if (S_OK == _SearchForPidl(iShellFolder2, pns2, pidlRealRel2,
  2563. fFolder, &iIndex, &pmfi))
  2564. {
  2565. // If we're renaming from this folder, into this folder, Check to see if the destination has a
  2566. // conflict. If there is a confict (This case), then convert first event to a remove,
  2567. // and the second event to the rename.
  2568. if (fFirstPidlInNamespace)
  2569. {
  2570. fEvent1Set = TRUE;
  2571. *plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE;
  2572. plNewEvent = plEvent2;
  2573. ppidlNewWrapped1 = ppidlOut1Event2;
  2574. ppidlNewWrapped2 = ppidlOut2Event2;
  2575. }
  2576. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is in Folder", pmfi->GetDisplayName());
  2577. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Adding pidl to %s. Convert to Rename for event %s",
  2578. pmfi->GetDisplayName(), fEvent1Set? TEXT("2") : TEXT("1"));
  2579. // Then the destination needs to be merged.
  2580. *ppidlNewWrapped1 = _ILCombineBase(pidl2, pmfi->GetIDList());
  2581. if (*ppidlNewWrapped1)
  2582. {
  2583. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Successfully created out pidl1");
  2584. _WrapAddIDList(pidlRealRel2, iShellFolder2, &pmfi->_pidlWrap);
  2585. *ppidlNewWrapped2 = _ILCombineBase(pidl2, pmfi->GetIDList());
  2586. *plNewEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2587. }
  2588. }
  2589. else
  2590. {
  2591. CMergedFldrItem* pmfiEnum = new CMergedFldrItem;
  2592. if (pmfiEnum)
  2593. {
  2594. LPITEMIDLIST pidlWrap;
  2595. if (SUCCEEDED(_CreateWrap(pidlRealRel2, (UINT)iShellFolder2, &pidlWrap)) &&
  2596. pmfiEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, iShellFolder2))
  2597. {
  2598. SEARCH_FOR_PIDL sfp;
  2599. sfp.pszDisplayName = pmfiEnum->GetDisplayName();
  2600. sfp.fFolder = BOOLIFY(pmfiEnum->GetFolderAttrib() & SFGAO_FOLDER);
  2601. sfp.self = this;
  2602. sfp.iNamespace = -1;
  2603. int iInsertIndex = DPA_Search(_hdpaObjects, &sfp, 0,
  2604. _SearchByName, NULL,
  2605. DPAS_SORTED | DPAS_INSERTAFTER);
  2606. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is a new item. Converting to Create", pmfiEnum->GetDisplayName());
  2607. if (iInsertIndex < 0)
  2608. iInsertIndex = DA_LAST;
  2609. if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, pmfiEnum) == -1)
  2610. {
  2611. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Was unable to add %s for some reason. Bailing",
  2612. pmfiEnum->GetDisplayName());
  2613. delete pmfiEnum;
  2614. }
  2615. else
  2616. {
  2617. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Creating new item %s at %d for event %s",
  2618. pmfiEnum->GetDisplayName(), iInsertIndex, fEvent1Set? TEXT("2") : TEXT("1"));
  2619. // If we're renaming from this folder, into this folder, Then the first event stays
  2620. // a rename.
  2621. if (!fFirstPidlInNamespace)
  2622. {
  2623. *plNewEvent = fFolder ? SHCNE_MKDIR : SHCNE_CREATE;
  2624. *ppidlNewWrapped1 = _ILCombineBase(pidl2, pidlWrap);
  2625. *ppidlNewWrapped2 = NULL;
  2626. }
  2627. else
  2628. *ppidlOut2 = _ILCombineBase(pidl2, pidlWrap);
  2629. }
  2630. }
  2631. else
  2632. delete pmfiEnum;
  2633. }
  2634. }
  2635. }
  2636. }
  2637. break;
  2638. default:
  2639. break;
  2640. }
  2641. Cleanup:
  2642. ILFree(pidlReal1);
  2643. ILFree(pidlReal2);
  2644. return hr;
  2645. }
  2646. // parsedisplayname with some extras.
  2647. // this is used to process change notifies. the change notify can be fired AFTER the
  2648. // item has been moved/deleted/whatever, but we still have to be able to correctly process that
  2649. // pidl. so here pidlAbsNamespace identifies the absolute pidl of the item being changenotified.
  2650. // if we're parsing a name within that namespace, force the parsedisplayname with STGM_CREATE to
  2651. // make sure we get the pidl (because it may have been moved or deleted by now) if fForce==TRUE.
  2652. HRESULT CMergedFolder::_ForceParseDisplayName(LPCITEMIDLIST pidlAbsNamespace, LPTSTR pszDisplayName, BOOL fForce, BOOL *pfOthersInWrap, LPITEMIDLIST *ppidl)
  2653. {
  2654. *ppidl = NULL;
  2655. *pfOthersInWrap = FALSE;
  2656. HRESULT hr = S_OK;
  2657. // set up a bindctx: will have STGM_CREATE if fForce is passed, NULL otherwise
  2658. IBindCtx *pbc;
  2659. if (fForce)
  2660. {
  2661. hr = BindCtx_CreateWithMode(STGM_CREATE, &pbc);
  2662. }
  2663. else
  2664. {
  2665. pbc = NULL;
  2666. }
  2667. if (SUCCEEDED(hr))
  2668. {
  2669. CMergedFldrNamespace *pnsWrap = NULL;
  2670. CMergedFldrNamespace *pnsLoop;
  2671. for (int i = 0; SUCCEEDED(hr) && (pnsLoop = _Namespace(i)); i++)
  2672. {
  2673. HRESULT hrLoop = E_FAIL; // not propagated since ParseDisplayName can fail and that's okay
  2674. LPITEMIDLIST pidlNamespace;
  2675. // so if the namespace pnsLoop is a parent to the pidl, we know it
  2676. // came from there so use the bindctx to force creation if necessary
  2677. if (ILIsParent(pnsLoop->GetIDList(), pidlAbsNamespace, FALSE))
  2678. {
  2679. hrLoop = pnsLoop->Folder()->ParseDisplayName(NULL, pbc, pszDisplayName, NULL, &pidlNamespace, NULL);
  2680. }
  2681. else if (_ShouldMergeNamespaces(pnsWrap, pnsLoop))
  2682. {
  2683. pnsWrap = pnsLoop;
  2684. // only if we're merging, tack on other namespace's pidls.
  2685. hrLoop = pnsLoop->Folder()->ParseDisplayName(NULL, NULL, pszDisplayName, NULL, &pidlNamespace, NULL);
  2686. }
  2687. if (SUCCEEDED(hrLoop))
  2688. {
  2689. if (*ppidl)
  2690. {
  2691. *pfOthersInWrap = TRUE;
  2692. }
  2693. hr = _WrapAddIDList(pidlNamespace, i, ppidl);
  2694. ILFree(pidlNamespace);
  2695. }
  2696. }
  2697. // it could be NULL
  2698. if (pbc)
  2699. {
  2700. pbc->Release();
  2701. }
  2702. }
  2703. return hr;
  2704. }
  2705. // this takes an absolute pidl in the given namespace and converts it to a merged pidl.
  2706. // if the item does not actually exist in the namespace, it forces creation if fForce==TRUE (so if
  2707. // a changenotify comes in on that namespace it will process it normally, even if the
  2708. // underlying item has been moved or deleted).
  2709. HRESULT CMergedFolder::_AbsPidlToAbsWrap(CMergedFldrNamespace *pns, LPCITEMIDLIST pidl, BOOL fForce, BOOL *pfOthersInWrap, LPITEMIDLIST *ppidl)
  2710. {
  2711. LPCITEMIDLIST pidlRel = ILFindChild(pns->GetIDList(), pidl);
  2712. HRESULT hr = SHILClone(_pidl, ppidl);
  2713. if (SUCCEEDED(hr))
  2714. {
  2715. IBindCtx *pbc;
  2716. hr = SHCreateSkipBindCtx(NULL, &pbc); // skip all other binding
  2717. if (SUCCEEDED(hr))
  2718. {
  2719. CMergedFolder *pmfMerged = this;
  2720. pmfMerged->AddRef();
  2721. IShellFolder *psfNamespace = pns->Folder();
  2722. psfNamespace->AddRef();
  2723. HRESULT hrLoop = S_OK;
  2724. while (SUCCEEDED(hr) && SUCCEEDED(hrLoop) && !ILIsEmpty(pidlRel))
  2725. {
  2726. hr = E_OUTOFMEMORY;
  2727. LPITEMIDLIST pidlRelFirst = ILCloneFirst(pidlRel);
  2728. if (pidlRelFirst)
  2729. {
  2730. TCHAR szRelPath[MAX_PATH];
  2731. hr = DisplayNameOf(psfNamespace, pidlRelFirst, SHGDN_FORPARSING | SHGDN_INFOLDER, szRelPath, ARRAYSIZE(szRelPath));
  2732. if (SUCCEEDED(hr))
  2733. {
  2734. LPITEMIDLIST pidlNextPart;
  2735. hr = pmfMerged->_ForceParseDisplayName(pidl, szRelPath, fForce, pfOthersInWrap, &pidlNextPart);
  2736. if (SUCCEEDED(hr))
  2737. {
  2738. // shilappend frees pidlnextpart
  2739. hr = SHILAppend(pidlNextPart, ppidl);
  2740. }
  2741. }
  2742. if (SUCCEEDED(hr))
  2743. {
  2744. // advance and clean up
  2745. IShellFolder *psfFree = psfNamespace;
  2746. psfNamespace = NULL;
  2747. hrLoop = psfFree->BindToObject(pidlRelFirst, pbc, IID_PPV_ARG(IShellFolder, &psfNamespace));
  2748. psfFree->Release();
  2749. if (SUCCEEDED(hrLoop))
  2750. {
  2751. CMergedFolder *pmfFree = pmfMerged;
  2752. pmfMerged = NULL;
  2753. hrLoop = pmfFree->BindToObject(ILFindLastID(*ppidl), pbc, CLSID_MergedFolder, (void **) &pmfMerged);
  2754. pmfFree->Release();
  2755. }
  2756. pidlRel = ILGetNext(pidlRel);
  2757. }
  2758. ILFree(pidlRelFirst);
  2759. }
  2760. }
  2761. if (pmfMerged)
  2762. pmfMerged->Release();
  2763. if (psfNamespace)
  2764. psfNamespace->Release();
  2765. pbc->Release();
  2766. }
  2767. }
  2768. return hr;
  2769. }
  2770. // new translateids.
  2771. // when the start menu works a little better this whole interface can be revised.
  2772. HRESULT CMergedFolder::_NewTranslateIDs(
  2773. LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
  2774. LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2,
  2775. LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
  2776. LPITEMIDLIST *ppidlOut2Event2)
  2777. {
  2778. if (!plEvent || !ppidlOut1 || !ppidlOut2)
  2779. return E_INVALIDARG;
  2780. // If they are already wrapped, don't wrap twice.
  2781. if (SUCCEEDED(_IsWrap(ILFindLastID(pidl1))) ||
  2782. SUCCEEDED(_IsWrap(ILFindLastID(pidl2))))
  2783. {
  2784. // We don't want to wrap twice.
  2785. return E_INVALIDARG;
  2786. }
  2787. if (!_hdpaNamespaces)
  2788. return E_FAIL;
  2789. HRESULT hr = E_FAIL;
  2790. switch (*plEvent)
  2791. {
  2792. case SHCNE_EXTENDED_EVENT:
  2793. case SHCNE_ASSOCCHANGED:
  2794. case SHCNE_UPDATEIMAGE:
  2795. return S_OK;
  2796. }
  2797. LONG lEvent = *plEvent;
  2798. *plEvent2 = (LONG)-1;
  2799. *ppidlOut1Event2 = NULL;
  2800. *ppidlOut2Event2 = NULL;
  2801. *ppidlOut1 = (LPITEMIDLIST)pidl1;
  2802. *ppidlOut2 = (LPITEMIDLIST)pidl2;
  2803. CMergedFldrNamespace *pns1, *pns2;
  2804. int iShellFolder1, iShellFolder2;
  2805. BOOL fPidl1IsChild, fPidl2IsChild;
  2806. // Get the information about these Simple pidls: Are they our Children? If so, what _Namespace?
  2807. fPidl1IsChild = _IsChildIDInternal(pidl1, FALSE, &iShellFolder1);
  2808. if (fPidl1IsChild)
  2809. {
  2810. pns1 = _Namespace(iShellFolder1);
  2811. }
  2812. fPidl2IsChild = _IsChildIDInternal(pidl2, FALSE, &iShellFolder2);
  2813. if (fPidl2IsChild)
  2814. {
  2815. pns2 = _Namespace(iShellFolder2);
  2816. }
  2817. // and is either a child?
  2818. if (fPidl1IsChild || fPidl2IsChild)
  2819. {
  2820. hr = S_OK;
  2821. BOOL fOthersInNamespace1, fOthersInNamespace2;
  2822. BOOL fFolderEvent = FALSE;
  2823. switch (*plEvent)
  2824. {
  2825. case SHCNE_MKDIR:
  2826. fFolderEvent = TRUE;
  2827. case SHCNE_CREATE:
  2828. if (fPidl1IsChild)
  2829. {
  2830. hr = _AbsPidlToAbsWrap(pns1, pidl1, FALSE, &fOthersInNamespace1, ppidlOut1);
  2831. if (SUCCEEDED(hr) && fOthersInNamespace1 && !_fDontMerge)
  2832. {
  2833. // whoops, it was already here and this create should become a rename
  2834. // since we're just merging in with the existing pidl.
  2835. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2836. // this new wrapped one is what it's renamed TO so bump it to ppidlOut2.
  2837. *ppidlOut2 = *ppidlOut1;
  2838. // strip it to get what it used to be.
  2839. hr = _WrapRemoveIDListAbs(*ppidlOut2, iShellFolder1, ppidlOut1);
  2840. }
  2841. }
  2842. break;
  2843. case SHCNE_RMDIR:
  2844. fFolderEvent = TRUE;
  2845. case SHCNE_DELETE:
  2846. if (fPidl1IsChild)
  2847. {
  2848. hr = _AbsPidlToAbsWrap(pns1, pidl1, TRUE, &fOthersInNamespace1, ppidlOut1);
  2849. if (SUCCEEDED(hr) && fOthersInNamespace1 && !_fDontMerge)
  2850. {
  2851. // whoops, there are still more parts to it and it should become a rename
  2852. // since we're just unmerging from the existing pidl.
  2853. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2854. // strip it to get the "deleted" version
  2855. hr = _WrapRemoveIDListAbs(*ppidlOut1, iShellFolder1, ppidlOut2);
  2856. }
  2857. }
  2858. break;
  2859. case SHCNE_RENAMEFOLDER:
  2860. fFolderEvent = TRUE;
  2861. case SHCNE_RENAMEITEM:
  2862. if (fPidl1IsChild)
  2863. {
  2864. // this is just like a delete.
  2865. *plEvent = fFolderEvent ? SHCNE_RMDIR : SHCNE_DELETE;
  2866. hr = _AbsPidlToAbsWrap(pns1, pidl1, TRUE, &fOthersInNamespace1, ppidlOut1);
  2867. if (SUCCEEDED(hr) && fOthersInNamespace1 && !_fDontMerge)
  2868. {
  2869. // whoops, there are still more parts to it and it should become a rename
  2870. // since we're just unmerging from the existing pidl.
  2871. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2872. // strip it to get the "deleted" version
  2873. hr = _WrapRemoveIDListAbs(*ppidlOut1, iShellFolder1, ppidlOut2);
  2874. }
  2875. // set ourselves up so that if fPidl2IsChild, it will write into the second event.
  2876. plEvent = plEvent2;
  2877. ppidlOut1 = ppidlOut1Event2;
  2878. ppidlOut2 = ppidlOut2Event2;
  2879. }
  2880. if (fPidl2IsChild)
  2881. {
  2882. // this is just like a create.
  2883. *plEvent = fFolderEvent ? SHCNE_MKDIR : SHCNE_CREATE;
  2884. hr = _AbsPidlToAbsWrap(pns2, pidl2, FALSE, &fOthersInNamespace2, ppidlOut1);
  2885. if (SUCCEEDED(hr) && fOthersInNamespace2 && !_fDontMerge)
  2886. {
  2887. // whoops, it was already here and this create should become a rename
  2888. // since we're just merging in with the existing pidl.
  2889. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2890. // this new wrapped one is what it's renamed TO so bump it to ppidlOut2.
  2891. *ppidlOut2 = *ppidlOut1;
  2892. // strip it to get what it used to be.
  2893. hr = _WrapRemoveIDListAbs(*ppidlOut2, iShellFolder2, ppidlOut1);
  2894. }
  2895. }
  2896. break;
  2897. case SHCNE_UPDATEDIR:
  2898. case SHCNE_UPDATEITEM:
  2899. case SHCNE_MEDIAINSERTED:
  2900. case SHCNE_MEDIAREMOVED:
  2901. hr = _AbsPidlToAbsWrap(pns1, pidl1, FALSE, &fOthersInNamespace1, ppidlOut1);
  2902. break;
  2903. default:
  2904. break;
  2905. }
  2906. }
  2907. return hr;
  2908. }
  2909. STDMETHODIMP CMergedFolder::IsChildID(LPCITEMIDLIST pidlKid, BOOL fImmediate)
  2910. {
  2911. return _IsChildIDInternal(pidlKid, fImmediate, NULL) ? S_OK : S_FALSE;
  2912. }
  2913. STDMETHODIMP CMergedFolder::IsEqualID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2914. {
  2915. // This used to return E_NOTIMPL. I'm kinda overloading the interface to mean:
  2916. // is this equal tp any of your _Namespaces.
  2917. HRESULT hr = S_FALSE;
  2918. CMergedFldrNamespace *pns;
  2919. for (int i = 0; (hr == S_FALSE) && (pns = _Namespace(i)); i++)
  2920. {
  2921. if (pidl1)
  2922. {
  2923. if (ILIsEqual(pns->GetIDList(), pidl1))
  2924. hr = S_OK;
  2925. }
  2926. else if (pidl2) // If you pass a pidl2 it means: Is pidl2 a parent of one of my _Namespaces?
  2927. {
  2928. if (ILIsParent(pidl2, pns->GetIDList(), FALSE))
  2929. hr = S_OK;
  2930. }
  2931. }
  2932. return hr;
  2933. }
  2934. typedef struct
  2935. {
  2936. HWND hwnd;
  2937. UINT uMsg;
  2938. LONG lEvents;
  2939. } REGISTERNOTIFYINFO;
  2940. int CMergedFolder::_SetNotifyProc(void *pv, void *pvParam)
  2941. {
  2942. CMergedFldrNamespace *pns = (CMergedFldrNamespace*)pv;
  2943. if (pvParam)
  2944. {
  2945. REGISTERNOTIFYINFO *prni = (REGISTERNOTIFYINFO*)pvParam;
  2946. pns->RegisterNotify(prni->hwnd, prni->uMsg, prni->lEvents);
  2947. }
  2948. else
  2949. {
  2950. pns->UnregisterNotify();
  2951. }
  2952. return 1;
  2953. }
  2954. STDMETHODIMP CMergedFolder::Register(HWND hwnd, UINT uMsg, long lEvents)
  2955. {
  2956. if (_fInShellView)
  2957. {
  2958. // only register the alias if we have no parent folder
  2959. // the merged folder at the junction point can take care of the registration
  2960. // for everybody.
  2961. if (_pidl && !_pmfParent)
  2962. {
  2963. CMergedFldrNamespace *pns;
  2964. for (int i = 0; pns = _Namespace(i); i++)
  2965. {
  2966. if (!ILIsEqual(pns->GetIDList(), _pidl))
  2967. {
  2968. SHChangeNotifyRegisterAlias(pns->GetIDList(), _pidl);
  2969. }
  2970. }
  2971. }
  2972. }
  2973. else if (_hdpaNamespaces)
  2974. {
  2975. REGISTERNOTIFYINFO rni = {hwnd, uMsg, lEvents};
  2976. DPA_EnumCallback(_hdpaNamespaces, _SetNotifyProc, &rni);
  2977. }
  2978. return S_OK;
  2979. }
  2980. STDMETHODIMP CMergedFolder::Unregister()
  2981. {
  2982. if (_hdpaNamespaces)
  2983. {
  2984. DPA_EnumCallback(_hdpaNamespaces, _SetNotifyProc, NULL);
  2985. }
  2986. return S_OK;
  2987. }
  2988. BOOL CMergedFolder::_IsChildIDInternal(LPCITEMIDLIST pidlKid, BOOL fImmediate, int* piShellFolder)
  2989. {
  2990. // This is basically the same Method as the interface method, but returns the shell folder
  2991. // that it came from.
  2992. BOOL fChild = FALSE;
  2993. //At this point we should have a translated pidl
  2994. if (SUCCEEDED(_IsWrap(pidlKid)))
  2995. {
  2996. LPCITEMIDLIST pidlRelKid = ILFindLastID(pidlKid);
  2997. if (pidlRelKid)
  2998. {
  2999. CMergedFldrNamespace *pns;
  3000. for (int i = 0; !fChild && (pns = _Namespace(i)); i++)
  3001. {
  3002. if (ILIsParent(pns->GetIDList(), pidlKid, fImmediate) &&
  3003. !ILIsEqual(pns->GetIDList(), pidlKid))
  3004. {
  3005. fChild = TRUE;
  3006. if (piShellFolder)
  3007. *piShellFolder = i;
  3008. }
  3009. }
  3010. }
  3011. }
  3012. else if (pidlKid)
  3013. {
  3014. CMergedFldrNamespace *pns;
  3015. for (int i = 0; !fChild && (pns = _Namespace(i)); i++)
  3016. {
  3017. if (ILIsParent(pns->GetIDList(), pidlKid, fImmediate))
  3018. {
  3019. fChild = TRUE;
  3020. if (piShellFolder)
  3021. *piShellFolder = i;
  3022. }
  3023. }
  3024. }
  3025. return fChild;
  3026. }
  3027. HRESULT CMergedFolder::_SearchForPidl(int iNamespace, CMergedFldrNamespace *pns, LPCITEMIDLIST pidl, BOOL fFolder, int* piIndex, CMergedFldrItem** ppmfi)
  3028. {
  3029. int iIndex = -1;
  3030. *ppmfi = NULL;
  3031. TCHAR szDisplayName[MAX_PATH];
  3032. if (SUCCEEDED(DisplayNameOf(pns->Folder(), pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szDisplayName, ARRAYSIZE(szDisplayName))))
  3033. {
  3034. SEARCH_FOR_PIDL sfp;
  3035. sfp.pszDisplayName = szDisplayName;
  3036. sfp.fFolder = fFolder;
  3037. sfp.self = this;
  3038. if (_fPartialMerge)
  3039. {
  3040. sfp.iNamespace = iNamespace;
  3041. }
  3042. else
  3043. {
  3044. sfp.iNamespace = -1;
  3045. }
  3046. iIndex = DPA_Search(_hdpaObjects, &sfp, 0, _SearchByName, NULL, DPAS_SORTED);
  3047. if (iIndex >= 0)
  3048. {
  3049. *ppmfi = _GetObject(iIndex);
  3050. }
  3051. }
  3052. if (piIndex)
  3053. *piIndex = iIndex;
  3054. if (*ppmfi)
  3055. return S_OK;
  3056. return S_FALSE;
  3057. }
  3058. HRESULT CMergedFolder::_GetTargetUIObjectOf(IShellFolder *psf, HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgf, void **ppv)
  3059. {
  3060. *ppv = NULL;
  3061. IPersistFolder3 *ppf3;
  3062. HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3));
  3063. if (SUCCEEDED(hr))
  3064. {
  3065. PERSIST_FOLDER_TARGET_INFO pfti;
  3066. hr = ppf3->GetFolderTargetInfo(&pfti);
  3067. if (SUCCEEDED(hr) && pfti.pidlTargetFolder)
  3068. {
  3069. IShellFolder *psfTarget;
  3070. hr = SHBindToObjectEx(NULL, pfti.pidlTargetFolder, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
  3071. if (SUCCEEDED(hr))
  3072. {
  3073. DWORD dwAttrib = SFGAO_VALIDATE;
  3074. hr = psfTarget->GetAttributesOf(cidl, apidl, &dwAttrib);
  3075. if (SUCCEEDED(hr))
  3076. {
  3077. hr = psfTarget->GetUIObjectOf(hwnd, cidl, apidl, riid, prgf, ppv);
  3078. }
  3079. psfTarget->Release();
  3080. }
  3081. ILFree(pfti.pidlTargetFolder);
  3082. }
  3083. else
  3084. {
  3085. hr = E_FAIL;
  3086. }
  3087. ppf3->Release();
  3088. }
  3089. if (FAILED(hr))
  3090. {
  3091. DWORD dwAttrib = SFGAO_VALIDATE;
  3092. hr = psf->GetAttributesOf(cidl, apidl, &dwAttrib);
  3093. if (SUCCEEDED(hr))
  3094. {
  3095. hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgf, ppv);
  3096. }
  3097. }
  3098. return hr;
  3099. }
  3100. HRESULT CMergedFolder::_GetContextMenu(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, void **ppv)
  3101. {
  3102. HRESULT hr = E_OUTOFMEMORY;
  3103. UINT iNumCommon = 0;
  3104. LPITEMIDLIST *apidlCommon = new LPITEMIDLIST[cidl];
  3105. if (apidlCommon)
  3106. {
  3107. UINT iNumUser = 0;
  3108. LPITEMIDLIST *apidlUser = new LPITEMIDLIST[cidl];
  3109. if (apidlUser)
  3110. {
  3111. IShellFolder *psf, *psfCommon, *psfUser; // not ref counted
  3112. LPITEMIDLIST pidl;
  3113. for (UINT i = 0; i < cidl; i++)
  3114. {
  3115. if (SUCCEEDED(_NamespaceForItem(apidl[i], ASFF_COMMON, ASFF_COMMON, &psf, &pidl, NULL, TRUE)))
  3116. {
  3117. apidlCommon[iNumCommon++] = pidl;
  3118. psfCommon = psf;
  3119. }
  3120. if (SUCCEEDED(_NamespaceForItem(apidl[i], ASFF_COMMON, 0, &psf, &pidl, NULL, TRUE)))
  3121. {
  3122. apidlUser[iNumUser++] = pidl;
  3123. psfUser = psf;
  3124. }
  3125. }
  3126. IContextMenu *pcmCommon = NULL;
  3127. if (iNumCommon)
  3128. {
  3129. _GetTargetUIObjectOf(psfCommon, hwnd, iNumCommon, (LPCITEMIDLIST *)apidlCommon, IID_X_PPV_ARG(IContextMenu, NULL, &pcmCommon));
  3130. }
  3131. IContextMenu *pcmUser = NULL;
  3132. if (iNumUser)
  3133. {
  3134. _GetTargetUIObjectOf(psfUser, hwnd, iNumUser, (LPCITEMIDLIST *)apidlUser, IID_X_PPV_ARG(IContextMenu, NULL, &pcmUser));
  3135. }
  3136. BOOL fMerge = _fInShellView || (cidl == 1) && _IsFolder(apidl[0]);
  3137. if (fMerge && (pcmCommon || pcmUser))
  3138. {
  3139. hr = CMergedFldrContextMenu_CreateInstance(hwnd, this, cidl, apidl, pcmCommon, pcmUser, (IContextMenu**)ppv);
  3140. }
  3141. else if (pcmUser)
  3142. {
  3143. hr = pcmUser->QueryInterface(riid, ppv);
  3144. }
  3145. else if (pcmCommon)
  3146. {
  3147. hr = pcmCommon->QueryInterface(riid, ppv);
  3148. }
  3149. else
  3150. {
  3151. hr = E_FAIL;
  3152. }
  3153. for (i = 0; i < iNumCommon; i++)
  3154. {
  3155. ILFree(apidlCommon[i]);
  3156. }
  3157. for (i = 0; i < iNumUser; i++)
  3158. {
  3159. ILFree(apidlUser[i]);
  3160. }
  3161. ATOMICRELEASE(pcmCommon);
  3162. ATOMICRELEASE(pcmUser);
  3163. delete [] apidlUser;
  3164. }
  3165. delete [] apidlCommon;
  3166. }
  3167. return hr;
  3168. }
  3169. // out:
  3170. // *ppsf unreffed psf, don't call ->Release()!
  3171. // *ppidlItem clone of pidl in pidlWrap (nested pidl)
  3172. HRESULT CMergedFolder::_NamespaceForItem(LPCITEMIDLIST pidlWrap, ULONG dwAttribMask, ULONG dwAttrib,
  3173. IShellFolder **ppsf, LPITEMIDLIST *ppidlItem, CMergedFldrNamespace **ppns, BOOL fExact)
  3174. {
  3175. if (ppsf)
  3176. *ppsf = NULL;
  3177. if (ppns)
  3178. *ppns = NULL;
  3179. // first try to get the prefered name space based
  3180. HRESULT hr = E_UNEXPECTED; // assume failure from here
  3181. CMergedFldrNamespace *pns;
  3182. LPITEMIDLIST pidl;
  3183. for (UINT i = 0; SUCCEEDED(_GetSubPidl(pidlWrap, i, NULL, &pidl, &pns)); i++)
  3184. {
  3185. if ((dwAttribMask & dwAttrib) == (dwAttribMask & pns->FolderAttrib()))
  3186. {
  3187. hr = S_OK; // don't free, we're going to hand this one back.
  3188. break;
  3189. }
  3190. ILFree(pidl);
  3191. }
  3192. // not found, fall back to the first name space in the wrapped pidl
  3193. if (!fExact && FAILED(hr))
  3194. {
  3195. hr = _GetSubPidl(pidlWrap, 0, NULL, &pidl, &pns);
  3196. }
  3197. // it succeeded, so lets pass out the information that the caller wanted
  3198. if (SUCCEEDED(hr))
  3199. {
  3200. if (ppsf)
  3201. *ppsf = pns->Folder();
  3202. if (ppns)
  3203. *ppns = pns;
  3204. if (ppidlItem)
  3205. {
  3206. // transfer ownership
  3207. *ppidlItem = pidl;
  3208. }
  3209. else
  3210. {
  3211. ILFree(pidl);
  3212. }
  3213. }
  3214. return hr;
  3215. }
  3216. BOOL CMergedFolder::_NamespaceMatches(ULONG dwAttribMask, ULONG dwAttrib, LPCGUID pguid, CMergedFldrNamespace *pns)
  3217. {
  3218. BOOL fRet = FALSE;
  3219. dwAttrib &= dwAttribMask;
  3220. if (dwAttrib == (dwAttribMask & pns->FolderAttrib()))
  3221. {
  3222. // If ASFF_MERGESAMEGUID is set, then the GUID must also match
  3223. if (!(dwAttrib & ASFF_MERGESAMEGUID) ||
  3224. IsEqualGUID(*pguid, pns->GetGUID()))
  3225. {
  3226. fRet = TRUE;
  3227. }
  3228. }
  3229. return fRet;
  3230. }
  3231. // find a name space based on its attributes
  3232. // dwAttribMask is a mask of the bits to test
  3233. // dwAttrib is the value of the bits in the mask, tested for equality
  3234. // pguid is the GUID to match if dwAttrib includes ASFF_MERGESAMEGUID
  3235. //
  3236. // pnSrcID is optional out param
  3237. HRESULT CMergedFolder::_FindNamespace(ULONG dwAttribMask, ULONG dwAttrib, LPCGUID pguid,
  3238. CMergedFldrNamespace **ppns, BOOL fFallback)
  3239. {
  3240. *ppns = NULL;
  3241. CMergedFldrNamespace *pns;
  3242. int i;
  3243. // first look for a matching namespace.
  3244. for (i = 0; !*ppns && (pns = _Namespace(i)); i++)
  3245. {
  3246. if (!_ShouldSuspend(pns->GetGUID()) && _NamespaceMatches(dwAttribMask, dwAttrib, pguid, pns))
  3247. {
  3248. *ppns = pns;
  3249. }
  3250. }
  3251. if (fFallback && !*ppns)
  3252. {
  3253. // if the matching namespace got scoped out, fall back to another.
  3254. for (i = 0; !*ppns && (pns = _Namespace(i)); i++)
  3255. {
  3256. if (!_ShouldSuspend(pns->GetGUID()))
  3257. {
  3258. *ppns = pns;
  3259. }
  3260. }
  3261. }
  3262. return *ppns ? S_OK : E_FAIL;
  3263. }
  3264. // lamadio: Move this to a better location, This is a nice generic function
  3265. #ifdef DEBUG
  3266. BOOL DPA_VerifySorted(HDPA hdpa, PFNDPACOMPARE pfn, LPARAM lParam)
  3267. {
  3268. if (!EVAL(hdpa))
  3269. return FALSE;
  3270. for (int i = 0; i < DPA_GetPtrCount(hdpa) - 1; i++)
  3271. {
  3272. if (pfn(DPA_FastGetPtr(hdpa, i), DPA_FastGetPtr(hdpa, i + 1), lParam) > 0)
  3273. return FALSE;
  3274. }
  3275. return TRUE;
  3276. }
  3277. #else
  3278. #define DPA_VerifySorted(hdpa, pfn, lParam) TRUE
  3279. #endif
  3280. int CMergedFolder::_AcquireObjects()
  3281. {
  3282. _fAcquiring = TRUE;
  3283. HDPA hdpa2 = NULL;
  3284. ASSERT(_hdpaObjects == NULL);
  3285. CMergedFldrNamespace *pns;
  3286. for (int i = 0; pns = _Namespace(i); i++)
  3287. {
  3288. if (_ShouldSuspend(pns->GetGUID()))
  3289. {
  3290. continue;
  3291. }
  3292. HDPA *phdpa = (i == 0) ? &_hdpaObjects : &hdpa2;
  3293. IEnumIDList *peid;
  3294. if (S_OK == pns->Folder()->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &peid))
  3295. {
  3296. if (!*phdpa)
  3297. *phdpa = DPA_Create(4);
  3298. if (*phdpa)
  3299. {
  3300. LPITEMIDLIST pidl;
  3301. ULONG cEnum;
  3302. while (S_OK == peid->Next(1, &pidl, &cEnum))
  3303. {
  3304. CMergedFldrItem* pmfiEnum = new CMergedFldrItem;
  3305. if (pmfiEnum)
  3306. {
  3307. // This is ok, the memory just gets
  3308. LPITEMIDLIST pidlWrap;
  3309. if (SUCCEEDED(_CreateWrap(pidl, i, &pidlWrap)) &&
  3310. pmfiEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, i))
  3311. {
  3312. if (DPA_AppendPtr(*phdpa, pmfiEnum) != -1)
  3313. pmfiEnum = NULL; // don't free below
  3314. }
  3315. if (pmfiEnum)
  3316. delete pmfiEnum;
  3317. }
  3318. ILFree(pidl);
  3319. }
  3320. }
  3321. peid->Release();
  3322. }
  3323. // if we have only hdpa2 but not _hdpaObjects, do a switcheroo.
  3324. if (hdpa2 && !_hdpaObjects)
  3325. {
  3326. _hdpaObjects = hdpa2;
  3327. hdpa2 = NULL;
  3328. }
  3329. // now that we have both hdpa's (or one) let's merge them.
  3330. if (_hdpaObjects && hdpa2)
  3331. {
  3332. DPA_Merge(_hdpaObjects, hdpa2, DPAM_UNION, _Compare, _Merge, (LPARAM)this);
  3333. DPA_DestroyCallback(hdpa2, _DestroyObjectsProc, NULL);
  3334. hdpa2 = NULL;
  3335. }
  3336. else if (_hdpaObjects)
  3337. {
  3338. DPA_Sort(_hdpaObjects, _Compare, (LPARAM)this);
  3339. }
  3340. }
  3341. _fAcquiring = FALSE;
  3342. return _ObjectCount();
  3343. }
  3344. int CMergedFolder::_DestroyObjectsProc(void *pv, void *pvData)
  3345. {
  3346. CMergedFldrItem* pmfiEnum = (CMergedFldrItem*)pv;
  3347. if (pmfiEnum)
  3348. delete pmfiEnum;
  3349. return TRUE;
  3350. }
  3351. void CMergedFolder::_FreeObjects()
  3352. {
  3353. if (!_fAcquiring && _hdpaObjects)
  3354. {
  3355. DPA_DestroyCallback(_hdpaObjects, _DestroyObjectsProc, NULL);
  3356. _hdpaObjects = NULL;
  3357. }
  3358. }
  3359. int CMergedFolder::_DestroyNamespacesProc(void *pv, void *pvData)
  3360. {
  3361. CMergedFldrNamespace* p = (CMergedFldrNamespace*)pv;
  3362. if (p)
  3363. delete p;
  3364. return TRUE;
  3365. }
  3366. void CMergedFolder::_FreeNamespaces()
  3367. {
  3368. if (_hdpaNamespaces)
  3369. {
  3370. DPA_DestroyCallback(_hdpaNamespaces, _DestroyNamespacesProc, NULL);
  3371. _hdpaNamespaces = NULL;
  3372. }
  3373. }
  3374. HRESULT CMergedFolder::_GetPidl(int* piPos, DWORD grfEnumFlags, LPITEMIDLIST* ppidl)
  3375. {
  3376. *ppidl = NULL;
  3377. if (_hdpaObjects == NULL)
  3378. _AcquireObjects();
  3379. if (_hdpaObjects == NULL)
  3380. return E_OUTOFMEMORY;
  3381. BOOL fWantFolders = 0 != (grfEnumFlags & SHCONTF_FOLDERS),
  3382. fWantNonFolders = 0 != (grfEnumFlags & SHCONTF_NONFOLDERS),
  3383. fWantHidden = 0 != (grfEnumFlags & SHCONTF_INCLUDEHIDDEN),
  3384. fWantAll = 0 != (grfEnumFlags & SHCONTF_STORAGE);
  3385. HRESULT hr = S_FALSE;
  3386. while (*piPos < _ObjectCount())
  3387. {
  3388. CMergedFldrItem* pmfiEnum = _GetObject(*piPos);
  3389. if (pmfiEnum)
  3390. {
  3391. BOOL fFolder = 0 != (pmfiEnum->GetFolderAttrib() & SFGAO_FOLDER),
  3392. fHidden = 0 != (pmfiEnum->GetFolderAttrib() & SFGAO_HIDDEN);
  3393. if (fWantAll ||
  3394. (!fHidden || fWantHidden) &&
  3395. ((fFolder && fWantFolders) || (!fFolder && fWantNonFolders)))
  3396. {
  3397. // Copy out the pidl
  3398. hr = SHILClone(pmfiEnum->GetIDList(), ppidl);
  3399. break;
  3400. }
  3401. else
  3402. {
  3403. (*piPos)++;
  3404. }
  3405. }
  3406. }
  3407. return hr;
  3408. }
  3409. HRESULT CMergedFolder::_GetOverlayInfo(LPCITEMIDLIST pidl, int *pIndex, DWORD dwFlags)
  3410. {
  3411. int iOverlayIndex = -1;
  3412. HRESULT hr = S_OK;
  3413. CMergedFldrNamespace *pns;
  3414. if (_fCDBurn)
  3415. {
  3416. hr = E_OUTOFMEMORY;
  3417. LPITEMIDLIST pidlInNamespace;
  3418. if (SUCCEEDED(_GetSubPidl(pidl, 0, NULL, &pidlInNamespace, &pns)))
  3419. {
  3420. ASSERTMSG(!_fDontMerge || _GetSourceCount(pidl) == 1, "item for overlay should have exactly one wrapped pidl if not merged");
  3421. LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
  3422. if (pidlFirst)
  3423. {
  3424. if (_GetSourceCount(pidlFirst) > 1)
  3425. {
  3426. // an overlay to indicate overwrite
  3427. iOverlayIndex = pns->GetConflictOverlayIndex();
  3428. }
  3429. if (iOverlayIndex == -1)
  3430. {
  3431. // overlay to indicate staged
  3432. iOverlayIndex = pns->GetDefaultOverlayIndex();
  3433. }
  3434. if (iOverlayIndex == -1)
  3435. {
  3436. // use the one provided by the namespace
  3437. iOverlayIndex = pns->GetNamespaceOverlayIndex(pidlInNamespace);
  3438. }
  3439. ILFree(pidlFirst);
  3440. }
  3441. ILFree(pidlInNamespace);
  3442. }
  3443. }
  3444. ASSERT(pIndex);
  3445. *pIndex = (dwFlags == SIOM_OVERLAYINDEX) ? iOverlayIndex : INDEXTOOVERLAYMASK(iOverlayIndex);
  3446. return hr;
  3447. }
  3448. HRESULT CMergedFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
  3449. {
  3450. return (*pIndex == OI_ASYNC) ? E_PENDING : _GetOverlayInfo(pidl, pIndex, SIOM_OVERLAYINDEX);
  3451. }
  3452. HRESULT CMergedFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex)
  3453. {
  3454. return _GetOverlayInfo(pidl, pIconIndex, SIOM_ICONINDEX);
  3455. }
  3456. STDMETHODIMP CMergedFolder::BindToParent(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPITEMIDLIST *ppidlLast)
  3457. {
  3458. // tybeam: nobody seems to call this any more.
  3459. // if this is ever needed tell me to add it back.
  3460. return E_NOTIMPL;
  3461. }
  3462. HRESULT CMergedFolder::_AddComposite(const COMPFOLDERINIT *pcfi)
  3463. {
  3464. HRESULT hr = E_FAIL;
  3465. LPITEMIDLIST pidl = NULL;
  3466. switch (pcfi->uType)
  3467. {
  3468. case CFITYPE_CSIDL:
  3469. SHGetSpecialFolderLocation(NULL, pcfi->csidl, &pidl);
  3470. break;
  3471. case CFITYPE_PIDL:
  3472. pidl = ILClone(pcfi->pidl);
  3473. break;
  3474. case CFITYPE_PATH:
  3475. pidl = ILCreateFromPath(pcfi->pszPath);
  3476. break;
  3477. default:
  3478. ASSERT(FALSE);
  3479. }
  3480. if (pidl)
  3481. {
  3482. hr = AddNameSpace(NULL, NULL, pidl, ASFF_DEFNAMESPACE_ALL);
  3483. ILFree(pidl);
  3484. }
  3485. return hr;
  3486. }
  3487. STDMETHODIMP CMergedFolder::InitComposite(WORD wSignature, REFCLSID refclsid, CFINITF cfiFlags, ULONG celt, const COMPFOLDERINIT *rgCFs)
  3488. {
  3489. HRESULT hr = S_OK;
  3490. _clsid = refclsid;
  3491. ASSERTMSG(cfiFlags == CFINITF_FLAT, "merged folder doesn't support listing namespaces as children any more");
  3492. if (cfiFlags != CFINITF_FLAT)
  3493. {
  3494. hr = E_INVALIDARG;
  3495. }
  3496. if (SUCCEEDED(hr))
  3497. {
  3498. for (ULONG i = 0; SUCCEEDED(hr) && (i < celt); i++)
  3499. {
  3500. hr = _AddComposite(rgCFs + i);
  3501. }
  3502. }
  3503. return hr;
  3504. }
  3505. STDMETHODIMP CMergedFolder::_DeleteItemByIDList(LPCITEMIDLIST pidl)
  3506. {
  3507. LPITEMIDLIST pidlSrc;
  3508. CMergedFldrNamespace *pns;
  3509. HRESULT hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_BINDSTG, ASFF_DEFNAMESPACE_BINDSTG, NULL, &pidlSrc, &pns, FALSE);
  3510. if (SUCCEEDED(hr))
  3511. {
  3512. IStorage *pstg;
  3513. hr = pns->Folder()->QueryInterface(IID_PPV_ARG(IStorage, &pstg));
  3514. if (SUCCEEDED(hr))
  3515. {
  3516. TCHAR szName[MAX_PATH];
  3517. hr = DisplayNameOf(pns->Folder(), pidlSrc, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3518. if (SUCCEEDED(hr))
  3519. {
  3520. hr = pstg->DestroyElement(szName);
  3521. }
  3522. pstg->Release();
  3523. }
  3524. ILFree(pidlSrc);
  3525. }
  3526. return hr;
  3527. }
  3528. // helper to take a bind context
  3529. // will move to shell\lib if/when there are any other callers (tybeam)
  3530. HRESULT SHBindToParentEx(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast)
  3531. {
  3532. HRESULT hr;
  3533. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  3534. if (pidlParent)
  3535. {
  3536. hr = SHBindToObjectEx(NULL, pidlParent, pbc, riid, ppv);
  3537. ILFree(pidlParent);
  3538. }
  3539. else
  3540. hr = E_OUTOFMEMORY;
  3541. if (ppidlLast)
  3542. *ppidlLast = ILFindLastID(pidl);
  3543. return hr;
  3544. }
  3545. HRESULT CMergedFolder::_GetDestinationStorage(DWORD grfMode, IStorage **ppstg)
  3546. {
  3547. CMergedFldrNamespace *pns;
  3548. HRESULT hr = _FindNamespace(ASFF_DEFNAMESPACE_BINDSTG, ASFF_DEFNAMESPACE_BINDSTG, NULL, &pns, TRUE);
  3549. if (SUCCEEDED(hr))
  3550. {
  3551. IShellFolder *psf;
  3552. LPCITEMIDLIST pidlLast;
  3553. hr = SHBindToIDListParent(pns->GetIDList(), IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  3554. if (SUCCEEDED(hr))
  3555. {
  3556. // SHGetAttributes doesn't help for SFGAO_VALIDATE
  3557. DWORD dwAttrib = SFGAO_VALIDATE;
  3558. hr = psf->GetAttributesOf(1, &pidlLast, &dwAttrib);
  3559. psf->Release();
  3560. }
  3561. }
  3562. if (SUCCEEDED(hr))
  3563. {
  3564. IBindCtx *pbc;
  3565. hr = BindCtx_CreateWithMode(grfMode, &pbc);
  3566. if (SUCCEEDED(hr))
  3567. {
  3568. hr = SHBindToObjectEx(NULL, pns->GetIDList(), pbc, IID_PPV_ARG(IStorage, ppstg));
  3569. pbc->Release();
  3570. }
  3571. }
  3572. else if (_pmfParent)
  3573. {
  3574. // the current pidl doesnt have the storage target namespace, so create it using our parent.
  3575. IStorage *pstgParent;
  3576. hr = _pmfParent->_GetDestinationStorage(grfMode, &pstgParent);
  3577. if (SUCCEEDED(hr))
  3578. {
  3579. TCHAR szName[MAX_PATH];
  3580. hr = DisplayNameOf((IShellFolder*)(void*)_pmfParent, ILFindLastID(_pidl), SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3581. if (SUCCEEDED(hr))
  3582. {
  3583. hr = pstgParent->CreateStorage(szName, STGM_READWRITE, 0, 0, ppstg);
  3584. }
  3585. pstgParent->Release();
  3586. }
  3587. }
  3588. return hr;
  3589. }
  3590. STDMETHODIMP CMergedFolder::_StgCreate(LPCITEMIDLIST pidl, DWORD grfMode, REFIID riid, void **ppv)
  3591. {
  3592. HRESULT hr = S_OK;
  3593. // first we need to ensure that the folder exists.
  3594. if (_pstg == NULL)
  3595. {
  3596. hr = _GetDestinationStorage(grfMode, &_pstg);
  3597. }
  3598. if (SUCCEEDED(hr))
  3599. {
  3600. TCHAR szName[MAX_PATH];
  3601. hr = DisplayNameOf((IShellFolder*)(void*)this, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3602. if (SUCCEEDED(hr))
  3603. {
  3604. if (IsEqualIID(riid, IID_IStorage))
  3605. {
  3606. hr = _pstg->CreateStorage(szName, grfMode, 0, 0, (IStorage **) ppv);
  3607. }
  3608. else if (IsEqualIID(riid, IID_IStream))
  3609. {
  3610. hr = _pstg->CreateStream(szName, grfMode, 0, 0, (IStream **) ppv);
  3611. }
  3612. else
  3613. {
  3614. hr = E_INVALIDARG;
  3615. }
  3616. }
  3617. }
  3618. return hr;
  3619. }
  3620. // enumerator object
  3621. class CMergedFldrEnum : public IEnumIDList
  3622. {
  3623. public:
  3624. // IUnknown
  3625. STDMETHOD (QueryInterface) (REFIID, void **);
  3626. STDMETHOD_(ULONG,AddRef) ();
  3627. STDMETHOD_(ULONG,Release) ();
  3628. // IEnumIDList
  3629. STDMETHODIMP Next(ULONG, LPITEMIDLIST*, ULONG*);
  3630. STDMETHODIMP Skip(ULONG);
  3631. STDMETHODIMP Reset();
  3632. STDMETHODIMP Clone(IEnumIDList**);
  3633. CMergedFldrEnum(CMergedFolder*pmf, DWORD grfEnumFlags, int iPos = 0);
  3634. private:
  3635. ~CMergedFldrEnum();
  3636. LONG _cRef;
  3637. CMergedFolder*_pmf;
  3638. DWORD _grfEnumFlags;
  3639. int _iPos;
  3640. };
  3641. CMergedFldrEnum::CMergedFldrEnum(CMergedFolder *pfm, DWORD grfEnumFlags, int iPos) :
  3642. _cRef(1), _iPos(iPos), _pmf(pfm), _grfEnumFlags(grfEnumFlags)
  3643. {
  3644. _pmf->AddRef();
  3645. }
  3646. CMergedFldrEnum::~CMergedFldrEnum()
  3647. {
  3648. _pmf->Release();
  3649. }
  3650. STDMETHODIMP CMergedFldrEnum::QueryInterface(REFIID riid, void **ppv)
  3651. {
  3652. static const QITAB qit[] = {
  3653. QITABENT(CMergedFldrEnum, IEnumIDList),
  3654. { 0 }
  3655. };
  3656. return QISearch(this, qit, riid, ppv);
  3657. }
  3658. STDMETHODIMP_(ULONG) CMergedFldrEnum::AddRef()
  3659. {
  3660. return InterlockedIncrement(&_cRef);
  3661. }
  3662. STDMETHODIMP_(ULONG) CMergedFldrEnum::Release()
  3663. {
  3664. if (InterlockedDecrement(&_cRef))
  3665. return _cRef;
  3666. delete this;
  3667. return 0;
  3668. }
  3669. // IEnumIDList
  3670. STDMETHODIMP CMergedFldrEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  3671. {
  3672. int iStart = _iPos;
  3673. int cFetched = 0;
  3674. HRESULT hr = S_OK;
  3675. if (!(celt > 0 && rgelt) || (NULL == pceltFetched && celt > 1))
  3676. return E_INVALIDARG;
  3677. *rgelt = 0;
  3678. while (hr == S_OK && (_iPos - iStart) < (int)celt)
  3679. {
  3680. LPITEMIDLIST pidl;
  3681. hr = _pmf->_GetPidl(&_iPos, _grfEnumFlags, &pidl);
  3682. if (hr == S_OK)
  3683. {
  3684. rgelt[cFetched] = pidl;
  3685. cFetched++;
  3686. }
  3687. _iPos++;
  3688. }
  3689. if (pceltFetched)
  3690. *pceltFetched = cFetched;
  3691. return celt == (ULONG)cFetched ? S_OK : S_FALSE;
  3692. }
  3693. STDMETHODIMP CMergedFldrEnum::Skip(ULONG celt)
  3694. {
  3695. _iPos += celt;
  3696. return S_OK;
  3697. }
  3698. STDMETHODIMP CMergedFldrEnum::Reset()
  3699. {
  3700. _iPos = 0;
  3701. return S_OK;
  3702. }
  3703. STDMETHODIMP CMergedFldrEnum::Clone(IEnumIDList **ppenum)
  3704. {
  3705. *ppenum = new CMergedFldrEnum(_pmf, _grfEnumFlags, _iPos);
  3706. return *ppenum ? S_OK : E_OUTOFMEMORY;
  3707. }
  3708. HRESULT CMergedFldrEnum_CreateInstance(CMergedFolder*pmf, DWORD grfFlags, IEnumIDList **ppenum)
  3709. {
  3710. CMergedFldrEnum *penum = new CMergedFldrEnum(pmf, grfFlags);
  3711. if (!penum)
  3712. return E_OUTOFMEMORY;
  3713. HRESULT hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  3714. penum->Release();
  3715. return hr;
  3716. }
  3717. // Drop Target handler
  3718. class CMergedFldrDropTarget : public IDropTarget,
  3719. public IObjectWithSite
  3720. {
  3721. public:
  3722. // IUnknown
  3723. STDMETHOD (QueryInterface) (REFIID, void **);
  3724. STDMETHOD_(ULONG,AddRef) ();
  3725. STDMETHOD_(ULONG,Release) ();
  3726. // IDropTarget
  3727. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  3728. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  3729. STDMETHODIMP DragLeave(void);
  3730. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  3731. // IObjectWithSite
  3732. STDMETHODIMP SetSite(IUnknown *punkSite);
  3733. STDMETHODIMP GetSite(REFIID riid, void **ppvSite);
  3734. CMergedFldrDropTarget(CMergedFolder*pmf, HWND hwnd);
  3735. private:
  3736. ~CMergedFldrDropTarget();
  3737. HRESULT _CreateOtherNameSpace(IShellFolder **ppsf);
  3738. HRESULT _FindTargetNamespace(CMergedFldrNamespace *pnsToMatch, CMergedFldrNamespace **ppns, CMergedFldrNamespace **ppnsMatched);
  3739. HRESULT _CreateFolder(IShellFolder *psf, LPCWSTR pszName, REFIID riid, void **ppv);
  3740. LPITEMIDLIST _FolderIDListFromData(IDataObject *pdtobj);
  3741. BOOL _IsCommonIDList(LPCITEMIDLIST pidlItem);
  3742. HRESULT _SetDropEffectFolders();
  3743. void _DestroyDropEffectFolders();
  3744. LONG _cRef;
  3745. CMergedFolder *_pmf;
  3746. IDropTarget *_pdt;
  3747. IDataObject *_pdo;
  3748. HWND _hwnd;
  3749. BOOL _fSrcIsCommon; // is _pdt a common programs folder (or its child)
  3750. LPITEMIDLIST _pidlSrcFolder; // where the source comes from
  3751. DWORD _grfKeyState;
  3752. DWORD _dwDragEnterEffect;
  3753. };
  3754. CMergedFldrDropTarget::CMergedFldrDropTarget(CMergedFolder*pfm, HWND hwnd) :
  3755. _cRef(1),
  3756. _pmf(pfm),
  3757. _hwnd(hwnd)
  3758. {
  3759. _pmf->AddRef();
  3760. }
  3761. CMergedFldrDropTarget::~CMergedFldrDropTarget()
  3762. {
  3763. _pmf->Release();
  3764. ASSERT(!_pdt);
  3765. ASSERT(!_pdo);
  3766. }
  3767. STDMETHODIMP CMergedFldrDropTarget::QueryInterface(REFIID riid, void **ppv)
  3768. {
  3769. static const QITAB qit[] =
  3770. {
  3771. QITABENT(CMergedFldrDropTarget, IDropTarget),
  3772. QITABENT(CMergedFldrDropTarget, IObjectWithSite),
  3773. { 0 }
  3774. };
  3775. return QISearch(this, qit, riid, ppv);
  3776. }
  3777. STDMETHODIMP_(ULONG) CMergedFldrDropTarget::AddRef()
  3778. {
  3779. return InterlockedIncrement(&_cRef);
  3780. }
  3781. STDMETHODIMP_(ULONG) CMergedFldrDropTarget::Release()
  3782. {
  3783. if (InterlockedDecrement(&_cRef))
  3784. return _cRef;
  3785. delete this;
  3786. return 0;
  3787. }
  3788. // is this pidl in the "All Users" part of the name space
  3789. BOOL CMergedFldrDropTarget::_IsCommonIDList(LPCITEMIDLIST pidlItem)
  3790. {
  3791. BOOL bRet = FALSE;
  3792. LPITEMIDLIST pidlCommon;
  3793. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommon)))
  3794. {
  3795. bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
  3796. ILFree(pidlCommon);
  3797. }
  3798. if (!bRet &&
  3799. SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlCommon)))
  3800. {
  3801. bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
  3802. ILFree(pidlCommon);
  3803. }
  3804. if (!bRet &&
  3805. SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &pidlCommon)))
  3806. {
  3807. bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
  3808. ILFree(pidlCommon);
  3809. }
  3810. return bRet;
  3811. }
  3812. LPITEMIDLIST CMergedFldrDropTarget::_FolderIDListFromData(IDataObject *pdtobj)
  3813. {
  3814. LPITEMIDLIST pidlFolder = NULL;
  3815. STGMEDIUM medium = {0};
  3816. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  3817. if (pida)
  3818. {
  3819. pidlFolder = ILClone(HIDA_GetPIDLFolder(pida));
  3820. HIDA_ReleaseStgMedium(pida, &medium);
  3821. }
  3822. return pidlFolder;
  3823. }
  3824. HRESULT CMergedFldrDropTarget::_CreateFolder(IShellFolder *psf, LPCWSTR pszName, REFIID riid, void **ppv)
  3825. {
  3826. *ppv = NULL;
  3827. IStorage *pstg;
  3828. HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IStorage, &pstg));
  3829. if (SUCCEEDED(hr))
  3830. {
  3831. DWORD grfMode = STGM_READWRITE;
  3832. IStorage *pstgNew;
  3833. hr = pstg->OpenStorage(pszName, NULL, grfMode, 0, 0, &pstgNew);
  3834. if (FAILED(hr))
  3835. {
  3836. // try create if it's not there
  3837. hr = pstg->CreateStorage(pszName, grfMode, 0, 0, &pstgNew);
  3838. }
  3839. if (SUCCEEDED(hr))
  3840. {
  3841. hr = pstgNew->QueryInterface(riid, ppv);
  3842. pstgNew->Release();
  3843. }
  3844. pstg->Release();
  3845. }
  3846. return hr;
  3847. }
  3848. HRESULT CMergedFldrDropTarget::_FindTargetNamespace(CMergedFldrNamespace *pnsToMatch, CMergedFldrNamespace **ppnsDstRoot, CMergedFldrNamespace **ppnsMatched)
  3849. {
  3850. *ppnsDstRoot = NULL;
  3851. *ppnsMatched = NULL;
  3852. // if the source of the drag drop is in one of our name spaces
  3853. // we prefer that as the target name space
  3854. for (CMergedFolder*pmf = this->_pmf; pmf && (NULL == *ppnsDstRoot); pmf = pmf->_Parent())
  3855. {
  3856. pmf->_FindNamespace(pnsToMatch->FolderAttrib(), pnsToMatch->FolderAttrib(), &pnsToMatch->GetGUID(), ppnsMatched);
  3857. CMergedFldrNamespace *pns;
  3858. for (int i = 0; (pns = pmf->_Namespace(i)) && (NULL == *ppnsDstRoot); i++)
  3859. {
  3860. if (pmf->_ShouldMergeNamespaces(*ppnsMatched, pns) &&
  3861. ILFindChild(pns->GetIDList(), _pidlSrcFolder))
  3862. {
  3863. *ppnsDstRoot = pns; // if the source is in one of our name spaces
  3864. }
  3865. }
  3866. ASSERT(NULL != *ppnsMatched); // pnsToMatch must exist above us
  3867. // If merging is disabled, then we don't want parent namespaces
  3868. // to infect us.
  3869. if (pmf->_fDontMerge)
  3870. {
  3871. break;
  3872. }
  3873. }
  3874. if (NULL == *ppnsDstRoot)
  3875. {
  3876. // if the source was not found, find the target name space based on
  3877. // 1) if the source data is an "All Users" item find the first common _Namespace
  3878. if (_fSrcIsCommon)
  3879. {
  3880. for (pmf = this->_pmf; pmf && (NULL == *ppnsDstRoot); pmf = pmf->_Parent())
  3881. {
  3882. pmf->_FindNamespace(pnsToMatch->FolderAttrib(), pnsToMatch->FolderAttrib(), &pnsToMatch->GetGUID(), ppnsMatched);
  3883. pmf->_FindNamespace(ASFF_COMMON, ASFF_COMMON, NULL, ppnsDstRoot);
  3884. }
  3885. }
  3886. // 2) find the first NON common name space for this guy
  3887. for (pmf = this->_pmf; pmf && (NULL == *ppnsDstRoot); pmf = pmf->_Parent())
  3888. {
  3889. pmf->_FindNamespace(pnsToMatch->FolderAttrib(), pnsToMatch->FolderAttrib(), &pnsToMatch->GetGUID(), ppnsMatched);
  3890. pmf->_FindNamespace(ASFF_COMMON, 0, NULL, ppnsDstRoot); // search for non common items
  3891. }
  3892. }
  3893. if (NULL == *ppnsMatched && NULL != *ppnsDstRoot)
  3894. {
  3895. delete *ppnsDstRoot;
  3896. *ppnsDstRoot = NULL;
  3897. }
  3898. return *ppnsDstRoot ? S_OK : E_FAIL;
  3899. }
  3900. HRESULT CMergedFldrDropTarget::_CreateOtherNameSpace(IShellFolder **ppsf)
  3901. {
  3902. *ppsf = NULL;
  3903. HRESULT hr = E_FAIL;
  3904. if (_pidlSrcFolder &&
  3905. (!_fSrcIsCommon || AffectAllUsers(_hwnd)))
  3906. {
  3907. CMergedFldrNamespace *pnsDstRoot; // name space we want to create this new item in
  3908. CMergedFldrNamespace *pnsSrc;
  3909. CMergedFldrNamespace *pnsStart = this->_pmf->_Namespace(0);
  3910. if (pnsStart)
  3911. {
  3912. hr = _FindTargetNamespace(pnsStart, &pnsDstRoot, &pnsSrc);
  3913. if (SUCCEEDED(hr))
  3914. {
  3915. LPCITEMIDLIST pidlChild = ILFindChild(pnsSrc->GetIDList(), pnsStart->GetIDList());
  3916. ASSERT(pidlChild && !ILIsEmpty(pidlChild)); // pnsSrc is a parent of pnsStart
  3917. IShellFolder *psfDst = pnsDstRoot->Folder();
  3918. psfDst->AddRef();
  3919. IShellFolder *psfSrc = pnsSrc->Folder();
  3920. psfSrc->AddRef();
  3921. for (LPCITEMIDLIST pidl = pidlChild; !ILIsEmpty(pidl) && SUCCEEDED(hr); pidl = _ILNext(pidl))
  3922. {
  3923. LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
  3924. if (pidlFirst)
  3925. {
  3926. WCHAR szName[MAX_PATH];
  3927. hr = DisplayNameOf(psfSrc, pidlFirst, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3928. if (SUCCEEDED(hr))
  3929. {
  3930. IShellFolder *psfNew;
  3931. hr = _CreateFolder(psfDst, szName, IID_PPV_ARG(IShellFolder, &psfNew));
  3932. if (SUCCEEDED(hr))
  3933. {
  3934. psfDst->Release();
  3935. psfDst = psfNew;
  3936. }
  3937. else
  3938. {
  3939. break;
  3940. }
  3941. }
  3942. // now advance to the next folder in the source name space
  3943. IShellFolder *psfNew;
  3944. hr = psfSrc->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfNew));
  3945. if (SUCCEEDED(hr))
  3946. {
  3947. psfSrc->Release();
  3948. psfSrc = psfNew;
  3949. }
  3950. else
  3951. {
  3952. break;
  3953. }
  3954. ILFree(pidlFirst);
  3955. }
  3956. else
  3957. hr = E_FAIL;
  3958. }
  3959. psfSrc->Release();
  3960. if (SUCCEEDED(hr))
  3961. *ppsf = psfDst; // copy out our ref
  3962. else
  3963. psfDst->Release();
  3964. }
  3965. }
  3966. }
  3967. return hr;
  3968. }
  3969. HRESULT CMergedFldrDropTarget::_SetDropEffectFolders()
  3970. {
  3971. INT i, cFolders = 0;
  3972. // Compute the number of folders which have special drop effects
  3973. for (i = 0; i < _pmf->_NamespaceCount(); i++)
  3974. {
  3975. if (_pmf->_Namespace(i)->GetDropEffect() != 0)
  3976. cFolders++;
  3977. }
  3978. // If the number is > 0 then lets add to the IDataObject the clipboard format
  3979. // which defines this information.
  3980. HRESULT hr = S_OK;
  3981. if ((cFolders > 0) || (_pmf->_dwDropEffect != 0))
  3982. {
  3983. DWORD cb = sizeof(DROPEFFECTFOLDERLIST) + sizeof(DROPEFFECTFOLDER) * (cFolders - 1);
  3984. DROPEFFECTFOLDERLIST *pdefl = (DROPEFFECTFOLDERLIST*)LocalAlloc(LPTR, cb);
  3985. if (pdefl)
  3986. {
  3987. pdefl->cFolders = cFolders;
  3988. pdefl->dwDefaultDropEffect = _pmf->_dwDropEffect; // default effect for the folders.
  3989. // fill the array backwards with the folders we have and their desired effects
  3990. for (i = 0, cFolders--; cFolders >= 0; i++)
  3991. {
  3992. DWORD dwDropEffect = _pmf->_Namespace(i)->GetDropEffect();
  3993. if (dwDropEffect != 0)
  3994. {
  3995. pdefl->aFolders[cFolders].dwDropEffect = dwDropEffect;
  3996. StrCpyN(pdefl->aFolders[cFolders].wszPath, _pmf->_Namespace(i)->GetDropFolder(), ARRAYSIZE(pdefl->aFolders[cFolders].wszPath));
  3997. cFolders--;
  3998. }
  3999. }
  4000. ASSERTMSG(g_cfDropEffectFolderList != 0, "Clipboard format for CFSTR_DROPEFFECTFOLDERS not registered");
  4001. hr = DataObj_SetBlob(_pdo, g_cfDropEffectFolderList, pdefl, cb);
  4002. LocalFree(pdefl);
  4003. }
  4004. else
  4005. {
  4006. hr = E_OUTOFMEMORY;
  4007. }
  4008. }
  4009. return hr;
  4010. }
  4011. // nuke the modified drop effects
  4012. void CMergedFldrDropTarget::_DestroyDropEffectFolders()
  4013. {
  4014. // there are no DROPEFFECTFOLDERs so subtract off the [1] array
  4015. DWORD cb = sizeof(DROPEFFECTFOLDERLIST) - sizeof(DROPEFFECTFOLDER);
  4016. DROPEFFECTFOLDERLIST *pdefl = (DROPEFFECTFOLDERLIST*)LocalAlloc(LPTR, cb);
  4017. if (pdefl)
  4018. {
  4019. pdefl->cFolders = 0;
  4020. pdefl->dwDefaultDropEffect = DROPEFFECT_NONE; // means do default handling
  4021. ASSERTMSG(g_cfDropEffectFolderList != 0, "Clipboard format for CFSTR_DROPEFFECTFOLDERS not registered");
  4022. DataObj_SetBlob(_pdo, g_cfDropEffectFolderList, pdefl, cb);
  4023. LocalFree(pdefl);
  4024. }
  4025. }
  4026. HRESULT CMergedFldrDropTarget::SetSite(IUnknown *punkSite)
  4027. {
  4028. IUnknown_SetSite(_pdt, punkSite);
  4029. return S_OK;
  4030. }
  4031. HRESULT CMergedFldrDropTarget::GetSite(REFIID riid, void **ppvSite)
  4032. {
  4033. return IUnknown_GetSite(_pdt, riid, ppvSite);
  4034. }
  4035. HRESULT CMergedFldrDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  4036. {
  4037. ASSERT(!_fSrcIsCommon);
  4038. ASSERT(_pdt == NULL);
  4039. ASSERT(_pidlSrcFolder == NULL);
  4040. ASSERT(_pdo == NULL);
  4041. IUnknown_Set((IUnknown**)&_pdo, pdtobj);
  4042. _SetDropEffectFolders();
  4043. _pidlSrcFolder = _FolderIDListFromData(pdtobj);
  4044. if (_pidlSrcFolder)
  4045. _fSrcIsCommon = _IsCommonIDList(_pidlSrcFolder);
  4046. HRESULT hr;
  4047. CMergedFldrNamespace *pns;
  4048. if (_fSrcIsCommon)
  4049. {
  4050. // if the item is comming from an "All Users" folder we want to target
  4051. // the common folder. if that does not exist yet then we continue on
  4052. // with a NULL _pdt and create that name space when the drop happens
  4053. hr = _pmf->_FindNamespace(ASFF_COMMON, ASFF_COMMON, NULL, &pns);
  4054. }
  4055. else
  4056. {
  4057. // not "All Users" item, get the default name space (if there is one)
  4058. hr = _pmf->_FindNamespace(ASFF_DEFNAMESPACE_VIEWOBJ, ASFF_DEFNAMESPACE_VIEWOBJ, NULL, &pns);
  4059. }
  4060. if (SUCCEEDED(hr) || _pmf->_fCDBurn)
  4061. {
  4062. if (_pmf->_fCDBurn)
  4063. {
  4064. IShellExtInit *psei;
  4065. hr = CoCreateInstance(CLSID_CDBurn, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellExtInit, &psei));
  4066. if (SUCCEEDED(hr))
  4067. {
  4068. hr = psei->Initialize(_pmf->_pidl, NULL, NULL);
  4069. if (SUCCEEDED(hr))
  4070. {
  4071. hr = psei->QueryInterface(IID_PPV_ARG(IDropTarget, &_pdt));
  4072. }
  4073. psei->Release();
  4074. }
  4075. }
  4076. else
  4077. {
  4078. hr = pns->Folder()->CreateViewObject(_hwnd, IID_PPV_ARG(IDropTarget, &_pdt));
  4079. }
  4080. if (SUCCEEDED(hr))
  4081. {
  4082. _pdt->DragEnter(pdtobj, grfKeyState, pt, pdwEffect);
  4083. }
  4084. }
  4085. _grfKeyState = grfKeyState;
  4086. _dwDragEnterEffect = *pdwEffect;
  4087. return S_OK;
  4088. }
  4089. HRESULT CMergedFldrDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  4090. {
  4091. _grfKeyState = grfKeyState;
  4092. return _pdt ? _pdt->DragOver(grfKeyState, pt, pdwEffect) : S_OK;
  4093. }
  4094. HRESULT CMergedFldrDropTarget::DragLeave(void)
  4095. {
  4096. if (_pdt)
  4097. {
  4098. _pdt->DragLeave();
  4099. IUnknown_SetSite(_pdt, NULL);
  4100. IUnknown_Set((IUnknown **)&_pdt, NULL);
  4101. }
  4102. if (_pdo)
  4103. {
  4104. _DestroyDropEffectFolders();
  4105. IUnknown_Set((IUnknown**)&_pdo, NULL);
  4106. }
  4107. _fSrcIsCommon = 0;
  4108. ILFree(_pidlSrcFolder); // accepts NULL
  4109. _pidlSrcFolder = NULL;
  4110. return S_OK;
  4111. }
  4112. HRESULT CMergedFldrDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  4113. {
  4114. HRESULT hr = S_OK;
  4115. if (!_pdt)
  4116. {
  4117. // we came here because we don't have _pdt which means that
  4118. // there is only one folder in our _Namespace and that's not
  4119. // the one we have to drop on. so we need to create it now
  4120. IShellFolder *psf;
  4121. hr = _CreateOtherNameSpace(&psf);
  4122. if (SUCCEEDED(hr))
  4123. {
  4124. hr = psf->CreateViewObject(_hwnd, IID_PPV_ARG(IDropTarget, &_pdt));
  4125. if (SUCCEEDED(hr))
  4126. _pdt->DragEnter(pdtobj, _grfKeyState, pt, &_dwDragEnterEffect);
  4127. psf->Release();
  4128. }
  4129. }
  4130. if (_pdt)
  4131. {
  4132. hr = _pdt->Drop(pdtobj, grfKeyState, pt, pdwEffect);
  4133. DragLeave();
  4134. }
  4135. return S_OK;
  4136. }
  4137. HRESULT CMergedFldrDropTarget_CreateInstance(CMergedFolder*pmf, HWND hwndOwner, IDropTarget **ppdt)
  4138. {
  4139. CMergedFldrDropTarget *pdt = new CMergedFldrDropTarget(pmf, hwndOwner);
  4140. if (!pdt)
  4141. return E_OUTOFMEMORY;
  4142. HRESULT hr = pdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdt));
  4143. pdt->Release();
  4144. return hr;
  4145. }
  4146. // context menu handling.
  4147. class CMergedFldrContextMenu : public IContextMenu3,
  4148. public IObjectWithSite
  4149. {
  4150. public:
  4151. // IUnknown
  4152. STDMETHOD (QueryInterface)(REFIID, void**);
  4153. STDMETHOD_(ULONG, AddRef)();
  4154. STDMETHOD_(ULONG, Release)();
  4155. // IContextMenu
  4156. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  4157. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
  4158. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax);
  4159. // IContextMenu2
  4160. STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
  4161. // IContextMenu3
  4162. STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
  4163. // IObjectWithSite
  4164. STDMETHODIMP SetSite(IUnknown *punkSite);
  4165. STDMETHODIMP GetSite(REFIID riid, void **ppvSite);
  4166. CMergedFldrContextMenu(HWND hwnd, IContextMenu *pcmCommon, IContextMenu *pcmUser);
  4167. private:
  4168. ~CMergedFldrContextMenu();
  4169. HRESULT _Initialize(CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl);
  4170. BOOL _IsMergedCommand(LPCMINVOKECOMMANDINFO pici);
  4171. HRESULT _InvokeCanonical(IContextMenu *pcm, LPCMINVOKECOMMANDINFO pici);
  4172. HRESULT _InvokeMergedCommand(LPCMINVOKECOMMANDINFO pici);
  4173. IContextMenu* _DefaultMenu();
  4174. LONG _cRef;
  4175. IContextMenu * _pcmCommon;
  4176. IContextMenu * _pcmUser;
  4177. UINT _cidl;
  4178. LPITEMIDLIST *_apidl;
  4179. CMergedFolder *_pmfParent;
  4180. UINT _idFirst;
  4181. HWND _hwnd;
  4182. friend HRESULT CMergedFldrContextMenu_CreateInstance(HWND hwnd, CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl, IContextMenu *pcmCommon, IContextMenu *pcmUser, IContextMenu **ppcm);
  4183. };
  4184. CMergedFldrContextMenu::CMergedFldrContextMenu(HWND hwnd, IContextMenu *pcmCommon, IContextMenu *pcmUser)
  4185. {
  4186. ASSERT(pcmCommon || pcmUser); // need at least one of these
  4187. _cRef = 1;
  4188. _hwnd = hwnd;
  4189. IUnknown_Set((IUnknown **)&_pcmCommon, pcmCommon);
  4190. IUnknown_Set((IUnknown **)&_pcmUser, pcmUser);
  4191. }
  4192. CMergedFldrContextMenu::~CMergedFldrContextMenu()
  4193. {
  4194. ATOMICRELEASE(_pcmCommon);
  4195. ATOMICRELEASE(_pcmUser);
  4196. ATOMICRELEASE(_pmfParent);
  4197. for (UINT i = 0; i < _cidl; i++)
  4198. {
  4199. ILFree(_apidl[i]);
  4200. }
  4201. }
  4202. IContextMenu* CMergedFldrContextMenu::_DefaultMenu()
  4203. {
  4204. ASSERT(_pcmUser || _pcmCommon); // need at least one of these; this is pretty much guaranteed since we're given them
  4205. // at constructor time.
  4206. return _pcmUser ? _pcmUser : _pcmCommon;
  4207. }
  4208. HRESULT CMergedFldrContextMenu::_Initialize(CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl)
  4209. {
  4210. _pmfParent = pmf;
  4211. if (_pmfParent)
  4212. {
  4213. _pmfParent->AddRef();
  4214. }
  4215. HRESULT hr = E_OUTOFMEMORY;
  4216. LPITEMIDLIST *apidlNew = new LPITEMIDLIST[cidl];
  4217. if (apidlNew)
  4218. {
  4219. hr = S_OK;
  4220. for (UINT i = 0; SUCCEEDED(hr) && i < cidl; i++)
  4221. {
  4222. hr = SHILClone(apidl[i], &(apidlNew[i]));
  4223. }
  4224. if (SUCCEEDED(hr))
  4225. {
  4226. _apidl = apidlNew;
  4227. _cidl = cidl;
  4228. }
  4229. else
  4230. {
  4231. for (i = 0; i < cidl; i++)
  4232. {
  4233. ILFree(apidlNew[i]);
  4234. }
  4235. delete [] apidlNew;
  4236. }
  4237. }
  4238. return hr;
  4239. }
  4240. STDMETHODIMP CMergedFldrContextMenu::QueryInterface(REFIID riid, void **ppv)
  4241. {
  4242. static const QITAB qit[] = {
  4243. QITABENTMULTI(CMergedFldrContextMenu, IContextMenu, IContextMenu3),
  4244. QITABENTMULTI(CMergedFldrContextMenu, IContextMenu2, IContextMenu3),
  4245. QITABENT(CMergedFldrContextMenu, IContextMenu3),
  4246. QITABENT(CMergedFldrContextMenu, IObjectWithSite),
  4247. { 0 },
  4248. };
  4249. return QISearch(this, qit, riid, ppv);
  4250. }
  4251. STDMETHODIMP_(ULONG) CMergedFldrContextMenu::AddRef()
  4252. {
  4253. return InterlockedIncrement(&_cRef);
  4254. }
  4255. STDMETHODIMP_(ULONG) CMergedFldrContextMenu::Release()
  4256. {
  4257. if (InterlockedDecrement(&_cRef))
  4258. return _cRef;
  4259. delete this;
  4260. return 0;
  4261. }
  4262. HRESULT CMergedFldrContextMenu::SetSite(IUnknown *punkSite)
  4263. {
  4264. // setsite/getsite will always be matched because _pcmUser and _pcmCommon never change
  4265. // after the constructor is called.
  4266. IUnknown_SetSite(_DefaultMenu(), punkSite);
  4267. return S_OK;
  4268. }
  4269. HRESULT CMergedFldrContextMenu::GetSite(REFIID riid, void **ppvSite)
  4270. {
  4271. return IUnknown_GetSite(_DefaultMenu(), riid, ppvSite);
  4272. }
  4273. HRESULT CMergedFldrContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  4274. {
  4275. HRESULT hr;
  4276. if (_pmfParent->_fInShellView)
  4277. {
  4278. hr = _DefaultMenu()->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
  4279. }
  4280. else
  4281. {
  4282. if (hmenu)
  4283. {
  4284. HMENU hmContext = SHLoadMenuPopup(HINST_THISDLL, MENU_SM_CONTEXTMENU);
  4285. if (hmContext)
  4286. {
  4287. int i;
  4288. if (!_pcmCommon || !_pcmUser)
  4289. {
  4290. DeleteMenu(hmContext, SMIDM_OPENCOMMON, MF_BYCOMMAND);
  4291. DeleteMenu(hmContext, SMIDM_EXPLORECOMMON, MF_BYCOMMAND);
  4292. }
  4293. _idFirst = idCmdFirst;
  4294. i = Shell_MergeMenus(hmenu, hmContext, -1, idCmdFirst, idCmdLast, MM_ADDSEPARATOR);
  4295. DestroyMenu(hmContext);
  4296. // Make it look "Shell Like"
  4297. SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
  4298. hr = ResultFromShort(i);
  4299. }
  4300. else
  4301. hr = E_OUTOFMEMORY;
  4302. }
  4303. else
  4304. hr = E_INVALIDARG;
  4305. }
  4306. return hr;
  4307. }
  4308. BOOL CMergedFldrContextMenu::_IsMergedCommand(LPCMINVOKECOMMANDINFO pici)
  4309. {
  4310. HRESULT hr = S_OK;
  4311. WCHAR szVerb[32];
  4312. if (!IS_INTRESOURCE(pici->lpVerb))
  4313. {
  4314. lstrcpyn(szVerb, (LPCWSTR)pici->lpVerb, ARRAYSIZE(szVerb));
  4315. }
  4316. else
  4317. {
  4318. hr = _DefaultMenu()->GetCommandString((UINT_PTR)pici->lpVerb, GCS_VERBW, NULL, (LPSTR)szVerb, ARRAYSIZE(szVerb));
  4319. }
  4320. BOOL fRet = FALSE;
  4321. if (SUCCEEDED(hr))
  4322. {
  4323. if ((lstrcmpi(szVerb, c_szOpen) == 0) ||
  4324. (lstrcmpi(szVerb, c_szExplore) == 0))
  4325. {
  4326. fRet = TRUE;
  4327. }
  4328. }
  4329. return fRet;
  4330. }
  4331. HRESULT CMergedFldrContextMenu::_InvokeMergedCommand(LPCMINVOKECOMMANDINFO pici)
  4332. {
  4333. // this code is mostly to navigate into folders from the shell view.
  4334. // the reason why we have to do this manually is because the context menus for
  4335. // the different namespaces have dataobjects which refer only to one namespace.
  4336. // when the navigate happens, that means we get passed bogus non-merged pidls
  4337. // into our BindToObject which screws things up.
  4338. ASSERT(_pmfParent->_fInShellView);
  4339. BOOL fHasFolders = FALSE;
  4340. BOOL fHasNonFolders = FALSE;
  4341. for (UINT i = 0; i < _cidl; i++)
  4342. {
  4343. if (_pmfParent->_IsFolder(_apidl[i]))
  4344. {
  4345. fHasFolders = TRUE;
  4346. }
  4347. else
  4348. {
  4349. fHasNonFolders = TRUE;
  4350. }
  4351. }
  4352. HRESULT hr;
  4353. if (fHasFolders && _IsMergedCommand(pici))
  4354. {
  4355. if (!fHasNonFolders)
  4356. {
  4357. // reinit the defcm with a new dataobject
  4358. IShellExtInit *psei;
  4359. hr = _DefaultMenu()->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei));
  4360. if (SUCCEEDED(hr))
  4361. {
  4362. IDataObject *pdo;
  4363. hr = SHCreateFileDataObject(_pmfParent->_pidl, _cidl, (LPCITEMIDLIST *)_apidl, NULL, &pdo);
  4364. if (SUCCEEDED(hr))
  4365. {
  4366. hr = psei->Initialize(_pmfParent->_pidl, pdo, NULL);
  4367. if (SUCCEEDED(hr))
  4368. {
  4369. hr = _DefaultMenu()->InvokeCommand(pici);
  4370. }
  4371. pdo->Release();
  4372. }
  4373. psei->Release();
  4374. }
  4375. }
  4376. else
  4377. {
  4378. // to open both folders and items simultaneously means we'd have to
  4379. // get a new context menu for just the items.
  4380. // we can do this if this scenario comes up.
  4381. hr = E_FAIL;
  4382. }
  4383. }
  4384. else
  4385. {
  4386. hr = _DefaultMenu()->InvokeCommand(pici);
  4387. }
  4388. return hr;
  4389. }
  4390. const ICIVERBTOIDMAP c_sIDVerbMap[] =
  4391. {
  4392. { L"delete", "delete", SMIDM_DELETE, SMIDM_DELETE, },
  4393. { L"rename", "rename", SMIDM_RENAME, SMIDM_RENAME, },
  4394. { L"properties", "properties", SMIDM_PROPERTIES, SMIDM_PROPERTIES, },
  4395. };
  4396. HRESULT CMergedFldrContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  4397. {
  4398. HRESULT hr = E_FAIL;
  4399. CMINVOKECOMMANDINFOEX ici = {0};
  4400. memcpy(&ici, pici, min(sizeof(ici), pici->cbSize));
  4401. ici.cbSize = sizeof(ici);
  4402. if (_pmfParent->_fInShellView)
  4403. {
  4404. hr = _InvokeMergedCommand((LPCMINVOKECOMMANDINFO)&ici);
  4405. }
  4406. else
  4407. {
  4408. UINT id;
  4409. hr = SHMapICIVerbToCmdID((LPCMINVOKECOMMANDINFO)&ici, c_sIDVerbMap, ARRAYSIZE(c_sIDVerbMap), &id);
  4410. if (SUCCEEDED(hr))
  4411. {
  4412. // The below sets an ansi verb only, make sure no lpVerbW is used
  4413. ici.fMask &= (~CMIC_MASK_UNICODE);
  4414. switch (id)
  4415. {
  4416. case SMIDM_OPEN:
  4417. case SMIDM_EXPLORE:
  4418. case SMIDM_OPENCOMMON:
  4419. case SMIDM_EXPLORECOMMON:
  4420. {
  4421. ASSERT(!_pmfParent->_fInShellView);
  4422. IContextMenu * pcm;
  4423. if (id == SMIDM_OPEN || id == SMIDM_EXPLORE)
  4424. {
  4425. pcm = _DefaultMenu();
  4426. }
  4427. else
  4428. {
  4429. pcm = _pcmCommon;
  4430. }
  4431. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, pcm, ici.fMask,
  4432. (id == SMIDM_EXPLORE || id == SMIDM_EXPLORECOMMON) ? "explore" : "open");
  4433. }
  4434. break;
  4435. case SMIDM_PROPERTIES:
  4436. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, _DefaultMenu(), ici.fMask, "properties");
  4437. break;
  4438. case SMIDM_DELETE:
  4439. ici.lpVerb = "delete";
  4440. if (_pcmUser)
  4441. {
  4442. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, _pcmUser, ici.fMask, "delete");
  4443. }
  4444. else
  4445. {
  4446. ASSERT(_pcmCommon);
  4447. ici.fMask |= CMIC_MASK_FLAG_NO_UI;
  4448. if (AffectAllUsers(_hwnd))
  4449. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, _pcmCommon, ici.fMask, "delete");
  4450. else
  4451. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  4452. }
  4453. break;
  4454. case SMIDM_RENAME:
  4455. ASSERT(0);
  4456. hr = E_NOTIMPL; // sftbar picks this off
  4457. break;
  4458. default:
  4459. ASSERTMSG(FALSE, "shouldn't have unknown command");
  4460. hr = E_INVALIDARG;
  4461. }
  4462. }
  4463. }
  4464. return hr;
  4465. }
  4466. HRESULT CMergedFldrContextMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax)
  4467. {
  4468. HRESULT hr = E_NOTIMPL;
  4469. switch (uType)
  4470. {
  4471. case GCS_VERBA:
  4472. case GCS_VERBW:
  4473. hr = SHMapCmdIDToVerb(idCmd, c_sIDVerbMap, ARRAYSIZE(c_sIDVerbMap), pszName, cchMax, GCS_VERBW == uType);
  4474. }
  4475. if (FAILED(hr))
  4476. {
  4477. hr = _DefaultMenu()->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
  4478. }
  4479. return hr;
  4480. }
  4481. HRESULT CMergedFldrContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4482. {
  4483. HRESULT hr;
  4484. if (_pmfParent->_fInShellView)
  4485. {
  4486. IContextMenu2 *pcm2;
  4487. hr = _DefaultMenu()->QueryInterface(IID_PPV_ARG(IContextMenu2, &pcm2));
  4488. if (SUCCEEDED(hr))
  4489. {
  4490. hr = pcm2->HandleMenuMsg(uMsg, wParam, lParam);
  4491. pcm2->Release();
  4492. }
  4493. }
  4494. else
  4495. {
  4496. hr = E_NOTIMPL;
  4497. }
  4498. return hr;
  4499. }
  4500. HRESULT CMergedFldrContextMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  4501. {
  4502. HRESULT hr;
  4503. if (_pmfParent->_fInShellView)
  4504. {
  4505. IContextMenu3 *pcm3;
  4506. hr = _DefaultMenu()->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3));
  4507. if (SUCCEEDED(hr))
  4508. {
  4509. hr = pcm3->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
  4510. pcm3->Release();
  4511. }
  4512. }
  4513. else
  4514. {
  4515. hr = E_NOTIMPL;
  4516. }
  4517. return hr;
  4518. }
  4519. HRESULT CMergedFldrContextMenu_CreateInstance(HWND hwnd, CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl, IContextMenu *pcmCommon, IContextMenu *pcmUser, IContextMenu **ppcm)
  4520. {
  4521. HRESULT hr = E_OUTOFMEMORY;
  4522. CMergedFldrContextMenu* pcm = new CMergedFldrContextMenu(hwnd, pcmCommon, pcmUser);
  4523. if (pcm)
  4524. {
  4525. hr = pcm->_Initialize(pmf, cidl, apidl);
  4526. if (SUCCEEDED(hr))
  4527. {
  4528. hr = pcm->QueryInterface(IID_PPV_ARG(IContextMenu, ppcm));
  4529. }
  4530. pcm->Release();
  4531. }
  4532. return hr;
  4533. }
  4534. class CMergedCategorizer : public ICategorizer,
  4535. public IShellExtInit
  4536. {
  4537. public:
  4538. // IUnknown
  4539. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  4540. STDMETHODIMP_(ULONG) AddRef(void);
  4541. STDMETHODIMP_(ULONG) Release(void);
  4542. // ICategorizer
  4543. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  4544. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  4545. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  4546. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  4547. // IShellExtInit
  4548. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  4549. CMergedCategorizer();
  4550. private:
  4551. ~CMergedCategorizer();
  4552. long _cRef;
  4553. CMergedFolder *_pmf;
  4554. };
  4555. // Type Categorizer
  4556. STDAPI CMergedCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  4557. {
  4558. HRESULT hr = E_OUTOFMEMORY;
  4559. CMergedCategorizer *pmc = new CMergedCategorizer();
  4560. if (pmc)
  4561. {
  4562. hr = pmc->QueryInterface(riid, ppv);
  4563. pmc->Release();
  4564. }
  4565. return hr;
  4566. }
  4567. CMergedCategorizer::CMergedCategorizer() :
  4568. _cRef(1)
  4569. {
  4570. }
  4571. CMergedCategorizer::~CMergedCategorizer()
  4572. {
  4573. ATOMICRELEASE(_pmf);
  4574. }
  4575. HRESULT CMergedCategorizer::QueryInterface(REFIID riid, void **ppv)
  4576. {
  4577. static const QITAB qit[] =
  4578. {
  4579. QITABENT(CMergedCategorizer, ICategorizer),
  4580. QITABENT(CMergedCategorizer, IShellExtInit),
  4581. { 0 },
  4582. };
  4583. return QISearch(this, qit, riid, ppv);
  4584. }
  4585. ULONG CMergedCategorizer::AddRef()
  4586. {
  4587. return InterlockedIncrement(&_cRef);
  4588. }
  4589. ULONG CMergedCategorizer::Release()
  4590. {
  4591. if (InterlockedDecrement(&_cRef))
  4592. return _cRef;
  4593. delete this;
  4594. return 0;
  4595. }
  4596. HRESULT CMergedCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
  4597. {
  4598. ATOMICRELEASE(_pmf);
  4599. return SHBindToObjectEx(NULL, pidlFolder, NULL, CLSID_MergedFolder, (void**)&_pmf);
  4600. }
  4601. HRESULT CMergedCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  4602. {
  4603. LoadString(HINST_THISDLL, IDS_WHICHFOLDER_COL, pszDesc, cch);
  4604. return S_OK;
  4605. }
  4606. HRESULT CMergedCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST *apidl, DWORD *rgCategoryIds)
  4607. {
  4608. HRESULT hr = E_ACCESSDENIED;
  4609. if (_pmf)
  4610. {
  4611. for (UINT i = 0; i < cidl; i++)
  4612. {
  4613. rgCategoryIds[i] = -1;
  4614. CMergedFldrNamespace *pns;
  4615. UINT uSrcID;
  4616. for (UINT j = 0; SUCCEEDED(_pmf->_GetSubPidl(apidl[i], j, &uSrcID, NULL, &pns)); j++)
  4617. {
  4618. if (_pmf->_ShouldSuspend(pns->GetGUID()))
  4619. {
  4620. continue;
  4621. }
  4622. rgCategoryIds[i] = uSrcID;
  4623. if (ASFF_DEFNAMESPACE_ATTRIB & pns->FolderAttrib())
  4624. {
  4625. // if we have more than one, keep the one from the defnamespace for attrib.
  4626. break;
  4627. }
  4628. }
  4629. }
  4630. hr = S_OK;
  4631. }
  4632. return hr;
  4633. }
  4634. HRESULT CMergedCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  4635. {
  4636. CMergedFldrNamespace *pns;
  4637. HRESULT hr = _pmf->_Namespace(dwCategoryId, &pns);
  4638. if (SUCCEEDED(hr))
  4639. {
  4640. hr = pns->GetLocation(pci->wszName, ARRAYSIZE(pci->wszName));
  4641. }
  4642. return hr;
  4643. }
  4644. HRESULT CMergedCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  4645. {
  4646. if (dwCategoryId1 == dwCategoryId2)
  4647. return ResultFromShort(0);
  4648. else if (dwCategoryId1 > dwCategoryId2)
  4649. return ResultFromShort(-1);
  4650. else
  4651. return ResultFromShort(1);
  4652. }
  4653. #define NORMALEVENTS (SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | SHCNE_MKDIR | SHCNE_RMDIR)
  4654. #define CDBURNEVENTS (SHCNE_MEDIAREMOVED | SHCNE_MEDIAINSERTED)
  4655. CMergedFolderViewCB::CMergedFolderViewCB(CMergedFolder *pmf) :
  4656. CBaseShellFolderViewCB(pmf->_pidl, (pmf->_fCDBurn) ? CDBURNEVENTS|NORMALEVENTS : NORMALEVENTS),
  4657. _pmf(pmf)
  4658. {
  4659. _pmf->AddRef();
  4660. }
  4661. CMergedFolderViewCB::~CMergedFolderViewCB()
  4662. {
  4663. _pmf->Release();
  4664. }
  4665. HRESULT CMergedFolderViewCB::_RefreshObjectsWithSameName(IShellFolderView *psfv, LPITEMIDLIST pidl)
  4666. {
  4667. TCHAR szName[MAX_PATH];
  4668. HRESULT hr = DisplayNameOf(SAFECAST(_pmf, IShellFolder2 *), pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  4669. if (SUCCEEDED(hr))
  4670. {
  4671. CMergedFldrNamespace *pns;
  4672. for (int i = 0; SUCCEEDED(hr) && (pns = _pmf->_Namespace(i)); i++)
  4673. {
  4674. LPITEMIDLIST pidlItem;
  4675. if (SUCCEEDED(pns->Folder()->ParseDisplayName(NULL, NULL, szName, NULL, &pidlItem, NULL)))
  4676. {
  4677. LPITEMIDLIST pidlItemWrap;
  4678. hr = _pmf->_CreateWrap(pidlItem, i, &pidlItemWrap);
  4679. if (SUCCEEDED(hr))
  4680. {
  4681. UINT uItem;
  4682. hr = psfv->UpdateObject(pidlItemWrap, pidlItemWrap, &uItem);
  4683. ILFree(pidlItemWrap);
  4684. }
  4685. ILFree(pidlItem);
  4686. }
  4687. }
  4688. }
  4689. return hr;
  4690. }
  4691. HRESULT CMergedFolderViewCB::_OnFSNotify(DWORD pv, LPCITEMIDLIST *ppidl, LPARAM lp)
  4692. {
  4693. // bail out early if we're merging.
  4694. // S_OK indicates to defview to do what it usually does
  4695. if (!_pmf->_fDontMerge)
  4696. return S_OK;
  4697. IShellFolderView *psfv;
  4698. HRESULT hr = IUnknown_GetSite(SAFECAST(this, IShellFolderViewCB*), IID_PPV_ARG(IShellFolderView, &psfv));
  4699. if (SUCCEEDED(hr))
  4700. {
  4701. LONG lEvent = (LONG) lp;
  4702. LPITEMIDLIST pidl1 = ppidl[0] ? ILFindChild(_pidl, ppidl[0]) : NULL;
  4703. LPITEMIDLIST pidl2 = ppidl[1] ? ILFindChild(_pidl, ppidl[1]) : NULL;
  4704. UINT uItem;
  4705. switch (lEvent)
  4706. {
  4707. case SHCNE_RENAMEFOLDER:
  4708. case SHCNE_RENAMEITEM:
  4709. // we need to handle the in/out for the view
  4710. if (pidl1 && pidl2)
  4711. {
  4712. if (SUCCEEDED(psfv->UpdateObject(pidl1, pidl2, &uItem)))
  4713. hr = S_FALSE;
  4714. _RefreshObjectsWithSameName(psfv, pidl1);
  4715. _RefreshObjectsWithSameName(psfv, pidl2);
  4716. }
  4717. else if (pidl1)
  4718. {
  4719. if (SUCCEEDED(psfv->RemoveObject(pidl1, &uItem)))
  4720. hr = S_FALSE;
  4721. _RefreshObjectsWithSameName(psfv, pidl1);
  4722. }
  4723. else if (pidl2)
  4724. {
  4725. if (SUCCEEDED(psfv->AddObject(pidl2, &uItem)))
  4726. hr = S_FALSE;
  4727. _RefreshObjectsWithSameName(psfv, pidl2);
  4728. }
  4729. break;
  4730. case SHCNE_CREATE:
  4731. case SHCNE_MKDIR:
  4732. ASSERTMSG(pidl1 != NULL, "incoming notify should be child of _pidl because thats what we were listening for");
  4733. if (SUCCEEDED(psfv->AddObject(pidl1, &uItem)))
  4734. hr = S_FALSE;
  4735. _RefreshObjectsWithSameName(psfv, pidl1);
  4736. break;
  4737. case SHCNE_DELETE:
  4738. case SHCNE_RMDIR:
  4739. ASSERTMSG(pidl1 != NULL, "incoming notify should be child of _pidl because thats what we were listening for");
  4740. if (SUCCEEDED(psfv->RemoveObject(pidl1, &uItem)))
  4741. hr = S_FALSE;
  4742. _RefreshObjectsWithSameName(psfv, pidl1);
  4743. break;
  4744. default:
  4745. break;
  4746. }
  4747. psfv->Release();
  4748. }
  4749. return hr;
  4750. }
  4751. STDMETHODIMP CMergedFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4752. {
  4753. switch (uMsg)
  4754. {
  4755. HANDLE_MSG(0, SFVM_FSNOTIFY, _OnFSNotify);
  4756. default:
  4757. return E_NOTIMPL;
  4758. }
  4759. return S_OK;
  4760. }
  4761. HRESULT CMergedFolderViewCB_CreateInstance(CMergedFolder *pmf, IShellFolderViewCB **ppsfvcb)
  4762. {
  4763. HRESULT hr = E_OUTOFMEMORY;
  4764. CMergedFolderViewCB *pcmfvcb = new CMergedFolderViewCB(pmf);
  4765. if (pcmfvcb)
  4766. {
  4767. hr = pcmfvcb->QueryInterface(IID_PPV_ARG(IShellFolderViewCB, ppsfvcb));
  4768. pcmfvcb->Release();
  4769. }
  4770. return hr;
  4771. }