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
20 #include "shellmenu.h"
22 #include <shlwapi_undoc.h>
24 #include "CMergedFolder.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(CMergedFolder
);
31 IShellFolder
* parent
;
37 class CEnumMergedFolder
:
38 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
43 CComPtr
<IShellFolder
> m_UserLocalFolder
;
44 CComPtr
<IShellFolder
> m_AllUSersFolder
;
55 virtual ~CEnumMergedFolder();
57 DECLARE_NOT_AGGREGATABLE(CEnumMergedFolder
)
58 DECLARE_PROTECT_FINAL_CONSTRUCT()
60 BEGIN_COM_MAP(CEnumMergedFolder
)
61 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList
, IEnumIDList
)
64 int DsaDeleteCallback(LocalPidlInfo
* info
);
66 static int CALLBACK
s_DsaDeleteCallback(void *pItem
, void *pData
);
68 HRESULT
SetSources(IShellFolder
* userLocal
, IShellFolder
* allUSers
);
69 HRESULT
Begin(HWND hwndOwner
, SHCONTF flags
);
70 HRESULT
FindPidlInList(HWND hwndOwner
, LPCITEMIDLIST pcidl
, LocalPidlInfo
* pinfo
);
71 HRESULT
FindByName(HWND hwndOwner
, LPCWSTR strParsingName
, LocalPidlInfo
* pinfo
);
73 virtual HRESULT STDMETHODCALLTYPE
Next(
78 virtual HRESULT STDMETHODCALLTYPE
Skip(ULONG celt
);
79 virtual HRESULT STDMETHODCALLTYPE
Reset();
80 virtual HRESULT STDMETHODCALLTYPE
Clone(IEnumIDList
**ppenum
);
83 CEnumMergedFolder::CEnumMergedFolder() :
84 m_UserLocalFolder(NULL
),
85 m_AllUSersFolder(NULL
),
94 CEnumMergedFolder::~CEnumMergedFolder()
96 DSA_DestroyCallback(m_hDsa
, s_DsaDeleteCallback
, this);
99 int CEnumMergedFolder::DsaDeleteCallback(LocalPidlInfo
* info
)
104 CoTaskMemFree((LPVOID
)info
->parseName
);
108 int CALLBACK
CEnumMergedFolder::s_DsaDeleteCallback(void *pItem
, void *pData
)
110 CEnumMergedFolder
* mf
= (CEnumMergedFolder
*) pData
;
111 LocalPidlInfo
* item
= (LocalPidlInfo
*) pItem
;
112 return mf
->DsaDeleteCallback(item
);
115 HRESULT
CEnumMergedFolder::SetSources(IShellFolder
* userLocal
, IShellFolder
* allUSers
)
117 m_UserLocalFolder
= userLocal
;
118 m_AllUSersFolder
= allUSers
;
120 TRACE("SetSources %p %p\n", userLocal
, allUSers
);
124 HRESULT
CEnumMergedFolder::Begin(HWND hwndOwner
, SHCONTF flags
)
127 LPITEMIDLIST pidl
= NULL
;
129 if (m_hDsa
&& m_HwndOwner
== hwndOwner
&& m_Flags
== flags
)
134 TRACE("Search conditions changed, recreating list...\n");
136 CComPtr
<IEnumIDList
> userLocal
;
137 CComPtr
<IEnumIDList
> allUsers
;
139 hr
= m_UserLocalFolder
->EnumObjects(hwndOwner
, flags
, &userLocal
);
140 if (FAILED_UNEXPECTEDLY(hr
))
142 hr
= m_AllUSersFolder
->EnumObjects(hwndOwner
, flags
, &allUsers
);
143 if (FAILED_UNEXPECTEDLY(hr
))
148 m_hDsa
= DSA_Create(sizeof(LocalPidlInfo
), 10);
151 DSA_EnumCallback(m_hDsa
, s_DsaDeleteCallback
, this);
152 DSA_DeleteAllItems(m_hDsa
);
155 // The sources are not ordered so load all of the items for the user folder first
156 TRACE("Loading Local entries...\n");
159 hr
= userLocal
->Next(1, &pidl
, NULL
);
160 if (FAILED_UNEXPECTEDLY(hr
))
167 STRRET str
= { STRRET_WSTR
};
168 hr
= m_UserLocalFolder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
| SHGDN_INFOLDER
, &str
);
171 StrRetToStrW(&str
, pidl
, &name
);
173 LocalPidlInfo info
= {
183 TRACE("Inserting item %d with name %S\n", m_hDsaCount
, name
);
184 int idx
= DSA_InsertItem(m_hDsa
, DSA_APPEND
, &info
);
185 TRACE("New index: %d\n", idx
);
190 // Then load the items for the common folder
191 TRACE("Loading Common entries...\n");
194 hr
= allUsers
->Next(1, &pidl
, NULL
);
195 if (FAILED_UNEXPECTEDLY(hr
))
202 STRRET str
= { STRRET_WSTR
};
203 hr
= m_AllUSersFolder
->GetDisplayNameOf(pidl
, SHGDN_FORPARSING
| SHGDN_INFOLDER
, &str
);
206 StrRetToStrW(&str
, pidl
, &name
);
208 LocalPidlInfo info
= {
218 // Try to find an existing entry with the same name, and makr it as shared.
219 // FIXME: This is sub-optimal, a hash table would be a lot more efficient.
220 BOOL bShared
= FALSE
;
221 for (int i
= 0; i
< (int)m_hDsaCount
; i
++)
223 LocalPidlInfo
*pInfo
= (LocalPidlInfo
*) DSA_GetItemPtr(m_hDsa
, i
);
225 int order
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
,
226 pInfo
->parseName
, lstrlenW(pInfo
->parseName
),
227 info
.parseName
, lstrlenW(info
.parseName
));
229 if (order
== CSTR_EQUAL
)
231 TRACE("Item name already exists! Marking '%S' as shared ...\n", name
);
233 pInfo
->shared
= TRUE
;
234 pInfo
->pidl2
= info
.pidl
;
240 // If an entry was not found, add a new one for this item
243 TRACE("Inserting item %d with name %S\n", m_hDsaCount
, name
);
244 int idx
= DSA_InsertItem(m_hDsa
, DSA_APPEND
, &info
);
245 TRACE("New index: %d\n", idx
);
251 m_HwndOwner
= hwndOwner
;
257 HRESULT
CEnumMergedFolder::FindPidlInList(HWND hwndOwner
, LPCITEMIDLIST pcidl
, LocalPidlInfo
* pinfo
)
263 Begin(hwndOwner
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
);
266 TRACE("Searching for pidl { cb=%d } in a list of %d items\n", pcidl
->mkid
.cb
, m_hDsaCount
);
268 for (int i
= 0; i
< (int)m_hDsaCount
; i
++)
270 LocalPidlInfo
* pInfo
= (LocalPidlInfo
*) DSA_GetItemPtr(m_hDsa
, i
);
274 TRACE("Comparing with item at %d with parent %p and pidl { cb=%d }\n", i
, pInfo
->parent
, pInfo
->pidl
->mkid
.cb
);
276 hr
= pInfo
->parent
->CompareIDs(0, pInfo
->pidl
, pcidl
);
277 if (FAILED_UNEXPECTEDLY(hr
))
287 TRACE("Comparison returned %d\n", (int) (short) (hr
& 0xFFFF));
291 TRACE("Pidl not found\n");
292 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
295 HRESULT
CEnumMergedFolder::FindByName(HWND hwndOwner
, LPCWSTR strParsingName
, LocalPidlInfo
* pinfo
)
299 Begin(hwndOwner
, SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
);
302 TRACE("Searching for '%S' in a list of %d items\n", strParsingName
, m_hDsaCount
);
304 for (int i
= 0; i
< (int) m_hDsaCount
; i
++)
306 LocalPidlInfo
* pInfo
= (LocalPidlInfo
*) DSA_GetItemPtr(m_hDsa
, i
);
310 int order
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
,
311 pInfo
->parseName
, lstrlenW(pInfo
->parseName
),
312 strParsingName
, lstrlenW(strParsingName
));
323 TRACE("Pidl not found\n");
324 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
327 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Next(
335 if (m_hDsaIndex
== m_hDsaCount
)
338 for (int i
= 0; i
< (int)celt
;)
340 LocalPidlInfo
* tinfo
= (LocalPidlInfo
*) DSA_GetItemPtr(m_hDsa
, m_hDsaIndex
);
344 LocalPidlInfo info
= *tinfo
;
346 TRACE("Returning next item at %d with parent %p and pidl { cb=%d }\n", m_hDsaIndex
, info
.parent
, info
.pidl
->mkid
.cb
);
348 // FIXME: ILClone shouldn't be needed here! This should be causing leaks
349 if (rgelt
) rgelt
[i
] = ILClone(info
.pidl
);
353 if (m_hDsaIndex
== m_hDsaCount
)
357 return (i
== (int)celt
) ? S_OK
: S_FALSE
;
361 if (pceltFetched
) *pceltFetched
= celt
;
365 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Skip(ULONG celt
)
367 return Next(celt
, NULL
, NULL
);
370 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Reset()
376 HRESULT STDMETHODCALLTYPE
CEnumMergedFolder::Clone(
377 IEnumIDList
**ppenum
)
383 //-----------------------------------------------------------------------------
386 CMergedFolder::CMergedFolder() :
390 m_UserLocalPidl(NULL
),
391 m_AllUsersPidl(NULL
),
396 CMergedFolder::~CMergedFolder()
398 if (m_UserLocalPidl
) ILFree(m_UserLocalPidl
);
399 if (m_AllUsersPidl
) ILFree(m_AllUsersPidl
);
402 // IAugmentedShellFolder2
403 HRESULT STDMETHODCALLTYPE
CMergedFolder::AddNameSpace(LPGUID lpGuid
, IShellFolder
* psf
, LPCITEMIDLIST pcidl
, ULONG dwUnknown
)
407 TRACE("FIXME: No idea how to handle the GUID\n");
411 TRACE("AddNameSpace %p %p\n", m_UserLocal
.p
, m_AllUsers
.p
);
413 // FIXME: Use a DSA to store the list of merged namespaces, together with their related info (psf, pidl, ...)
414 // For now, assume only 2 will ever be used, and ignore all the other data.
418 m_UserLocalPidl
= ILClone(pcidl
);
426 m_AllUsersPidl
= ILClone(pcidl
);
428 m_EnumSource
= new CComObject
<CEnumMergedFolder
>();
429 return m_EnumSource
->SetSources(m_UserLocal
, m_AllUsers
);
432 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetNameSpaceID(LPCITEMIDLIST pcidl
, LPGUID lpGuid
)
438 HRESULT STDMETHODCALLTYPE
CMergedFolder::QueryNameSpace(ULONG dwUnknown
, LPGUID lpGuid
, IShellFolder
** ppsf
)
444 HRESULT STDMETHODCALLTYPE
CMergedFolder::EnumNameSpace(ULONG dwUnknown
, PULONG lpUnknown
)
450 HRESULT STDMETHODCALLTYPE
CMergedFolder::UnWrapIDList(LPCITEMIDLIST pcidl
, LONG lUnknown
, IShellFolder
** ppsf
, LPITEMIDLIST
* ppidl1
, LPITEMIDLIST
*ppidl2
, LONG
* lpUnknown
)
457 HRESULT STDMETHODCALLTYPE
CMergedFolder::ParseDisplayName(
460 LPOLESTR lpszDisplayName
,
463 ULONG
*pdwAttributes
)
477 TRACE("ParseDisplayName name=%S\n", lpszDisplayName
);
479 hr
= m_EnumSource
->FindByName(hwndOwner
, lpszDisplayName
, &info
);
482 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
485 *ppidl
= ILClone(info
.pidl
);
488 *pchEaten
= lstrlenW(info
.parseName
);
491 *pdwAttributes
= info
.parent
->GetAttributesOf(1, (LPCITEMIDLIST
*)ppidl
, pdwAttributes
);
496 HRESULT STDMETHODCALLTYPE
CMergedFolder::EnumObjects(
499 IEnumIDList
**ppenumIDList
)
501 TRACE("EnumObjects\n");
502 HRESULT hr
= m_EnumSource
->QueryInterface(IID_PPV_ARG(IEnumIDList
, ppenumIDList
));
503 if (FAILED_UNEXPECTEDLY(hr
))
505 return m_EnumSource
->Begin(hwndOwner
, grfFlags
);
508 HRESULT STDMETHODCALLTYPE
CMergedFolder::BindToObject(
517 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
518 if (FAILED_UNEXPECTEDLY(hr
))
521 TRACE("BindToObject shared = %d\n", info
.shared
);
524 return info
.parent
->BindToObject(info
.pidl
, pbcReserved
, riid
, ppvOut
);
526 if (riid
!= IID_IShellFolder
)
529 // Construct a child MergedFolder and return it
530 CComPtr
<IShellFolder
> fld1
;
531 CComPtr
<IShellFolder
> fld2
;
533 // In shared folders, the user one takes precedence over the common one, so it will always be on pidl1
534 hr
= m_UserLocal
->BindToObject(info
.pidl
, pbcReserved
, IID_PPV_ARG(IShellFolder
, &fld1
));
535 if (FAILED_UNEXPECTEDLY(hr
))
538 hr
= m_AllUsers
->BindToObject(info
.pidl2
, pbcReserved
, IID_PPV_ARG(IShellFolder
, &fld2
));
539 if (FAILED_UNEXPECTEDLY(hr
))
542 CComPtr
<IAugmentedShellFolder
> pasf
;
543 hr
= CMergedFolder_CreateInstance(IID_PPV_ARG(IAugmentedShellFolder
, &pasf
));
544 if (FAILED_UNEXPECTEDLY(hr
))
547 hr
= pasf
->QueryInterface(riid
, ppvOut
);
548 if (FAILED_UNEXPECTEDLY(hr
))
551 hr
= pasf
->AddNameSpace(NULL
, fld1
, info
.pidl
, 0xFF00);
552 if (FAILED_UNEXPECTEDLY(hr
))
555 hr
= pasf
->AddNameSpace(NULL
, fld2
, info
.pidl2
, 0x0000);
556 if (FAILED_UNEXPECTEDLY(hr
))
562 HRESULT STDMETHODCALLTYPE
CMergedFolder::BindToStorage(
572 HRESULT STDMETHODCALLTYPE
CMergedFolder::CompareIDs(
577 TRACE("CompareIDs\n");
578 return m_UserLocal
->CompareIDs(lParam
, pidl1
, pidl2
);
581 HRESULT STDMETHODCALLTYPE
CMergedFolder::CreateViewObject(
590 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetAttributesOf(
592 PCUITEMID_CHILD_ARRAY apidl
,
598 TRACE("GetAttributesOf\n");
600 for (int i
= 0; i
< (int)cidl
; i
++)
602 LPCITEMIDLIST pidl
= apidl
[i
];
604 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
605 if (FAILED_UNEXPECTEDLY(hr
))
610 SFGAOF
* pinOut1
= rgfInOut
? rgfInOut
+ i
: NULL
;
612 hr
= info
.parent
->GetAttributesOf(1, &pidl
, pinOut1
);
614 if (FAILED_UNEXPECTEDLY(hr
))
621 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetUIObjectOf(
624 PCUITEMID_CHILD_ARRAY apidl
,
632 TRACE("GetUIObjectOf\n");
634 for (int i
= 0; i
< (int)cidl
; i
++)
636 LPCITEMIDLIST pidl
= apidl
[i
];
638 TRACE("Processing GetUIObjectOf item %d of %u...\n", i
, cidl
);
640 hr
= m_EnumSource
->FindPidlInList(hwndOwner
, pidl
, &info
);
641 if (FAILED_UNEXPECTEDLY(hr
))
646 TRACE("FindPidlInList succeeded with parent %p and pidl { db=%d }\n", info
.parent
, info
.pidl
->mkid
.cb
);
648 UINT
* pinOut1
= prgfInOut
? prgfInOut
+i
: NULL
;
649 void** ppvOut1
= ppvOut
? ppvOut
+ i
: NULL
;
651 hr
= info
.parent
->GetUIObjectOf(hwndOwner
, 1, &pidl
, riid
, pinOut1
, ppvOut1
);
653 if (FAILED_UNEXPECTEDLY(hr
))
660 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDisplayNameOf(
668 TRACE("GetDisplayNameOf\n");
670 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
671 if (FAILED_UNEXPECTEDLY(hr
))
674 hr
= info
.parent
->GetDisplayNameOf(info
.pidl
, uFlags
, lpName
);
676 if (FAILED_UNEXPECTEDLY(hr
))
681 HRESULT STDMETHODCALLTYPE
CMergedFolder::SetNameOf(
686 LPITEMIDLIST
*ppidlOut
)
693 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetClassID(CLSID
*lpClassId
)
700 HRESULT STDMETHODCALLTYPE
CMergedFolder::Initialize(LPCITEMIDLIST pidl
)
702 m_shellPidl
= ILClone(pidl
);
707 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetCurFolder(PIDLIST_ABSOLUTE
* pidl
)
715 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDefaultSearchGUID(
722 HRESULT STDMETHODCALLTYPE
CMergedFolder::EnumSearches(
723 IEnumExtraSearch
**ppenum
)
729 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDefaultColumn(
738 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDefaultColumnState(
740 SHCOLSTATEF
*pcsFlags
)
746 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDetailsEx(
748 const SHCOLUMNID
*pscid
,
754 TRACE("GetDetailsEx\n");
756 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
757 if (FAILED_UNEXPECTEDLY(hr
))
760 CComPtr
<IShellFolder2
> parent2
;
761 hr
= info
.parent
->QueryInterface(IID_PPV_ARG(IShellFolder2
, &parent2
));
762 if (FAILED_UNEXPECTEDLY(hr
))
765 hr
= parent2
->GetDetailsEx(info
.pidl
, pscid
, pv
);
766 if (FAILED_UNEXPECTEDLY(hr
))
771 HRESULT STDMETHODCALLTYPE
CMergedFolder::GetDetailsOf(
779 TRACE("GetDetailsOf\n");
781 hr
= m_EnumSource
->FindPidlInList(NULL
, pidl
, &info
);
782 if (FAILED_UNEXPECTEDLY(hr
))
785 CComPtr
<IShellFolder2
> parent2
;
786 hr
= info
.parent
->QueryInterface(IID_PPV_ARG(IShellFolder2
, &parent2
));
787 if (FAILED_UNEXPECTEDLY(hr
))
790 hr
= parent2
->GetDetailsOf(info
.pidl
, iColumn
, psd
);
792 if (FAILED_UNEXPECTEDLY(hr
))
797 HRESULT STDMETHODCALLTYPE
CMergedFolder::MapColumnToSCID(
805 // IAugmentedShellFolder3
806 HRESULT STDMETHODCALLTYPE
CMergedFolder::QueryNameSpace2(ULONG
, QUERYNAMESPACEINFO
*)
813 HRESULT WINAPI
RSHELL_CMergedFolder_CreateInstance(REFIID riid
, LPVOID
*ppv
)
815 return ShellObjectCreator
<CMergedFolder
>(riid
, ppv
);