explorerframe: Implement expansion of nodes.
This commit is contained in:
parent
6963f37253
commit
4ad1050eb5
|
@ -112,6 +112,32 @@ static HRESULT events_OnItemDeleted(NSTC2Impl *This, IShellItem *psi, BOOL fIsRo
|
|||
return ret;
|
||||
}
|
||||
|
||||
static HRESULT events_OnBeforeExpand(NSTC2Impl *This, IShellItem *psi)
|
||||
{
|
||||
HRESULT ret;
|
||||
LONG refcount;
|
||||
if(!This->pnstce) return S_OK;
|
||||
|
||||
refcount = IShellItem_AddRef(psi);
|
||||
ret = INameSpaceTreeControlEvents_OnBeforeExpand(This->pnstce, psi);
|
||||
if(IShellItem_Release(psi) < refcount - 1)
|
||||
ERR("ShellItem was released by client - please file a bug.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static HRESULT events_OnAfterExpand(NSTC2Impl *This, IShellItem *psi)
|
||||
{
|
||||
HRESULT ret;
|
||||
LONG refcount;
|
||||
if(!This->pnstce) return S_OK;
|
||||
|
||||
refcount = IShellItem_AddRef(psi);
|
||||
ret = INameSpaceTreeControlEvents_OnAfterExpand(This->pnstce, psi);
|
||||
if(IShellItem_Release(psi) < refcount - 1)
|
||||
ERR("ShellItem was released by client - please file a bug.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* NamespaceTree helper functions
|
||||
*/
|
||||
|
@ -176,6 +202,25 @@ static IShellItem *shellitem_from_treeitem(NSTC2Impl *This, HTREEITEM hitem)
|
|||
return (IShellItem*)tvi.lParam;
|
||||
}
|
||||
|
||||
/* Returns the root that the given treeitem belongs to. */
|
||||
static nstc_root *root_for_treeitem(NSTC2Impl *This, HTREEITEM hitem)
|
||||
{
|
||||
HTREEITEM tmp, hroot = hitem;
|
||||
nstc_root *root;
|
||||
|
||||
/* Work our way up the hierarchy */
|
||||
for(tmp = hitem; tmp != NULL; hroot = tmp?tmp:hroot)
|
||||
tmp = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hroot);
|
||||
|
||||
/* Search through the list of roots for a match */
|
||||
LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
|
||||
if(root->htreeitem == hroot)
|
||||
break;
|
||||
|
||||
TRACE("root is %p\n", root);
|
||||
return root;
|
||||
}
|
||||
|
||||
static int get_icon(LPCITEMIDLIST lpi, UINT extra_flags)
|
||||
{
|
||||
SHFILEINFOW sfi;
|
||||
|
@ -212,6 +257,67 @@ static HTREEITEM insert_shellitem(NSTC2Impl *This, IShellItem *psi,
|
|||
return hinserted;
|
||||
}
|
||||
|
||||
/* Enumerates the children of the folder represented by hitem
|
||||
* according to the settings for the root, and adds them to the
|
||||
* treeview. Returns the number of children added. */
|
||||
static UINT fill_sublevel(NSTC2Impl *This, HTREEITEM hitem)
|
||||
{
|
||||
IShellItem *psiParent = shellitem_from_treeitem(This, hitem);
|
||||
nstc_root *root = root_for_treeitem(This, hitem);
|
||||
LPITEMIDLIST pidl_parent;
|
||||
IShellFolder *psf;
|
||||
IEnumIDList *peidl;
|
||||
UINT added = 0;
|
||||
HRESULT hr;
|
||||
|
||||
hr = SHGetIDListFromObject((IUnknown*)psiParent, &pidl_parent);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
hr = IShellItem_BindToHandler(psiParent, NULL, &BHID_SFObject, &IID_IShellFolder, (void**)&psf);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
hr = IShellFolder_EnumObjects(psf, NULL, root->enum_flags, &peidl);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
LPITEMIDLIST pidl;
|
||||
IShellItem *psi;
|
||||
ULONG fetched;
|
||||
|
||||
while( S_OK == IEnumIDList_Next(peidl, 1, &pidl, &fetched) )
|
||||
{
|
||||
hr = SHCreateShellItem(NULL, psf , pidl, &psi);
|
||||
ILFree(pidl);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
if(insert_shellitem(This, psi, hitem, NULL))
|
||||
{
|
||||
events_OnItemAdded(This, psi, FALSE);
|
||||
added++;
|
||||
}
|
||||
|
||||
IShellItem_Release(psi);
|
||||
}
|
||||
else
|
||||
ERR("SHCreateShellItem failed with 0x%08x\n", hr);
|
||||
}
|
||||
IEnumIDList_Release(peidl);
|
||||
}
|
||||
else
|
||||
ERR("EnumObjects failed with 0x%08x\n", hr);
|
||||
|
||||
IShellFolder_Release(psf);
|
||||
}
|
||||
else
|
||||
ERR("BindToHandler failed with 0x%08x\n", hr);
|
||||
|
||||
ILFree(pidl_parent);
|
||||
}
|
||||
else
|
||||
ERR("SHGetIDListFromObject failed with 0x%08x\n", hr);
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* NamespaceTree window functions
|
||||
*/
|
||||
|
@ -358,6 +464,50 @@ static LRESULT on_tvn_getdispinfow(NSTC2Impl *This, LPARAM lParam)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL treenode_has_subfolders(NSTC2Impl *This, HTREEITEM node)
|
||||
{
|
||||
return SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
|
||||
}
|
||||
|
||||
static LRESULT on_tvn_itemexpandingw(NSTC2Impl *This, LPARAM lParam)
|
||||
{
|
||||
NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
|
||||
IShellItem *psi;
|
||||
TRACE("%p\n", This);
|
||||
|
||||
psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
|
||||
events_OnBeforeExpand(This, psi);
|
||||
|
||||
if(!treenode_has_subfolders(This, nmtv->itemNew.hItem))
|
||||
{
|
||||
/* The node has no children, try to find some */
|
||||
if(!fill_sublevel(This, nmtv->itemNew.hItem))
|
||||
{
|
||||
TVITEMEXW tvi;
|
||||
/* Failed to enumerate any children, remove the expando
|
||||
* (if any). */
|
||||
tvi.hItem = nmtv->itemNew.hItem;
|
||||
tvi.mask = TVIF_CHILDREN;
|
||||
tvi.cChildren = 0;
|
||||
SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static LRESULT on_tvn_itemexpandedw(NSTC2Impl *This, LPARAM lParam)
|
||||
{
|
||||
NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
|
||||
IShellItem *psi;
|
||||
TRACE("%p\n", This);
|
||||
|
||||
psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
|
||||
events_OnAfterExpand(This, psi);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK NSTC2_WndProc(HWND hWnd, UINT uMessage,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
@ -375,6 +525,8 @@ static LRESULT CALLBACK NSTC2_WndProc(HWND hWnd, UINT uMessage,
|
|||
{
|
||||
case TVN_DELETEITEMW: return on_tvn_deleteitemw(This, lParam);
|
||||
case TVN_GETDISPINFOW: return on_tvn_getdispinfow(This, lParam);
|
||||
case TVN_ITEMEXPANDINGW: return on_tvn_itemexpandingw(This, lParam);
|
||||
case TVN_ITEMEXPANDEDW: return on_tvn_itemexpandedw(This, lParam);
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -317,6 +317,43 @@ static INameSpaceTreeControlEventsImpl *create_nstc_events(void)
|
|||
return This;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Event count checking
|
||||
*/
|
||||
static void ok_no_events_(INameSpaceTreeControlEventsImpl *impl,
|
||||
const char *file, int line)
|
||||
{
|
||||
UINT i;
|
||||
for(i = 0; i < LastEvent; i++)
|
||||
{
|
||||
ok_(file, line)
|
||||
(!impl->count[i], "Got event %d, count %d\n", i, impl->count[i]);
|
||||
impl->count[i] = 0;
|
||||
}
|
||||
}
|
||||
#define ok_no_events(impl) \
|
||||
ok_no_events_(impl, __FILE__, __LINE__)
|
||||
|
||||
#define ok_event_count_broken(impl, event, c, b) \
|
||||
do { ok(impl->count[event] == c || broken(impl->count[event] == b), \
|
||||
"Got event %d, count %d\n", event, impl->count[event]); \
|
||||
impl->count[event] = 0; \
|
||||
} while(0)
|
||||
|
||||
#define ok_event_count(impl, event, c) \
|
||||
ok_event_count_broken(impl, event, c, -1)
|
||||
|
||||
#define ok_event_broken(impl, event) \
|
||||
do { ok(impl->count[event] || broken(!impl->count[event]), \
|
||||
"No event.\n"); \
|
||||
impl->count[event] = 0; \
|
||||
} while(0)
|
||||
|
||||
#define ok_event(impl, event) \
|
||||
do { ok(impl->count[event], "No event %d.\n", event); \
|
||||
impl->count[event] = 0; \
|
||||
} while(0)
|
||||
|
||||
/* Process some messages */
|
||||
static void process_msgs(void)
|
||||
{
|
||||
|
@ -403,6 +440,27 @@ static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
|
|||
return lpszPath;
|
||||
}
|
||||
|
||||
static HWND get_treeview_hwnd(INameSpaceTreeControl *pnstc)
|
||||
{
|
||||
IOleWindow *pow;
|
||||
HRESULT hr;
|
||||
HWND treeview = NULL;
|
||||
|
||||
hr = INameSpaceTreeControl_QueryInterface(pnstc, &IID_IOleWindow, (void**)&pow);
|
||||
ok(hr == S_OK, "Got 0x%08x\n", hr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
HWND host;
|
||||
hr = IOleWindow_GetWindow(pow, &host);
|
||||
ok(hr == S_OK, "Got 0x%08x\n", hr);
|
||||
if(SUCCEEDED(hr))
|
||||
treeview = FindWindowExW(host, NULL, WC_TREEVIEWW, NULL);
|
||||
IOleWindow_Release(pow);
|
||||
}
|
||||
|
||||
return treeview;
|
||||
}
|
||||
|
||||
/* Returns FALSE if the NamespaceTreeControl failed to be instantiated. */
|
||||
static BOOL test_initialization(void)
|
||||
{
|
||||
|
@ -1053,6 +1111,7 @@ static void test_events(void)
|
|||
IOleWindow *pow;
|
||||
LPITEMIDLIST pidl_desktop;
|
||||
DWORD cookie1, cookie2;
|
||||
HWND hwnd_tv;
|
||||
HRESULT hr;
|
||||
UINT res;
|
||||
|
||||
|
@ -1074,6 +1133,7 @@ static void test_events(void)
|
|||
/* Create two instances of INameSpaceTreeControlEvents */
|
||||
pnstceimpl = create_nstc_events();
|
||||
pnstce = (INameSpaceTreeControlEvents*)pnstceimpl;
|
||||
ZeroMemory(&pnstceimpl->count, sizeof(UINT)*LastEvent);
|
||||
pnstceimpl2 = create_nstc_events();
|
||||
pnstce2 = (INameSpaceTreeControlEvents*)pnstceimpl2;
|
||||
|
||||
|
@ -1159,6 +1219,62 @@ static void test_events(void)
|
|||
pnstceimpl->qi_called_count);
|
||||
ok(pnstceimpl->ref == 1, "refcount was %d\n", pnstceimpl->ref);
|
||||
|
||||
/* Advise again.. */
|
||||
pnstceimpl->qi_enable_events = 1;
|
||||
pnstceimpl->qi_called_count = 0;
|
||||
hr = INameSpaceTreeControl_TreeAdvise(pnstc, (IUnknown*)pnstce, &cookie2);
|
||||
ok(hr == S_OK, "Got (0x%08x)\n", hr);
|
||||
ok(cookie2 == 1, "Cookie is %d\n", cookie2);
|
||||
ok(cookie1 == cookie2, "Old cookie differs from old cookie.\n");
|
||||
todo_wine
|
||||
{
|
||||
ok(pnstceimpl->qi_called_count == 7 || pnstceimpl->qi_called_count == 4 /* Vista */,
|
||||
"QueryInterface called %d times.\n",
|
||||
pnstceimpl->qi_called_count);
|
||||
}
|
||||
ok(pnstceimpl->ref == 2, "refcount was %d\n", pnstceimpl->ref);
|
||||
|
||||
/* Initialize the control */
|
||||
hr = INameSpaceTreeControl_Initialize(pnstc, hwnd, NULL, 0);
|
||||
ok(hr == S_OK, "Got (0x%08x)\n", hr);
|
||||
ok_no_events(pnstceimpl);
|
||||
|
||||
hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop,
|
||||
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, 0, NULL);
|
||||
ok(hr == S_OK, "Got (0x%08x)\n", hr);
|
||||
process_msgs();
|
||||
ok_event_count_broken(pnstceimpl, OnItemAdded, 1, 0 /* Vista */);
|
||||
ok_event_count(pnstceimpl, OnGetDefaultIconIndex, 0);
|
||||
ok_no_events(pnstceimpl);
|
||||
|
||||
hwnd_tv = get_treeview_hwnd(pnstc);
|
||||
ok(hwnd_tv != NULL, "Failed to get hwnd_tv HWND.\n");
|
||||
if(hwnd_tv)
|
||||
{
|
||||
HTREEITEM hroot;
|
||||
|
||||
/* Test On*Expand */
|
||||
hroot = (HTREEITEM)SendMessageW(hwnd_tv, TVM_GETNEXTITEM, TVGN_ROOT, 0);
|
||||
SendMessage(hwnd_tv, TVM_EXPAND, TVE_EXPAND, (LPARAM)hroot);
|
||||
process_msgs();
|
||||
ok_event_count(pnstceimpl, OnBeforeExpand, 1);
|
||||
ok_event_count(pnstceimpl, OnAfterExpand, 1);
|
||||
ok_event_broken(pnstceimpl, OnItemAdded); /* No event on Vista */
|
||||
todo_wine ok_event_count(pnstceimpl, OnSelectionChanged, 1);
|
||||
ok_no_events(pnstceimpl);
|
||||
SendMessage(hwnd_tv, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)hroot);
|
||||
process_msgs();
|
||||
ok_no_events(pnstceimpl);
|
||||
SendMessage(hwnd_tv, TVM_EXPAND, TVE_EXPAND, (LPARAM)hroot);
|
||||
process_msgs();
|
||||
ok_no_events(pnstceimpl);
|
||||
}
|
||||
else
|
||||
skip("Skipping some tests.\n");
|
||||
|
||||
hr = INameSpaceTreeControl_RemoveAllRoots(pnstc);
|
||||
process_msgs();
|
||||
ok(hr == S_OK, "Got 0x%08x\n", hr);
|
||||
|
||||
hr = INameSpaceTreeControl_QueryInterface(pnstc, &IID_IOleWindow, (void**)&pow);
|
||||
ok(hr == S_OK, "Got 0x%08x\n", hr);
|
||||
|
@ -1171,6 +1287,9 @@ static void test_events(void)
|
|||
IOleWindow_Release(pow);
|
||||
}
|
||||
|
||||
hr = INameSpaceTreeControl_TreeUnadvise(pnstc, cookie2);
|
||||
ok(hr == S_OK, "Got 0x%08x\n", hr);
|
||||
|
||||
res = INameSpaceTreeControl_Release(pnstc);
|
||||
ok(!res, "res was %d!\n", res);
|
||||
|
||||
|
|
Loading…
Reference in New Issue