[PATCH 3/6] explorerframe: Implement expansion of nodes.

David Hedberg david.hedberg at gmail.com
Mon Aug 16 02:17:32 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 3bd3aa2..e595260 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 0622322..a2e5773 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