* Implement a rudimentary but mostly functional (for start menu purposes) CMergedFolder class. It's still disabled by default because clicking on an item in a merged folder appears to fail to execute it.

svn path=/branches/shell-experiments/; revision=63658
This commit is contained in:
David Quintana 2014-06-28 15:38:25 +00:00
parent 9ebe8b2e4e
commit f03fef66d1
5 changed files with 449 additions and 106 deletions

View file

@ -31,6 +31,7 @@ add_importlibs(rshell
uxtheme
shlwapi
shell32
comctl32
gdi32
ole32
user32

View file

@ -1260,10 +1260,9 @@ HRESULT CMenuSFToolbar::FillToolbar(BOOL clearFirst)
IEnumIDList * eidl;
m_shellFolder->EnumObjects(GetToolbar(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &eidl);
LPITEMIDLIST item = static_cast<LPITEMIDLIST>(CoTaskMemAlloc(sizeof(ITEMIDLIST)));
ULONG fetched;
hr = eidl->Next(1, &item, &fetched);
while (SUCCEEDED(hr) && fetched > 0)
LPITEMIDLIST item = { 0 };
hr = eidl->Next(1, &item, NULL);
while (hr == S_OK)
{
INT index = 0;
INT indexOpen = 0;
@ -1286,13 +1285,13 @@ HRESULT CMenuSFToolbar::FillToolbar(BOOL clearFirst)
DWORD_PTR dwData = reinterpret_cast<DWORD_PTR>(ILClone(item));
// Fetch next item already, so we know if the current one is the last
hr = eidl->Next(1, &item, &fetched);
hr = eidl->Next(1, &item, NULL);
AddButton(++i, MenuString, attrs & SFGAO_FOLDER, index, dwData, FAILED(hr) || fetched == 0);
AddButton(++i, MenuString, attrs & SFGAO_FOLDER, index, dwData, hr != S_OK);
CoTaskMemFree(MenuString);
}
CoTaskMemFree(item);
ILFree(item);
// If no items were added, show the "empty" placeholder
if (i == 0)

View file

@ -25,18 +25,33 @@
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()
@ -45,84 +60,346 @@ public:
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)
{
HRESULT hr;
ULONG *pceltFetched);
*pceltFetched = 0;
if (!m_FirstDone)
{
hr = m_UserLocal->Next(celt, rgelt, pceltFetched);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (hr == S_FALSE)
m_FirstDone = true;
else if (celt < 2)
return hr;
}
DWORD offset = *pceltFetched;
if (*pceltFetched < celt)
{
rgelt += *pceltFetched;
celt = (celt - *pceltFetched);
*pceltFetched = 0;
hr = m_AllUSers->Next(celt, rgelt, pceltFetched);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
*pceltFetched += offset;
return hr;
}
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Skip(
ULONG celt)
{
UNIMPLEMENTED;
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Reset(
)
{
if (m_FirstDone)
m_AllUSers->Reset();
return m_UserLocal->Reset();
}
virtual HRESULT STDMETHODCALLTYPE Clone(
IEnumIDList **ppenum)
{
UNIMPLEMENTED;
return E_NOTIMPL;
}
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)
{
return Reset();
}
TRACE("Search conditions changed, recreating list...\n");
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)
{
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;
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;
}
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)
{
info.side = 0;
info.pidl = ILClone(pidl1);
ILFree(pidl1);
ILFree(pidl2);
}
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);
m_hDsaCount++;
} while (hr1 == S_OK || hr2 == S_OK);
m_HwndOwner = hwndOwner;
m_Flags = flags;
return Reset();
}
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);
for (int i = 0; i < (int)m_hDsaCount; i++)
{
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
}
}
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;)
{
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)
{
@ -148,7 +425,8 @@ HRESULT CMergedFolder::_SetSources(IShellFolder* userLocal, IShellFolder* allUse
{
m_UserLocal = userLocal;
m_AllUSers = allUsers;
return S_OK;
m_EnumSource = new CComObject<CEnumMergedFolder>();
return m_EnumSource->SetSources(m_UserLocal, m_AllUSers);
}
// IShellFolder
@ -169,9 +447,10 @@ HRESULT STDMETHODCALLTYPE CMergedFolder::EnumObjects(
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(
@ -180,15 +459,33 @@ 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);
if (riid != IID_IShellFolder)
return E_FAIL;
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, riid, ppvOut);
hr = m_AllUSers->BindToObject(pidl, pbcReserved, IID_PPV_ARG(IShellFolder, &fld2));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
return hr;
return CMergedFolder_Constructor(fld1, fld2, riid, ppvOut);
}
HRESULT STDMETHODCALLTYPE CMergedFolder::BindToStorage(
@ -206,8 +503,7 @@ HRESULT STDMETHODCALLTYPE CMergedFolder::CompareIDs(
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2)
{
UNIMPLEMENTED;
return E_NOTIMPL;
return m_UserLocal->CompareIDs(lParam, pidl1, pidl2);
}
HRESULT STDMETHODCALLTYPE CMergedFolder::CreateViewObject(
@ -224,16 +520,29 @@ HRESULT STDMETHODCALLTYPE CMergedFolder::GetAttributesOf(
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];
*rgfInOut = 0;
hr = m_AllUSers->GetAttributesOf(cidl, apidl, rgfInOut);
hr = m_EnumSource->FindPidlInList(pidl, &info);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
return hr;
SFGAOF * pinOut1 = rgfInOut ? rgfInOut + i : NULL;
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(
@ -244,15 +553,30 @@ 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(
@ -260,15 +584,21 @@ 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(

View file

@ -19,6 +19,8 @@
*/
#pragma once
class CEnumMergedFolder;
class CMergedFolder :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IShellFolder2
@ -26,6 +28,7 @@ class CMergedFolder :
private:
CComPtr<IShellFolder> m_UserLocal;
CComPtr<IShellFolder> m_AllUSers;
CComPtr<CEnumMergedFolder> m_EnumSource;
public:
CMergedFolder() {}

View file

@ -4716,8 +4716,18 @@ typedef struct {
_In_ PFNDSAENUMCALLBACK pfnCB,
_In_opt_ void *pData);
WINCOMMCTRLAPI
VOID
WINAPI
DSA_EnumCallback(
_In_ HDSA hdsa,
_In_ PFNDSAENUMCALLBACK enumProc,
_In_opt_ LPVOID lParam);
WINCOMMCTRLAPI PVOID WINAPI DSA_GetItemPtr(_In_ HDSA hdsa, int i);
WINCOMMCTRLAPI BOOL WINAPI DSA_DeleteAllItems(_In_ HDSA hdsa);
WINCOMMCTRLAPI
int
WINAPI