4 * Copyright 2014 David Quintana
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <shlwapi_undoc.h>
24 #include "CMergedFolder.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(CMergedFolder
);
31 IShellFolder
* parent
;
35 class CEnumMergedFolder
:
36 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
41 CComPtr
<IShellFolder
> m_UserLocalFolder
;
42 CComPtr
<IShellFolder
> m_AllUSersFolder
;
53 virtual ~CEnumMergedFolder();
55 DECLARE_NOT_AGGREGATABLE(CEnumMergedFolder
)
56 DECLARE_PROTECT_FINAL_CONSTRUCT()
58 BEGIN_COM_MAP(CEnumMergedFolder
)
59 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
62 int DsaDeleteCallback(LocalPidlInfo
* info
);
64 static int CALLBACK
s_DsaDeleteCallback(void *pItem
, void *pData
);
66 HRESULT
SetSources(IShellFolder
* userLocal
, IShellFolder
* allUSers
);
67 HRESULT
Begin(HWND hwndOwner
, SHCONTF flags
);
68 HRESULT
FindPidlInList(HWND hwndOwner
, LPCITEMIDLIST pcidl
, LocalPidlInfo
* pinfo
);
70 virtual HRESULT STDMETHODCALLTYPE
Next(
75 virtual HRESULT STDMETHODCALLTYPE
Skip(ULONG celt
);
76 virtual HRESULT STDMETHODCALLTYPE
Reset();
77 virtual HRESULT STDMETHODCALLTYPE
Clone(IEnumIDList
**ppenum
);
80 CEnumMergedFolder::CEnumMergedFolder() :
81 m_UserLocalFolder(NULL
),
82 m_AllUSersFolder(NULL
),
91 CEnumMergedFolder::~CEnumMergedFolder()
93 DSA_DestroyCallback(m_hDsa
, s_DsaDeleteCallback
, this);
96 int CEnumMergedFolder::DsaDeleteCallback(LocalPidlInfo
* info
)
102 int CALLBACK
CEnumMergedFolder::s_DsaDeleteCallback(void *pItem
, void *pData
)
104 CEnumMergedFolder
* mf
= (CEnumMergedFolder
*) pData
;
105 LocalPidlInfo
* item
= (LocalPidlInfo
*) pItem
;
106 return mf
->DsaDeleteCallback(item
);
109 HRESULT
CEnumMergedFolder::SetSources(IShellFolder
* userLocal
, IShellFolder
* allUSers
)
111 m_UserLocalFolder
= userLocal
;
112 m_AllUSersFolder
= allUSers
;
114 TRACE("SetSources %p %p\n", userLocal
, allUSers
);
118 HRESULT
CEnumMergedFolder::Begin(HWND hwndOwner
, SHCONTF flags
)
122 if (m_hDsa
&& m_HwndOwner
== hwndOwner
&& m_Flags
== flags
)
127 TRACE("Search conditions changed, recreating list...\n");
129 CComPtr
<IEnumIDList
> userLocal
;
130 CComPtr
<IEnumIDList
> allUSers
;
131 hr
= m_UserLocalFolder
->EnumObjects(hwndOwner
, flags
, &userLocal
);
132 if (FAILED_UNEXPECTEDLY(hr
))
134 hr
= m_AllUSersFolder
->EnumObjects(hwndOwner
, flags
, &allUSers
);
135 if (FAILED_UNEXPECTEDLY(hr
))
143 m_hDsa
= DSA_Create(sizeof(LocalPidlInfo
), 10);
146 DSA_EnumCallback(m_hDsa
, s_DsaDeleteCallback
, this);
147 DSA_DeleteAllItems(m_hDsa
);
152 LPITEMIDLIST pidl1
= NULL
;
153 LPITEMIDLIST pidl2
= NULL
;
161 hr1
= userLocal
->Next(1, &pidl1
, NULL
);
162 if (FAILED_UNEXPECTEDLY(hr1
))
174 hr2
= allUSers
->Next(1, &pidl2
, NULL
);
175 if (FAILED_UNEXPECTEDLY(hr2
))
184 if (hr1
== S_OK
&& hr2
== S_OK
)
188 STRRET str1
= { STRRET_WSTR
};
189 STRRET str2
= { STRRET_WSTR
};
190 hr
= m_UserLocalFolder
->GetDisplayNameOf(pidl1
, SHGDN_FORPARSING
| SHGDN_INFOLDER
, &str1
);
193 hr
= m_AllUSersFolder
->GetDisplayNameOf(pidl2
, SHGDN_FORPARSING
| SHGDN_INFOLDER
, &str2
);
196 StrRetToStrW(&str1
, pidl1
, &name1
);
197 StrRetToStrW(&str2
, pidl2
, &name2
);
198 order
= StrCmpW(name1
, name2
);
200 TRACE("Both sources are S_OK, comparison between %S and %S returns %d\n", name1
, name2
, order
);
202 CoTaskMemFree(name1
);
203 CoTaskMemFree(name2
);
205 else if (hr1
== S_OK
)
209 TRACE("Both sources are S_OK, forcing %d\n", order
);
211 else if (hr2
== S_OK
)
215 TRACE("Both sources are S_OK, forcing %d\n", order
);
219 TRACE("None of the sources\n");
223 LocalPidlInfo info
= { FALSE
};
226 info
.parent
= m_UserLocalFolder
;
227 info
.pidl
= ILClone(pidl1
);
232 info
.parent
= m_AllUSersFolder
;
233 info
.pidl
= ILClone(pidl2
);
236 else // if (order == 0)
239 info
.parent
= m_UserLocalFolder
;
240 info
.pidl
= ILClone(pidl1
);
245 TRACE("Inserting item %d with parent %p and pidl { cb=%d }\n", m_hDsaCount
, info
.parent
, info
.pidl
->mkid
.cb
);
246 int idx
= DSA_InsertItem(m_hDsa
, DSA_APPEND
, &info
);
247 TRACE("New index: %d\n", idx
);
251 } while (hr1
== S_OK
|| hr2
== S_OK
);
253 m_HwndOwner
= hwndOwner
;
259 HRESULT
CEnumMergedFolder::FindPidlInList(HWND hwndOwner
, LPCITEMIDLIST pcidl
, LocalPidlInfo
* pinfo
)
265 Begin(hwndOwner
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
);
268 TRACE("Searching for pidl { cb=%d } in a list of %d items\n", pcidl
->mkid
.cb
, m_hDsaCount
);
270 for (int i
= 0; i
< (int)m_hDsaCount
; i
++)
272 LocalPidlInfo
* tinfo
= (LocalPidlInfo
*)DSA_GetItemPtr(m_hDsa
, i
);
276 LocalPidlInfo info
= *tinfo
;
278 TRACE("Comparing with item at %d with parent %p and pidl { cb=%d }\n", i
, info
.parent
, info
.pidl
->mkid
.cb
);
280 hr
= info
.parent
->CompareIDs(0, info
.pidl
, pcidl
);
281 if (FAILED_UNEXPECTEDLY(hr
))
291 TRACE("Comparison returned %d\n", (int) (short) (hr
& 0xFFFF));
295 TRACE("Pidl not found\n");
296 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
299 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Next(
304 if (pceltFetched
) *pceltFetched
= 0;
306 if (m_hDsaIndex
== m_hDsaCount
)
309 for (int i
= 0; i
< (int)celt
;)
311 LocalPidlInfo
* tinfo
= (LocalPidlInfo
*) DSA_GetItemPtr(m_hDsa
, m_hDsaIndex
);
315 LocalPidlInfo info
= *tinfo
;
317 TRACE("Returning next item at %d with parent %p and pidl { cb=%d }\n", m_hDsaIndex
, info
.parent
, info
.pidl
->mkid
.cb
);
319 // FIXME: ILClone shouldn't be needed here! This should be causing leaks
320 if (rgelt
) rgelt
[i
] = ILClone(info
.pidl
);
324 if (m_hDsaIndex
== m_hDsaCount
)
326 if (pceltFetched
) *pceltFetched
= i
;
327 return (i
== (int)celt
) ? S_OK
: S_FALSE
;
331 if (pceltFetched
) *pceltFetched
= celt
;
335 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Skip(ULONG celt
)
337 return Next(celt
, NULL
, NULL
);
340 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Reset()
346 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Clone(
347 IEnumIDList
**ppenum
)
353 //-----------------------------------------------------------------------------
357 HRESULT WINAPI
CMergedFolder_Constructor(REFIID riid
, LPVOID
*ppv
)
359 return ShellObjectCreator
<CMergedFolder
>(riid
, ppv
);
362 CMergedFolder::CMergedFolder() :
366 m_UserLocalPidl(NULL
),
367 m_AllUsersPidl(NULL
),
372 CMergedFolder::~CMergedFolder()
374 if (m_UserLocalPidl
) ILFree(m_UserLocalPidl
);
375 if (m_AllUsersPidl
) ILFree(m_AllUsersPidl
);
378 // IAugmentedShellFolder2
379 HRESULT STDMETHODCALLTYPE
CMergedFolder::AddNameSpace(LPGUID lpGuid
, IShellFolder
* psf
, LPCITEMIDLIST pcidl
, ULONG dwUnknown
)
383 TRACE("FIXME: No idea how to handle the GUID\n");
387 TRACE("AddNameSpace %p %p\n", m_UserLocal
.p
, m_AllUSers
.p
);
389 // FIXME: Use a DSA to store the list of merged namespaces, together with their related info (psf, pidl, ...)
390 // For now, assume only 2 will ever be used, and ignore all the other data.
394 m_UserLocalPidl
= ILClone(pcidl
);
402 m_AllUsersPidl
= ILClone(pcidl
);
404 m_EnumSource
= new CComObject
<CEnumMergedFolder
>();
405 return m_EnumSource
->SetSources(m_UserLocal
, m_AllUSers
);
408 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetNameSpaceID(LPCITEMIDLIST pcidl
, LPGUID lpGuid
)
414 HRESULT STDMETHODCALLTYPE
CMergedFolder::QueryNameSpace(ULONG dwUnknown
, LPGUID lpGuid
, IShellFolder
** ppsf
)
420 HRESULT STDMETHODCALLTYPE
CMergedFolder::EnumNameSpace(ULONG dwUnknown
, PULONG lpUnknown
)
426 HRESULT STDMETHODCALLTYPE
CMergedFolder::UnWrapIDList(LPCITEMIDLIST pcidl
, LONG lUnknown
, IShellFolder
** ppsf
, LPITEMIDLIST
* ppidl1
, LPITEMIDLIST
*ppidl2
, LONG
* lpUnknown
)
433 HRESULT STDMETHODCALLTYPE
CMergedFolder::ParseDisplayName(
436 LPOLESTR lpszDisplayName
,
439 ULONG
*pdwAttributes
)
445 if (!ppidl
) return E_FAIL
;
447 if (pchEaten
) *pchEaten
= 0;
448 if (pdwAttributes
) *pdwAttributes
= 0;
450 TRACE("ParseDisplayName name=%S\n", lpszDisplayName
);
452 hr
= m_UserLocal
->ParseDisplayName(hwndOwner
, pbcReserved
, lpszDisplayName
, pchEaten
, &pidl
, pdwAttributes
);
455 TRACE("ParseDisplayName result local\n");
456 hr
= m_EnumSource
->FindPidlInList(hwndOwner
, pidl
, &info
);
460 *ppidl
= ILClone(info
.pidl
);
465 hr
= m_AllUSers
->ParseDisplayName(hwndOwner
, pbcReserved
, lpszDisplayName
, pchEaten
, &pidl
, pdwAttributes
);
468 TRACE("ParseDisplayName result common\n");
469 hr
= m_EnumSource
->FindPidlInList(hwndOwner
, pidl
, &info
);
473 *ppidl
= ILClone(info
.pidl
);
478 if (ppidl
) *ppidl
= NULL
;
479 if (pchEaten
) *pchEaten
= 0;
480 if (pdwAttributes
) *pdwAttributes
= 0;
481 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
484 HRESULT STDMETHODCALLTYPE
CMergedFolder::EnumObjects(
487 IEnumIDList
**ppenumIDList
)
489 TRACE("EnumObjects\n");
490 HRESULT hr
= m_EnumSource
->QueryInterface(IID_PPV_ARG(IEnumIDList
, ppenumIDList
));
491 if (FAILED_UNEXPECTEDLY(hr
))
493 return m_EnumSource
->Begin(hwndOwner
, grfFlags
);
496 HRESULT STDMETHODCALLTYPE
CMergedFolder::BindToObject(
505 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
506 if (FAILED_UNEXPECTEDLY(hr
))
509 TRACE("BindToObject shared = %d\n", info
.shared
);
512 return info
.parent
->BindToObject(info
.pidl
, pbcReserved
, riid
, ppvOut
);
514 if (riid
!= IID_IShellFolder
)
517 CComPtr
<IShellFolder
> fld1
;
518 CComPtr
<IShellFolder
> fld2
;
520 hr
= m_UserLocal
->BindToObject(info
.pidl
, pbcReserved
, IID_PPV_ARG(IShellFolder
, &fld1
));
521 if (FAILED_UNEXPECTEDLY(hr
))
524 hr
= m_AllUSers
->BindToObject(info
.pidl
, pbcReserved
, IID_PPV_ARG(IShellFolder
, &fld2
));
525 if (FAILED_UNEXPECTEDLY(hr
))
528 CComPtr
<IAugmentedShellFolder
> pasf
;
529 hr
= CMergedFolder_Constructor(IID_PPV_ARG(IAugmentedShellFolder
, &pasf
));
530 if (FAILED_UNEXPECTEDLY(hr
))
533 hr
= pasf
->QueryInterface(riid
, ppvOut
);
534 if (FAILED_UNEXPECTEDLY(hr
))
537 hr
= pasf
->AddNameSpace(NULL
, fld1
, info
.pidl
, 0xFF00);
538 if (FAILED_UNEXPECTEDLY(hr
))
541 hr
= pasf
->AddNameSpace(NULL
, fld2
, info
.pidl
, 0x0000);
542 if (FAILED_UNEXPECTEDLY(hr
))
548 HRESULT STDMETHODCALLTYPE
CMergedFolder::BindToStorage(
558 HRESULT STDMETHODCALLTYPE
CMergedFolder::CompareIDs(
563 TRACE("CompareIDs\n");
564 return m_UserLocal
->CompareIDs(lParam
, pidl1
, pidl2
);
567 HRESULT STDMETHODCALLTYPE
CMergedFolder::CreateViewObject(
576 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetAttributesOf(
578 LPCITEMIDLIST
*apidl
,
584 TRACE("GetAttributesOf\n");
586 for (int i
= 0; i
< (int)cidl
; i
++)
588 LPCITEMIDLIST pidl
= apidl
[i
];
590 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
591 if (FAILED_UNEXPECTEDLY(hr
))
596 SFGAOF
* pinOut1
= rgfInOut
? rgfInOut
+ i
: NULL
;
598 hr
= info
.parent
->GetAttributesOf(1, &pidl
, pinOut1
);
600 if (FAILED_UNEXPECTEDLY(hr
))
607 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetUIObjectOf(
610 LPCITEMIDLIST
*apidl
,
618 TRACE("GetUIObjectOf\n");
620 for (int i
= 0; i
< (int)cidl
; i
++)
622 LPCITEMIDLIST pidl
= apidl
[i
];
624 TRACE("Processing GetUIObjectOf item %d of %u...\n", i
, cidl
);
626 hr
= m_EnumSource
->FindPidlInList(hwndOwner
, pidl
, &info
);
627 if (FAILED_UNEXPECTEDLY(hr
))
632 TRACE("FindPidlInList succeeded with parent %p and pidl { db=%d }\n", info
.parent
, info
.pidl
->mkid
.cb
);
634 UINT
* pinOut1
= prgfInOut
? prgfInOut
+i
: NULL
;
635 void** ppvOut1
= ppvOut
? ppvOut
+ i
: NULL
;
637 hr
= info
.parent
->GetUIObjectOf(hwndOwner
, 1, &pidl
, riid
, pinOut1
, ppvOut1
);
639 if (FAILED_UNEXPECTEDLY(hr
))
646 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDisplayNameOf(
654 TRACE("GetDisplayNameOf\n");
656 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
657 if (FAILED_UNEXPECTEDLY(hr
))
660 hr
= info
.parent
->GetDisplayNameOf(info
.pidl
, uFlags
, lpName
);
662 if (FAILED_UNEXPECTEDLY(hr
))
667 HRESULT STDMETHODCALLTYPE
CMergedFolder::SetNameOf(
672 LPITEMIDLIST
*ppidlOut
)
679 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetClassID(CLSID
*lpClassId
)
686 HRESULT STDMETHODCALLTYPE
CMergedFolder::Initialize(LPCITEMIDLIST pidl
)
688 m_shellPidl
= ILClone(pidl
);
693 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetCurFolder(LPITEMIDLIST
* pidl
)
701 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDefaultSearchGUID(
708 HRESULT STDMETHODCALLTYPE
CMergedFolder::EnumSearches(
709 IEnumExtraSearch
**ppenum
)
715 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDefaultColumn(
724 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDefaultColumnState(
726 SHCOLSTATEF
*pcsFlags
)
732 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDetailsEx(
734 const SHCOLUMNID
*pscid
,
740 TRACE("GetDetailsEx\n");
742 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
743 if (FAILED_UNEXPECTEDLY(hr
))
746 CComPtr
<IShellFolder2
> parent2
;
747 hr
= info
.parent
->QueryInterface(IID_PPV_ARG(IShellFolder2
, &parent2
));
748 if (FAILED_UNEXPECTEDLY(hr
))
751 hr
= parent2
->GetDetailsEx(info
.pidl
, pscid
, pv
);
752 if (FAILED_UNEXPECTEDLY(hr
))
757 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDetailsOf(
765 TRACE("GetDetailsOf\n");
767 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
768 if (FAILED_UNEXPECTEDLY(hr
))
771 CComPtr
<IShellFolder2
> parent2
;
772 hr
= info
.parent
->QueryInterface(IID_PPV_ARG(IShellFolder2
, &parent2
));
773 if (FAILED_UNEXPECTEDLY(hr
))
776 hr
= parent2
->GetDetailsOf(info
.pidl
, iColumn
, psd
);
778 if (FAILED_UNEXPECTEDLY(hr
))
783 HRESULT STDMETHODCALLTYPE
CMergedFolder::MapColumnToSCID(