[PATCH 6/6] explorerframe: Implement expansion of nodes.
David Hedberg
david.hedberg at gmail.com
Tue Aug 3 20:57:52 CDT 2010
---
dlls/explorerframe/nstc.c | 152 +++++++++++++++++++++++++++++++++++++++
dlls/explorerframe/tests/nstc.c | 119 ++++++++++++++++++++++++++++++
2 files changed, 271 insertions(+), 0 deletions(-)
diff --git a/dlls/explorerframe/nstc.c b/dlls/explorerframe/nstc.c
index 5f6de53..534c06c 100644
--- a/dlls/explorerframe/nstc.c
+++ b/dlls/explorerframe/nstc.c
@@ -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;
diff --git a/dlls/explorerframe/tests/nstc.c b/dlls/explorerframe/tests/nstc.c
index fc8597a..d40feb5 100644
--- a/dlls/explorerframe/tests/nstc.c
+++ b/dlls/explorerframe/tests/nstc.c
@@ -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);
--
1.7.2
More information about the wine-patches
mailing list