From f87891c36223325308e9c017e207a7c340559c88 Mon Sep 17 00:00:00 2001 From: David Hedberg Date: Wed, 4 Aug 2010 03:57:47 +0200 Subject: [PATCH] explorerframe: Implement InsertRoot and AppendRoot. --- dlls/explorerframe/Makefile.in | 2 +- dlls/explorerframe/nstc.c | 122 ++++++++++++++++++++++++++++++-- dlls/explorerframe/tests/nstc.c | 97 +++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 5 deletions(-) diff --git a/dlls/explorerframe/Makefile.in b/dlls/explorerframe/Makefile.in index 8dd454265e1..ef5ba562286 100644 --- a/dlls/explorerframe/Makefile.in +++ b/dlls/explorerframe/Makefile.in @@ -4,7 +4,7 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = explorerframe.dll -IMPORTS = uuid shell32 user32 +IMPORTS = uuid ole32 shell32 user32 C_SRCS = \ explorerframe_main.c \ diff --git a/dlls/explorerframe/nstc.c b/dlls/explorerframe/nstc.c index da84ccc2f86..153c4c91278 100644 --- a/dlls/explorerframe/nstc.c +++ b/dlls/explorerframe/nstc.c @@ -27,13 +27,25 @@ #include "winerror.h" #include "windef.h" #include "winbase.h" +#include "winuser.h" +#include "shellapi.h" +#include "wine/list.h" #include "wine/debug.h" #include "explorerframe_main.h" WINE_DEFAULT_DEBUG_CHANNEL(nstc); +typedef struct nstc_root { + IShellItem *psi; + HTREEITEM htreeitem; + SHCONTF enum_flags; + NSTCROOTSTYLE root_style; + IShellItemFilter *pif; + struct list entry; +} nstc_root; + typedef struct { const INameSpaceTreeControl2Vtbl *lpVtbl; const IOleWindowVtbl *lpowVtbl; @@ -44,6 +56,7 @@ typedef struct { NSTCSTYLE style; NSTCSTYLE2 style2; + struct list roots; INameSpaceTreeControlEvents *pnstce; } NSTC2Impl; @@ -56,6 +69,22 @@ static const DWORD unsupported_styles2 = NSTCS2_INTERRUPTNOTIFICATIONS | NSTCS2_SHOWNULLSPACEMENU | NSTCS2_DISPLAYPADDING | NSTCS2_DISPLAYPINNEDONLY | NTSCS2_NOSINGLETONAUTOEXPAND | NTSCS2_NEVERINSERTNONENUMERATED; +/************************************************************************* +* NamespaceTree event wrappers +*/ +static HRESULT events_OnItemAdded(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot) +{ + HRESULT ret; + LONG refcount; + if(!This->pnstce) return S_OK; + + refcount = IShellItem_AddRef(psi); + ret = INameSpaceTreeControlEvents_OnItemAdded(This->pnstce, psi, fIsRoot); + if(IShellItem_Release(psi) < refcount - 1) + ERR("ShellItem was released by client - please file a bug.\n"); + return ret; +} + /************************************************************************* * NamespaceTree helper functions */ @@ -106,6 +135,34 @@ static DWORD treeview_style_from_nstcs(NSTC2Impl *This, NSTCSTYLE nstcs, return old_style^*new_style; } +/* Insert a shellitem into the given place in the tree and return the + resulting treeitem. */ +static HTREEITEM insert_shellitem(NSTC2Impl *This, IShellItem *psi, + HTREEITEM hParent, HTREEITEM hInsertAfter) +{ + TVINSERTSTRUCTW tvins; + TVITEMEXW *tvi = &tvins.u.itemex; + HTREEITEM hinserted; + TRACE("%p (%p, %p)\n", psi, hParent, hInsertAfter); + + tvi->mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT; + tvi->cChildren = I_CHILDRENCALLBACK; + tvi->iImage = tvi->iSelectedImage = I_IMAGECALLBACK; + tvi->pszText = LPSTR_TEXTCALLBACKW; + + /* Every treeitem contains a pointer to the corresponding ShellItem. */ + tvi->lParam = (LPARAM)psi; + tvins.hParent = hParent; + tvins.hInsertAfter = hInsertAfter; + + hinserted = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_INSERTITEMW, 0, + (LPARAM)(LPTVINSERTSTRUCTW)&tvins); + if(hinserted) + IShellItem_AddRef(psi); + + return hinserted; +} + /************************************************************************* * NamespaceTree window functions */ @@ -365,8 +422,59 @@ static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface, IShellItemFilter *pif) { NSTC2Impl *This = (NSTC2Impl*)iface; - FIXME("stub, %p, %p, %x, %x, %p\n", This, psiRoot, grfEnumFlags, grfRootStyle, pif); - return E_NOTIMPL; + nstc_root *new_root; + struct list *add_after_entry; + HTREEITEM add_after_hitem; + UINT i; + + TRACE("%p, %d, %p, %x, %x, %p\n", This, iIndex, psiRoot, grfEnumFlags, grfRootStyle, pif); + + new_root = HeapAlloc(GetProcessHeap(), 0, sizeof(nstc_root)); + if(!new_root) + return E_OUTOFMEMORY; + + new_root->psi = psiRoot; + new_root->enum_flags = grfEnumFlags; + new_root->root_style = grfRootStyle; + new_root->pif = pif; + + /* We want to keep the roots in the internal list and in the + * treeview in the same order. */ + add_after_entry = &This->roots; + for(i = 0; i < max(0, iIndex) && list_next(&This->roots, add_after_entry); i++) + add_after_entry = list_next(&This->roots, add_after_entry); + + if(add_after_entry == &This->roots) + add_after_hitem = TVI_FIRST; + else + add_after_hitem = LIST_ENTRY(add_after_entry, nstc_root, entry)->htreeitem; + + new_root->htreeitem = insert_shellitem(This, psiRoot, TVI_ROOT, add_after_hitem); + if(!new_root->htreeitem) + { + WARN("Failed to add the root.\n"); + HeapFree(GetProcessHeap(), 0, new_root); + return E_FAIL; + } + + list_add_after(add_after_entry, &new_root->entry); + events_OnItemAdded(This, psiRoot, TRUE); + + if(grfRootStyle & NSTCRS_HIDDEN) + { + TVITEMEXW tvi; + tvi.mask = TVIF_STATEEX; + tvi.uStateEx = TVIS_EX_FLAT; + tvi.hItem = new_root->htreeitem; + + SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi); + } + + if(grfRootStyle & NSTCRS_EXPANDED) + SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_EXPAND, + (LPARAM)new_root->htreeitem); + + return S_OK; } static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface, @@ -376,9 +484,13 @@ static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface, IShellItemFilter *pif) { NSTC2Impl *This = (NSTC2Impl*)iface; - FIXME("stub, %p, %p, %x, %x, %p\n", + UINT root_count; + TRACE("%p, %p, %x, %x, %p\n", This, psiRoot, grfEnumFlags, grfRootStyle, pif); - return E_NOTIMPL; + + root_count = list_count(&This->roots); + + return NSTC2_fnInsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif); } static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface, @@ -702,6 +814,8 @@ HRESULT NamespaceTreeControl_Constructor(IUnknown *pUnkOuter, REFIID riid, void nstc->lpVtbl = &vt_INameSpaceTreeControl2; nstc->lpowVtbl = &vt_IOleWindow; + list_init(&nstc->roots); + ret = INameSpaceTreeControl_QueryInterface((INameSpaceTreeControl*)nstc, riid, ppv); INameSpaceTreeControl_Release((INameSpaceTreeControl*)nstc); diff --git a/dlls/explorerframe/tests/nstc.c b/dlls/explorerframe/tests/nstc.c index f48418bda18..2f8caa0fe05 100644 --- a/dlls/explorerframe/tests/nstc.c +++ b/dlls/explorerframe/tests/nstc.c @@ -315,6 +315,35 @@ static INameSpaceTreeControlEventsImpl *create_nstc_events(void) return This; } +/* Process some messages */ +static void process_msgs(void) +{ + MSG msg; + BOOL got_msg; + do { + got_msg = FALSE; + Sleep(100); + while(PeekMessage( &msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + got_msg = TRUE; + } + } while(got_msg); + + /* There seem to be a timer that sometimes fires after about + 500ms, we need to wait for it. Failing to wait can result in + seemingly sporadic selection change events. (Timer ID is 87, + sending WM_TIMER manually does not seem to help us.) */ + Sleep(500); + + while(PeekMessage( &msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + /* Returns FALSE if the NamespaceTreeControl failed to be instantiated. */ static BOOL test_initialization(void) { @@ -470,15 +499,52 @@ static void test_basics(void) { INameSpaceTreeControl *pnstc; INameSpaceTreeControl2 *pnstc2; + IShellFolder *psfdesktop; + IShellItem *psidesktop, *psidesktop2; IOleWindow *pow; + LPITEMIDLIST pidl_desktop; HRESULT hr; UINT i, res; RECT rc; + /* These should exist on platforms supporting the NSTC */ + ok(pSHCreateShellItem != NULL, "No SHCreateShellItem.\n"); + ok(pSHGetIDListFromObject != NULL, "No SHCreateShellItem.\n"); + + /* Create ShellItems for testing. */ + SHGetDesktopFolder(&psfdesktop); + hr = pSHGetIDListFromObject((IUnknown*)psfdesktop, &pidl_desktop); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psidesktop); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psidesktop2); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(FAILED(hr)) IShellItem_Release(psidesktop); + } + ILFree(pidl_desktop); + } + ok(psidesktop != psidesktop2, "psidesktop == psidesktop2\n"); + IShellFolder_Release(psfdesktop); + + if(FAILED(hr)) + { + win_skip("Test setup failed.\n"); + return; + } + hr = CoCreateInstance(&CLSID_NamespaceTreeControl, NULL, CLSCTX_INPROC_SERVER, &IID_INameSpaceTreeControl, (void**)&pnstc); ok(hr == S_OK, "Failed to initialize control (0x%08x)\n", hr); + /* Some tests on an uninitialized control */ + hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop, SHCONTF_NONFOLDERS, 0, NULL); + ok(hr == E_FAIL, "Got (0x%08x)\n", hr); + process_msgs(); + /* Initialize the control */ rc.top = rc.left = 0; rc.right = rc.bottom = 200; hr = INameSpaceTreeControl_Initialize(pnstc, hwnd, &rc, 0); @@ -697,6 +763,37 @@ static void test_basics(void) skip("INameSpaceTreeControl2 missing.\n"); } + /* Append / Insert root */ + if(0) + { + /* Crashes under Windows 7 */ + hr = INameSpaceTreeControl_AppendRoot(pnstc, NULL, SHCONTF_FOLDERS, 0, NULL); + hr = INameSpaceTreeControl_InsertRoot(pnstc, 0, NULL, SHCONTF_FOLDERS, 0, NULL); + } + + /* Note the usage of psidesktop and psidesktop2 */ + hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop2, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + process_msgs(); + + hr = INameSpaceTreeControl_InsertRoot(pnstc, 0, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + hr = INameSpaceTreeControl_InsertRoot(pnstc, -1, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + hr = INameSpaceTreeControl_InsertRoot(pnstc, -1, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + hr = INameSpaceTreeControl_InsertRoot(pnstc, 50, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + hr = INameSpaceTreeControl_InsertRoot(pnstc, 1, psidesktop, SHCONTF_FOLDERS, 0, NULL); + ok(hr == S_OK, "Got (0x%08x)\n", hr); + + IShellItem_Release(psidesktop); + IShellItem_Release(psidesktop2); + hr = INameSpaceTreeControl_QueryInterface(pnstc, &IID_IOleWindow, (void**)&pow); ok(hr == S_OK, "Got 0x%08x\n", hr); if(SUCCEEDED(hr))