WINE_DEFAULT_DEBUG_CHANNEL(CMergedFolder);
+struct LocalPidlInfo
+{
+ int side; // -1 local, 0 shared, 1 common
+ LPITEMIDLIST pidl;
+};
+
class CEnumMergedFolder :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IEnumIDList
{
+
private:
+ CComPtr<IShellFolder> m_UserLocalFolder;
+ CComPtr<IShellFolder> m_AllUSersFolder;
CComPtr<IEnumIDList> m_UserLocal;
CComPtr<IEnumIDList> m_AllUSers;
- BOOL m_FirstDone;
+
+ HWND m_HwndOwner;
+ SHCONTF m_Flags;
+
+ HDSA m_hDsa;
+ UINT m_hDsaIndex;
+ UINT m_hDsaCount;
public:
- CEnumMergedFolder() : m_UserLocal(NULL), m_AllUSers(NULL), m_FirstDone(FALSE) {}
- virtual ~CEnumMergedFolder() {}
+ CEnumMergedFolder();
+ virtual ~CEnumMergedFolder();
DECLARE_NOT_AGGREGATABLE(CEnumMergedFolder)
DECLARE_PROTECT_FINAL_CONSTRUCT()
COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
END_COM_MAP()
- HRESULT Begin(HWND hwndOwner, SHCONTF flags, IShellFolder * userLocal, IShellFolder * allUSers)
- {
- HRESULT hr;
- hr = userLocal->EnumObjects(hwndOwner, flags, &m_UserLocal);
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
- hr = userLocal->EnumObjects(hwndOwner, flags, &m_AllUSers);
- if (FAILED_UNEXPECTEDLY(hr))
- {
- m_UserLocal = NULL;
- return hr;
- }
- m_FirstDone = FALSE;
- return S_OK;
- }
+ int DsaDeleteCallback(LocalPidlInfo * info);
+
+ static int CALLBACK s_DsaDeleteCallback(void *pItem, void *pData);
+
+ HRESULT SetSources(IShellFolder * userLocal, IShellFolder * allUSers);
+ HRESULT Begin(HWND hwndOwner, SHCONTF flags);
+ HRESULT FindPidlInList(LPCITEMIDLIST pcidl, LocalPidlInfo * pinfo);
virtual HRESULT STDMETHODCALLTYPE Next(
ULONG celt,
LPITEMIDLIST *rgelt,
- ULONG *pceltFetched)
+ ULONG *pceltFetched);
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt);
+ virtual HRESULT STDMETHODCALLTYPE Reset();
+ virtual HRESULT STDMETHODCALLTYPE Clone(IEnumIDList **ppenum);
+};
+
+CEnumMergedFolder::CEnumMergedFolder() :
+ m_UserLocalFolder(NULL),
+ m_AllUSersFolder(NULL),
+ m_UserLocal(NULL),
+ m_AllUSers(NULL),
+ m_HwndOwner(NULL),
+ m_Flags(0),
+ m_hDsaIndex(0)
+{
+ m_hDsa = DSA_Create(sizeof(LocalPidlInfo), 10);
+}
+
+CEnumMergedFolder::~CEnumMergedFolder()
+{
+ DSA_DestroyCallback(m_hDsa, s_DsaDeleteCallback, this);
+}
+
+int CEnumMergedFolder::DsaDeleteCallback(LocalPidlInfo * info)
+{
+ ILFree(info->pidl);
+ return 0;
+}
+
+int CALLBACK CEnumMergedFolder::s_DsaDeleteCallback(void *pItem, void *pData)
+{
+ CEnumMergedFolder * mf = (CEnumMergedFolder*) pData;
+ LocalPidlInfo * item = (LocalPidlInfo*) pItem;
+ return mf->DsaDeleteCallback(item);
+}
+
+HRESULT CEnumMergedFolder::SetSources(IShellFolder * userLocal, IShellFolder * allUSers)
+{
+ m_UserLocalFolder = userLocal;
+ m_AllUSersFolder = allUSers;
+ return S_OK;
+}
+
+HRESULT CEnumMergedFolder::Begin(HWND hwndOwner, SHCONTF flags)
+{
+ HRESULT hr;
+
+ if (m_HwndOwner == hwndOwner && m_Flags == flags)
{
- HRESULT hr;
+ return Reset();
+ }
- *pceltFetched = 0;
+ TRACE("Search conditions changed, recreating list...\n");
- if (!m_FirstDone)
+ hr = m_UserLocalFolder->EnumObjects(hwndOwner, flags, &m_UserLocal);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ hr = m_AllUSersFolder->EnumObjects(hwndOwner, flags, &m_AllUSers);
+ if (FAILED_UNEXPECTEDLY(hr))
+ {
+ m_UserLocal = NULL;
+ return hr;
+ }
+
+ DSA_EnumCallback(m_hDsa, s_DsaDeleteCallback, this);
+ DSA_DeleteAllItems(m_hDsa);
+ m_hDsaCount = 0;
+
+ HRESULT hr1 = S_OK;
+ HRESULT hr2 = S_OK;
+ LPITEMIDLIST pidl1 = NULL;
+ LPITEMIDLIST pidl2 = NULL;
+ int order = 0;
+ do
+ {
+ if (order <= 0)
{
- hr = m_UserLocal->Next(celt, rgelt, pceltFetched);
- if (FAILED_UNEXPECTEDLY(hr))
+ if (hr1 == S_OK)
+ {
+ hr1 = m_UserLocal->Next(1, &pidl1, NULL);
+ if (FAILED_UNEXPECTEDLY(hr1))
+ return hr1;
+ }
+ else
+ {
+ pidl1 = NULL;
+ }
+ }
+ if (order >= 0)
+ {
+ if (hr2 == S_OK)
+ {
+ hr2 = m_AllUSers->Next(1, &pidl2, NULL);
+ if (FAILED_UNEXPECTEDLY(hr2))
+ return hr2;
+ }
+ else
+ {
+ pidl2 = NULL;
+ }
+ }
+
+ if (hr1 == S_OK && hr2 == S_OK)
+ {
+ LPWSTR name1;
+ LPWSTR name2;
+ STRRET str1 = { STRRET_WSTR };
+ STRRET str2 = { STRRET_WSTR };
+ hr = m_UserLocalFolder->GetDisplayNameOf(pidl1, SHGDN_FORPARSING | SHGDN_INFOLDER, &str1);
+ if (FAILED(hr))
return hr;
- if (hr == S_FALSE)
- m_FirstDone = true;
- else if (celt < 2)
+ hr = m_AllUSersFolder->GetDisplayNameOf(pidl2, SHGDN_FORPARSING | SHGDN_INFOLDER, &str2);
+ if (FAILED(hr))
return hr;
+ StrRetToStrW(&str1, pidl1, &name1);
+ StrRetToStrW(&str2, pidl2, &name2);
+ order = StrCmpW(name1, name2);
+ CoTaskMemFree(name1);
+ CoTaskMemFree(name2);
+ }
+ else if (hr1 == S_OK)
+ {
+ order = -1;
+ }
+ else if (hr2 == S_OK)
+ {
+ order = 1;
+ }
+ else
+ {
+ break;
}
- DWORD offset = *pceltFetched;
- if (*pceltFetched < celt)
+ LocalPidlInfo info;
+ if (order < 0)
+ {
+ info.side = -1;
+ info.pidl = ILClone(pidl1);
+ ILFree(pidl1);
+ }
+ else if (order > 0)
+ {
+ info.side = 1;
+ info.pidl = ILClone(pidl2);
+ ILFree(pidl2);
+ }
+ else // if (order == 0)
{
- rgelt += *pceltFetched;
- celt = (celt - *pceltFetched);
- *pceltFetched = 0;
+ info.side = 0;
+ info.pidl = ILClone(pidl1);
+ ILFree(pidl1);
+ ILFree(pidl2);
+ }
- hr = m_AllUSers->Next(celt, rgelt, pceltFetched);
- if (FAILED_UNEXPECTEDLY(hr))
- return hr;
+ TRACE("Inserting item %d with side %d and pidl { cb=%d }\n", m_hDsaCount, info.side, info.pidl->mkid.cb);
+ int idx = DSA_InsertItem(m_hDsa, DSA_APPEND, &info);
+ TRACE("New index: %d\n", idx);
- *pceltFetched += offset;
+ m_hDsaCount++;
- return hr;
- }
+ } while (hr1 == S_OK || hr2 == S_OK);
- return S_OK;
- }
+ m_HwndOwner = hwndOwner;
+ m_Flags = flags;
+
+ return Reset();
+}
- virtual HRESULT STDMETHODCALLTYPE Skip(
- ULONG celt)
- {
- UNIMPLEMENTED;
- return E_NOTIMPL;
- }
+HRESULT CEnumMergedFolder::FindPidlInList(LPCITEMIDLIST pcidl, LocalPidlInfo * pinfo)
+{
+ HRESULT hr;
+
+ TRACE("Searching for pidl { cb=%d } in a list of %d items\n", pcidl->mkid.cb, m_hDsaCount);
- virtual HRESULT STDMETHODCALLTYPE Reset(
- )
+ for (int i = 0; i < (int)m_hDsaCount; i++)
{
- if (m_FirstDone)
- m_AllUSers->Reset();
- return m_UserLocal->Reset();
+ LocalPidlInfo * tinfo = (LocalPidlInfo *)DSA_GetItemPtr(m_hDsa, i);
+ if (!tinfo)
+ return E_FAIL;
+
+ LocalPidlInfo info = *tinfo;
+
+ TRACE("Comparing with item at %d with side %d and pidl { cb=%d }\n", i, info.side, info.pidl->mkid.cb);
+
+ if (info.side <= 0)
+ {
+#if 0
+ LPWSTR name1;
+ LPWSTR name2;
+ STRRET str1 = { STRRET_WSTR, 0 };
+ STRRET str2 = { STRRET_WSTR, 0 };
+ hr = m_UserLocalFolder->GetDisplayNameOf(info->pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str1);
+ if (FAILED(hr))
+ return hr;
+ hr = m_UserLocalFolder->GetDisplayNameOf(pcidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str2);
+ if (FAILED(hr))
+ return hr;
+ StrRetToStrW(&str1, info->pidl, &name1);
+ StrRetToStrW(&str2, pcidl, &name2);
+ int order = StrCmpW(name1, name2);
+ CoTaskMemFree(name1);
+ CoTaskMemFree(name2);
+
+ if (order == 0)
+ {
+ *pinfo = *info;
+ return S_OK;
+ }
+#else
+ // FIXME: This works in windows.
+ hr = m_UserLocalFolder->CompareIDs(0, info.pidl, pcidl);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ if (hr == S_OK)
+ {
+ *pinfo = info;
+ return S_OK;
+ }
+ else
+ {
+ TRACE("Comparison returned %d\n", (int) (short) (hr & 0xFFFF));
+ }
+#endif
+ }
+ else
+ {
+#if 0
+ LPWSTR name1;
+ LPWSTR name2;
+ STRRET str1 = { STRRET_WSTR, 0 };
+ STRRET str2 = { STRRET_WSTR, 0 };
+ hr = m_AllUSersFolder->GetDisplayNameOf(info->pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str1);
+ if (FAILED(hr))
+ return hr;
+ hr = m_AllUSersFolder->GetDisplayNameOf(pcidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str2);
+ if (FAILED(hr))
+ return hr;
+ StrRetToStrW(&str1, info->pidl, &name1);
+ StrRetToStrW(&str2, pcidl, &name2);
+ int order = StrCmpW(name1, name2);
+ CoTaskMemFree(name1);
+ CoTaskMemFree(name2);
+
+ if (order == 0)
+ {
+ *pinfo = *info;
+ return S_OK;
+ }
+#else
+ // FIXME: This works in windows.
+ hr = m_AllUSersFolder->CompareIDs(0, info.pidl, pcidl);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ if (hr == S_OK)
+ {
+ *pinfo = info;
+ return S_OK;
+ }
+ else
+ {
+ TRACE("Comparison returned %d\n", (int) (short) (hr & 0xFFFF));
+ }
+#endif
+ }
}
- virtual HRESULT STDMETHODCALLTYPE Clone(
- IEnumIDList **ppenum)
+ TRACE("Pidl not found\n");
+ return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+}
+
+HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Next(
+ ULONG celt,
+ LPITEMIDLIST *rgelt,
+ ULONG *pceltFetched)
+{
+ if (pceltFetched) *pceltFetched = 0;
+
+ if (m_hDsaIndex == m_hDsaCount)
+ return S_FALSE;
+
+ for (int i = 0; i < (int)celt;)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ LocalPidlInfo * tinfo = (LocalPidlInfo *) DSA_GetItemPtr(m_hDsa, m_hDsaIndex);
+ if (!tinfo)
+ return E_FAIL;
+
+ LocalPidlInfo info = *tinfo;
+
+ TRACE("Returning next item at %d with side %d and pidl { cb=%d }\n", m_hDsaIndex, info.side, info.pidl->mkid.cb);
+
+ // FIXME: ILClone shouldn't be needed here! This should be causing leaks
+ if (rgelt) rgelt[i] = ILClone(info.pidl);
+
+ m_hDsaIndex++;
+ i++;
+
+ if (m_hDsaIndex == m_hDsaCount)
+ {
+ if (pceltFetched) *pceltFetched = i;
+ return (i == (int)celt) ? S_OK : S_FALSE;
+ }
}
-};
+
+ if (pceltFetched) *pceltFetched = celt;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Skip(ULONG celt)
+{
+ return Next(celt, NULL, NULL);
+}
+
+HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Reset()
+{
+ m_hDsaIndex = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Clone(
+ IEnumIDList **ppenum)
+{
+ UNIMPLEMENTED;
+ return E_NOTIMPL;
+}
+
+//-----------------------------------------------------------------------------
+// CMergedFolder
extern "C"
HRESULT WINAPI CMergedFolder_Constructor(IShellFolder* userLocal, IShellFolder* allUsers, REFIID riid, LPVOID *ppv)
{
m_UserLocal = userLocal;
m_AllUSers = allUsers;
- return S_OK;
+ m_EnumSource = new CComObject<CEnumMergedFolder>();
+ return m_EnumSource->SetSources(m_UserLocal, m_AllUSers);
}
// IShellFolder
SHCONTF grfFlags,
IEnumIDList **ppenumIDList)
{
- CEnumMergedFolder * merged = new CComObject<CEnumMergedFolder>();
- *ppenumIDList = merged;
- return merged->Begin(hwndOwner, grfFlags, m_UserLocal, m_AllUSers);
+ HRESULT hr = m_EnumSource->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenumIDList));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ return m_EnumSource->Begin(hwndOwner, grfFlags);
}
HRESULT STDMETHODCALLTYPE CMergedFolder::BindToObject(
REFIID riid,
void **ppvOut)
{
+ LocalPidlInfo info;
HRESULT hr;
- hr = m_UserLocal->BindToObject(pidl, pbcReserved, riid, ppvOut);
- if (SUCCEEDED(hr))
+ hr = m_EnumSource->FindPidlInList(pidl, &info);
+ if (FAILED_UNEXPECTEDLY(hr))
return hr;
+
+ if (info.side < 0)
+ return m_UserLocal->BindToObject(pidl, pbcReserved, riid, ppvOut);
+ if (info.side > 0)
+ return m_AllUSers->BindToObject(pidl, pbcReserved, riid, ppvOut);
- hr = m_AllUSers->BindToObject(pidl, pbcReserved, riid, ppvOut);
+ if (riid != IID_IShellFolder)
+ return E_FAIL;
- return hr;
+ CComPtr<IShellFolder> fld1;
+ CComPtr<IShellFolder> fld2;
+
+ hr = m_UserLocal->BindToObject(pidl, pbcReserved, IID_PPV_ARG(IShellFolder, &fld1));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ hr = m_AllUSers->BindToObject(pidl, pbcReserved, IID_PPV_ARG(IShellFolder, &fld2));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ return CMergedFolder_Constructor(fld1, fld2, riid, ppvOut);
}
HRESULT STDMETHODCALLTYPE CMergedFolder::BindToStorage(
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ return m_UserLocal->CompareIDs(lParam, pidl1, pidl2);
}
HRESULT STDMETHODCALLTYPE CMergedFolder::CreateViewObject(
LPCITEMIDLIST *apidl,
SFGAOF *rgfInOut)
{
+ LocalPidlInfo info;
HRESULT hr;
- hr = m_UserLocal->GetAttributesOf(cidl, apidl, rgfInOut);
- if (SUCCEEDED(hr))
- return hr;
+ for (int i = 0; i < (int)cidl; i++)
+ {
+ LPCITEMIDLIST pidl = apidl[i];
+
+ hr = m_EnumSource->FindPidlInList(pidl, &info);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
- *rgfInOut = 0;
- hr = m_AllUSers->GetAttributesOf(cidl, apidl, rgfInOut);
+ SFGAOF * pinOut1 = rgfInOut ? rgfInOut + i : NULL;
- return hr;
+ if (info.side <= 0)
+ hr = m_UserLocal->GetAttributesOf(1, &pidl, pinOut1);
+ else
+ hr = m_AllUSers->GetAttributesOf(1, &pidl, pinOut1);
+
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ }
+
+ return S_OK;
}
HRESULT STDMETHODCALLTYPE CMergedFolder::GetUIObjectOf(
UINT *prgfInOut,
void **ppvOut)
{
+ LocalPidlInfo info;
HRESULT hr;
- hr = m_UserLocal->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
- if (SUCCEEDED(hr))
- return hr;
+ for (int i = 0; i < (int)cidl; i++)
+ {
+ LPCITEMIDLIST pidl = apidl[i];
- hr = m_AllUSers->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
+ hr = m_EnumSource->FindPidlInList(pidl, &info);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
- return hr;
+ UINT * pinOut1 = prgfInOut ? prgfInOut+i : NULL;
+ void** ppvOut1 = ppvOut ? ppvOut + i : NULL;
+
+ if (info.side <= 0)
+ hr = m_UserLocal->GetUIObjectOf(hwndOwner, 1, &pidl, riid, pinOut1, ppvOut1);
+ else
+ hr = m_AllUSers->GetUIObjectOf(hwndOwner, 1, &pidl, riid, pinOut1, ppvOut1);
+
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ }
+
+ return S_OK;
}
HRESULT STDMETHODCALLTYPE CMergedFolder::GetDisplayNameOf(
SHGDNF uFlags,
STRRET *lpName)
{
+ LocalPidlInfo info;
HRESULT hr;
- hr = m_UserLocal->GetDisplayNameOf(pidl, uFlags, lpName);
- if (SUCCEEDED(hr))
+ hr = m_EnumSource->FindPidlInList(pidl, &info);
+ if (FAILED_UNEXPECTEDLY(hr))
return hr;
- hr = m_AllUSers->GetDisplayNameOf(pidl, uFlags, lpName);
+ if (info.side <= 0)
+ hr = m_UserLocal->GetDisplayNameOf(pidl, uFlags, lpName);
+ else
+ hr = m_AllUSers->GetDisplayNameOf(pidl, uFlags, lpName);
- return hr;
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ return S_OK;
}
HRESULT STDMETHODCALLTYPE CMergedFolder::SetNameOf(