wine/dlls/explorerframe/nstc.c
2010-08-17 14:22:52 +02:00

1203 lines
38 KiB
C

/*
* NamespaceTreeControl implementation.
*
* Copyright 2010 David Hedberg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#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;
LONG ref;
HWND hwnd_main;
HWND hwnd_tv;
NSTCSTYLE style;
NSTCSTYLE2 style2;
struct list roots;
INameSpaceTreeControlEvents *pnstce;
} NSTC2Impl;
static const DWORD unsupported_styles =
NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM | NSTCS_FAVORITESMODE |
NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS | NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON |
NSTCS_SHOWREFRESHBUTTON | NSTCS_SPRINGEXPAND | NSTCS_RICHTOOLTIP | NSTCS_NOINDENTCHECKS;
static const DWORD unsupported_styles2 =
NSTCS2_INTERRUPTNOTIFICATIONS | NSTCS2_SHOWNULLSPACEMENU | NSTCS2_DISPLAYPADDING |
NSTCS2_DISPLAYPINNEDONLY | NTSCS2_NOSINGLETONAUTOEXPAND | NTSCS2_NEVERINSERTNONENUMERATED;
/*************************************************************************
* NamespaceTree event wrappers
*/
static HRESULT events_OnGetDefaultIconIndex(NSTC2Impl *This, IShellItem *psi,
int *piDefaultIcon, int *piOpenIcon)
{
HRESULT ret;
LONG refcount;
if(!This->pnstce) return E_NOTIMPL;
refcount = IShellItem_AddRef(psi);
ret = INameSpaceTreeControlEvents_OnGetDefaultIconIndex(This->pnstce, psi, piDefaultIcon, piOpenIcon);
if(IShellItem_Release(psi) < refcount - 1)
ERR("ShellItem was released by client - please file a bug.\n");
return ret;
}
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;
}
static HRESULT events_OnItemDeleted(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot)
{
HRESULT ret;
LONG refcount;
if(!This->pnstce) return S_OK;
refcount = IShellItem_AddRef(psi);
ret = INameSpaceTreeControlEvents_OnItemDeleted(This->pnstce, psi, fIsRoot);
if(IShellItem_Release(psi) < refcount - 1)
ERR("ShellItem was released by client - please file a bug.\n");
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;
}
static HRESULT events_OnSelectionChanged(NSTC2Impl *This, IShellItemArray *psia)
{
if(!This->pnstce) return S_OK;
return INameSpaceTreeControlEvents_OnSelectionChanged(This->pnstce, psia);
}
/*************************************************************************
* NamespaceTree helper functions
*/
static DWORD treeview_style_from_nstcs(NSTC2Impl *This, NSTCSTYLE nstcs,
NSTCSTYLE nstcs_mask, DWORD *new_style)
{
DWORD old_style, tv_mask = 0;
TRACE("%p, %x, %x, %p\n", This, nstcs, nstcs_mask, new_style);
if(This->hwnd_tv)
old_style = GetWindowLongPtrW(This->hwnd_tv, GWL_STYLE);
else
old_style = /* The default */
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
WS_TABSTOP | TVS_NOHSCROLL | TVS_NONEVENHEIGHT | TVS_INFOTIP |
TVS_EDITLABELS | TVS_TRACKSELECT;
if(nstcs_mask & NSTCS_HASEXPANDOS) tv_mask |= TVS_HASBUTTONS;
if(nstcs_mask & NSTCS_HASLINES) tv_mask |= TVS_HASLINES;
if(nstcs_mask & NSTCS_FULLROWSELECT) tv_mask |= TVS_FULLROWSELECT;
if(nstcs_mask & NSTCS_HORIZONTALSCROLL) tv_mask |= TVS_NOHSCROLL;
if(nstcs_mask & NSTCS_ROOTHASEXPANDO) tv_mask |= TVS_LINESATROOT;
if(nstcs_mask & NSTCS_SHOWSELECTIONALWAYS) tv_mask |= TVS_SHOWSELALWAYS;
if(nstcs_mask & NSTCS_NOINFOTIP) tv_mask |= TVS_INFOTIP;
if(nstcs_mask & NSTCS_EVENHEIGHT) tv_mask |= TVS_NONEVENHEIGHT;
if(nstcs_mask & NSTCS_DISABLEDRAGDROP) tv_mask |= TVS_DISABLEDRAGDROP;
if(nstcs_mask & NSTCS_NOEDITLABELS) tv_mask |= TVS_EDITLABELS;
if(nstcs_mask & NSTCS_CHECKBOXES) tv_mask |= TVS_CHECKBOXES;
*new_style = 0;
if(nstcs & NSTCS_HASEXPANDOS) *new_style |= TVS_HASBUTTONS;
if(nstcs & NSTCS_HASLINES) *new_style |= TVS_HASLINES;
if(nstcs & NSTCS_FULLROWSELECT) *new_style |= TVS_FULLROWSELECT;
if(!(nstcs & NSTCS_HORIZONTALSCROLL)) *new_style |= TVS_NOHSCROLL;
if(nstcs & NSTCS_ROOTHASEXPANDO) *new_style |= TVS_LINESATROOT;
if(nstcs & NSTCS_SHOWSELECTIONALWAYS) *new_style |= TVS_SHOWSELALWAYS;
if(!(nstcs & NSTCS_NOINFOTIP)) *new_style |= TVS_INFOTIP;
if(!(nstcs & NSTCS_EVENHEIGHT)) *new_style |= TVS_NONEVENHEIGHT;
if(nstcs & NSTCS_DISABLEDRAGDROP) *new_style |= TVS_DISABLEDRAGDROP;
if(!(nstcs & NSTCS_NOEDITLABELS)) *new_style |= TVS_EDITLABELS;
if(nstcs & NSTCS_CHECKBOXES) *new_style |= TVS_CHECKBOXES;
*new_style = (old_style & ~tv_mask) | (*new_style & tv_mask);
TRACE("old: %08x, new: %08x\n", old_style, *new_style);
return old_style^*new_style;
}
static IShellItem *shellitem_from_treeitem(NSTC2Impl *This, HTREEITEM hitem)
{
TVITEMEXW tvi;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;
tvi.hItem = hitem;
SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
TRACE("ShellItem: %p\n", (void*)tvi.lParam);
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;
UINT flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), flags | extra_flags);
return sfi.iIcon;
}
/* 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;
}
/* 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
*/
static LRESULT create_namespacetree(HWND hWnd, CREATESTRUCTW *crs)
{
NSTC2Impl *This = crs->lpCreateParams;
HIMAGELIST ShellSmallIconList;
DWORD treeview_style, treeview_ex_style;
TRACE("%p (%p)\n", This, crs);
SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LPARAM)This);
This->hwnd_main = hWnd;
treeview_style_from_nstcs(This, This->style, 0xFFFFFFFF, &treeview_style);
This->hwnd_tv = CreateWindowExW(0, WC_TREEVIEWW, NULL, treeview_style,
0, 0, crs->cx, crs->cy,
hWnd, NULL, explorerframe_hinstance, NULL);
if(!This->hwnd_tv)
{
ERR("Failed to create treeview!\n");
return HRESULT_FROM_WIN32(GetLastError());
}
treeview_ex_style = TVS_EX_DRAWIMAGEASYNC | TVS_EX_RICHTOOLTIP |
TVS_EX_DOUBLEBUFFER | TVS_EX_NOSINGLECOLLAPSE;
if(This->style & NSTCS_AUTOHSCROLL)
treeview_ex_style |= TVS_EX_AUTOHSCROLL;
if(This->style & NSTCS_FADEINOUTEXPANDOS)
treeview_ex_style |= TVS_EX_FADEINOUTEXPANDOS;
if(This->style & NSTCS_PARTIALCHECKBOXES)
treeview_ex_style |= TVS_EX_PARTIALCHECKBOXES;
if(This->style & NSTCS_EXCLUSIONCHECKBOXES)
treeview_ex_style |= TVS_EX_EXCLUSIONCHECKBOXES;
if(This->style & NSTCS_DIMMEDCHECKBOXES)
treeview_ex_style |= TVS_EX_DIMMEDCHECKBOXES;
SendMessageW(This->hwnd_tv, TVM_SETEXTENDEDSTYLE, treeview_ex_style, 0xffff);
if(Shell_GetImageLists(NULL, &ShellSmallIconList))
{
SendMessageW(This->hwnd_tv, TVM_SETIMAGELIST,
(WPARAM)TVSIL_NORMAL, (LPARAM)ShellSmallIconList);
}
else
{
ERR("Failed to get the System Image List.\n");
}
INameSpaceTreeControl_AddRef((INameSpaceTreeControl*)This);
return TRUE;
}
static LRESULT resize_namespacetree(NSTC2Impl *This)
{
RECT rc;
TRACE("%p\n", This);
GetClientRect(This->hwnd_main, &rc);
MoveWindow(This->hwnd_tv, 0, 0, rc.right-rc.left, rc.bottom-rc.top, TRUE);
return TRUE;
}
static LRESULT destroy_namespacetree(NSTC2Impl *This)
{
TRACE("%p\n", This);
INameSpaceTreeControl_RemoveAllRoots((INameSpaceTreeControl*)This);
/* This reference was added in create_namespacetree */
INameSpaceTreeControl_Release((INameSpaceTreeControl*)This);
return TRUE;
}
static LRESULT on_tvn_deleteitemw(NSTC2Impl *This, LPARAM lParam)
{
NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
TRACE("%p\n", This);
IShellItem_Release((IShellItem*)nmtv->itemOld.lParam);
return TRUE;
}
static LRESULT on_tvn_getdispinfow(NSTC2Impl *This, LPARAM lParam)
{
NMTVDISPINFOW *dispinfo = (NMTVDISPINFOW*)lParam;
TVITEMEXW *item = (TVITEMEXW*)&dispinfo->item;
IShellItem *psi = shellitem_from_treeitem(This, item->hItem);
HRESULT hr;
TRACE("%p, %p (mask: %x)\n", This, dispinfo, item->mask);
if(item->mask & TVIF_CHILDREN)
{
SFGAOF sfgao;
hr = IShellItem_GetAttributes(psi, SFGAO_HASSUBFOLDER, &sfgao);
if(SUCCEEDED(hr))
item->cChildren = (sfgao & SFGAO_HASSUBFOLDER)?1:0;
else
item->cChildren = 1;
item->mask |= TVIF_DI_SETITEM;
}
if(item->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE))
{
LPITEMIDLIST pidl;
hr = events_OnGetDefaultIconIndex(This, psi, &item->iImage, &item->iSelectedImage);
if(FAILED(hr))
{
hr = SHGetIDListFromObject((IUnknown*)psi, &pidl);
if(SUCCEEDED(hr))
{
item->iImage = item->iSelectedImage = get_icon(pidl, 0);
item->mask |= TVIF_DI_SETITEM;
ILFree(pidl);
}
else
ERR("Failed to get IDList (%08x).\n", hr);
}
}
if(item->mask & TVIF_TEXT)
{
LPWSTR display_name;
hr = IShellItem_GetDisplayName(psi, SIGDN_NORMALDISPLAY, &display_name);
if(SUCCEEDED(hr))
{
lstrcpynW(item->pszText, display_name, MAX_PATH);
item->mask |= TVIF_DI_SETITEM;
CoTaskMemFree(display_name);
}
else
ERR("Failed to get display name (%08x).\n", hr);
}
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 on_tvn_selchangedw(NSTC2Impl *This, LPARAM lParam)
{
NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
IShellItemArray *psia;
IShellItem *psi;
HRESULT hr;
TRACE("%p\n", This);
/* Note: Only supports one selected item. */
psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
hr = SHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, (void**)&psia);
if(SUCCEEDED(hr))
{
events_OnSelectionChanged(This, psia);
IShellItemArray_Release(psia);
}
return TRUE;
}
static LRESULT CALLBACK NSTC2_WndProc(HWND hWnd, UINT uMessage,
WPARAM wParam, LPARAM lParam)
{
NSTC2Impl *This = (NSTC2Impl*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
NMHDR *nmhdr;
switch(uMessage)
{
case WM_NCCREATE: return create_namespacetree(hWnd, (CREATESTRUCTW*)lParam);
case WM_SIZE: return resize_namespacetree(This);
case WM_DESTROY: return destroy_namespacetree(This);
case WM_NOTIFY:
nmhdr = (NMHDR*)lParam;
switch(nmhdr->code)
{
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);
case TVN_SELCHANGEDW: return on_tvn_selchangedw(This, lParam);
default: break;
}
break;
default: return DefWindowProcW(hWnd, uMessage, wParam, lParam);
}
return 0;
}
/**************************************************************************
* INameSpaceTreeControl2 Implementation
*/
static HRESULT WINAPI NSTC2_fnQueryInterface(INameSpaceTreeControl2* iface,
REFIID riid,
void **ppvObject)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
*ppvObject = NULL;
if(IsEqualIID(riid, &IID_INameSpaceTreeControl2) ||
IsEqualIID(riid, &IID_INameSpaceTreeControl) ||
IsEqualIID(riid, &IID_IUnknown))
{
*ppvObject = This;
}
else if(IsEqualIID(riid, &IID_IOleWindow))
{
*ppvObject = &This->lpowVtbl;
}
if(*ppvObject)
{
IUnknown_AddRef((IUnknown*)*ppvObject);
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI NSTC2_fnAddRef(INameSpaceTreeControl2* iface)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
LONG ref = InterlockedIncrement(&This->ref);
TRACE("%p - ref %d\n", This, ref);
return ref;
}
static ULONG WINAPI NSTC2_fnRelease(INameSpaceTreeControl2* iface)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
LONG ref = InterlockedDecrement(&This->ref);
TRACE("%p - ref: %d\n", This, ref);
if(!ref)
{
TRACE("Freeing.\n");
HeapFree(GetProcessHeap(), 0, This);
EFRAME_UnlockModule();
return 0;
}
return ref;
}
static HRESULT WINAPI NSTC2_fnInitialize(INameSpaceTreeControl2* iface,
HWND hwndParent,
RECT *prc,
NSTCSTYLE nstcsFlags)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
WNDCLASSW wc;
DWORD window_style, window_ex_style;
RECT rc;
static const WCHAR NSTC2_CLASS_NAME[] =
{'N','a','m','e','s','p','a','c','e','T','r','e','e',
'C','o','n','t','r','o','l',0};
TRACE("%p (%p, %p, %x)\n", This, hwndParent, prc, nstcsFlags);
if(nstcsFlags & unsupported_styles)
FIXME("0x%08x contains the unsupported style(s) 0x%08x\n",
nstcsFlags, nstcsFlags & unsupported_styles);
This->style = nstcsFlags;
if(!GetClassInfoW(explorerframe_hinstance, NSTC2_CLASS_NAME, &wc))
{
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = NSTC2_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = explorerframe_hinstance;
wc.hIcon = 0;
wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = NSTC2_CLASS_NAME;
if (!RegisterClassW(&wc)) return E_FAIL;
}
/* NSTCS_TABSTOP and NSTCS_BORDER affects the host window */
window_style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
(nstcsFlags & NSTCS_BORDER ? WS_BORDER : 0);
window_ex_style = nstcsFlags & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
if(prc)
CopyRect(&rc, prc);
else
rc.left = rc.right = rc.top = rc.bottom = 0;
This->hwnd_main = CreateWindowExW(window_ex_style, NSTC2_CLASS_NAME, NULL, window_style,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
hwndParent, 0, explorerframe_hinstance, This);
if(!This->hwnd_main)
{
ERR("Failed to create the window.\n");
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
static HRESULT WINAPI NSTC2_fnTreeAdvise(INameSpaceTreeControl2* iface,
IUnknown *punk,
DWORD *pdwCookie)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
HRESULT hr;
TRACE("%p (%p, %p)\n", This, punk, pdwCookie);
*pdwCookie = 0;
/* Only one client supported */
if(This->pnstce)
return E_FAIL;
hr = IUnknown_QueryInterface(punk, &IID_INameSpaceTreeControlEvents,(void**)&This->pnstce);
if(SUCCEEDED(hr))
{
*pdwCookie = 1;
return hr;
}
return E_FAIL;
}
static HRESULT WINAPI NSTC2_fnTreeUnadvise(INameSpaceTreeControl2* iface,
DWORD dwCookie)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
TRACE("%p (%x)\n", This, dwCookie);
/* The cookie is ignored. */
if(This->pnstce)
{
INameSpaceTreeControlEvents_Release(This->pnstce);
This->pnstce = NULL;
}
return S_OK;
}
static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface,
int iIndex,
IShellItem *psiRoot,
SHCONTF grfEnumFlags,
NSTCROOTSTYLE grfRootStyle,
IShellItemFilter *pif)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
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,
IShellItem *psiRoot,
SHCONTF grfEnumFlags,
NSTCROOTSTYLE grfRootStyle,
IShellItemFilter *pif)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
UINT root_count;
TRACE("%p, %p, %x, %x, %p\n",
This, psiRoot, grfEnumFlags, grfRootStyle, pif);
root_count = list_count(&This->roots);
return NSTC2_fnInsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif);
}
static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface,
IShellItem *psiRoot)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
nstc_root *cursor, *root = NULL;
TRACE("%p (%p)\n", This, psiRoot);
if(!psiRoot)
return E_NOINTERFACE;
LIST_FOR_EACH_ENTRY(cursor, &This->roots, nstc_root, entry)
{
HRESULT hr;
int order;
hr = IShellItem_Compare(psiRoot, cursor->psi, SICHINT_DISPLAY, &order);
if(hr == S_OK)
{
root = cursor;
break;
}
}
TRACE("root %p\n", root);
if(root)
{
events_OnItemDeleted(This, root->psi, TRUE);
SendMessageW(This->hwnd_tv, TVM_DELETEITEM, 0, (LPARAM)root->htreeitem);
list_remove(&root->entry);
HeapFree(GetProcessHeap(), 0, root);
return S_OK;
}
else
{
WARN("No matching root found.\n");
return E_FAIL;
}
}
static HRESULT WINAPI NSTC2_fnRemoveAllRoots(INameSpaceTreeControl2* iface)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
nstc_root *cur1, *cur2;
UINT removed = 0;
TRACE("%p\n", This);
LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->roots, nstc_root, entry)
{
NSTC2_fnRemoveRoot(iface, cur1->psi);
removed++;
}
if(removed)
return S_OK;
else
return E_INVALIDARG;
}
static HRESULT WINAPI NSTC2_fnGetRootItems(INameSpaceTreeControl2* iface,
IShellItemArray **ppsiaRootItems)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
IShellFolder *psf;
LPITEMIDLIST *array;
nstc_root *root;
UINT count, i;
HRESULT hr;
TRACE("%p (%p)\n", This, ppsiaRootItems);
count = list_count(&This->roots);
if(!count)
return E_INVALIDARG;
array = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST*)*count);
i = 0;
LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
SHGetIDListFromObject((IUnknown*)root->psi, &array[i++]);
SHGetDesktopFolder(&psf);
hr = SHCreateShellItemArray(NULL, psf, count, (PCUITEMID_CHILD_ARRAY)array,
ppsiaRootItems);
IShellFolder_Release(psf);
for(i = 0; i < count; i++)
ILFree(array[i]);
HeapFree(GetProcessHeap(), 0, array);
return hr;
}
static HRESULT WINAPI NSTC2_fnSetItemState(INameSpaceTreeControl2* iface,
IShellItem *psi,
NSTCITEMSTATE nstcisMask,
NSTCITEMSTATE nstcisFlags)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %x, %x)\n", This, psi, nstcisMask, nstcisFlags);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnGetItemState(INameSpaceTreeControl2* iface,
IShellItem *psi,
NSTCITEMSTATE nstcisMask,
NSTCITEMSTATE *pnstcisFlags)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %x, %p)\n", This, psi, nstcisMask, pnstcisFlags);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnGetSelectedItems(INameSpaceTreeControl2* iface,
IShellItemArray **psiaItems)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p)\n", This, psiaItems);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnGetItemCustomState(INameSpaceTreeControl2* iface,
IShellItem *psi,
int *piStateNumber)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %p)\n", This, psi, piStateNumber);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnSetItemCustomState(INameSpaceTreeControl2* iface,
IShellItem *psi,
int iStateNumber)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %d)\n", This, psi, iStateNumber);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnEnsureItemVisible(INameSpaceTreeControl2* iface,
IShellItem *psi)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p)\n", This, psi);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnSetTheme(INameSpaceTreeControl2* iface,
LPCWSTR pszTheme)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p)\n", This, pszTheme);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnGetNextItem(INameSpaceTreeControl2* iface,
IShellItem *psi,
NSTCGNI nstcgi,
IShellItem **ppsiNext)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %x, %p)\n", This, psi, nstcgi, ppsiNext);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnHitTest(INameSpaceTreeControl2* iface,
POINT *ppt,
IShellItem **ppsiOut)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %p)\n", This, ppsiOut, ppt);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnGetItemRect(INameSpaceTreeControl2* iface,
IShellItem *psi,
RECT *prect)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p (%p, %p)\n", This, psi, prect);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnCollapseAll(INameSpaceTreeControl2* iface)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
FIXME("stub, %p\n", This);
return E_NOTIMPL;
}
static HRESULT WINAPI NSTC2_fnSetControlStyle(INameSpaceTreeControl2* iface,
NSTCSTYLE nstcsMask,
NSTCSTYLE nstcsStyle)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
static const DWORD tv_style_flags =
NSTCS_HASEXPANDOS | NSTCS_HASLINES | NSTCS_FULLROWSELECT |
NSTCS_HORIZONTALSCROLL | NSTCS_ROOTHASEXPANDO |
NSTCS_SHOWSELECTIONALWAYS | NSTCS_NOINFOTIP | NSTCS_EVENHEIGHT |
NSTCS_DISABLEDRAGDROP | NSTCS_NOEDITLABELS | NSTCS_CHECKBOXES;
static const DWORD host_style_flags = NSTCS_TABSTOP | NSTCS_BORDER;
static const DWORD nstc_flags =
NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM |
NSTCS_FAVORITESMODE | NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS |
NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON | NSTCS_SHOWREFRESHBUTTON;
TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
/* Fail if there is an attempt to set an unknown style. */
if(nstcsMask & ~(tv_style_flags | host_style_flags | nstc_flags))
return E_FAIL;
if(nstcsMask & tv_style_flags)
{
DWORD new_style;
treeview_style_from_nstcs(This, nstcsStyle, nstcsMask, &new_style);
SetWindowLongPtrW(This->hwnd_tv, GWL_STYLE, new_style);
}
/* Flags affecting the host window */
if(nstcsMask & NSTCS_BORDER)
{
DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_STYLE);
new_style &= ~WS_BORDER;
new_style |= nstcsStyle & NSTCS_BORDER ? WS_BORDER : 0;
SetWindowLongPtrW(This->hwnd_main, GWL_STYLE, new_style);
}
if(nstcsMask & NSTCS_TABSTOP)
{
DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE);
new_style &= ~WS_EX_CONTROLPARENT;
new_style |= nstcsStyle & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
SetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE, new_style);
}
if((nstcsStyle & nstcsMask) & unsupported_styles)
FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
(nstcsStyle & nstcsMask),
(nstcsStyle & nstcsMask) & unsupported_styles);
This->style &= ~nstcsMask;
This->style |= (nstcsStyle & nstcsMask);
return S_OK;
}
static HRESULT WINAPI NSTC2_fnGetControlStyle(INameSpaceTreeControl2* iface,
NSTCSTYLE nstcsMask,
NSTCSTYLE *pnstcsStyle)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
*pnstcsStyle = (This->style & nstcsMask);
return S_OK;
}
static HRESULT WINAPI NSTC2_fnSetControlStyle2(INameSpaceTreeControl2* iface,
NSTCSTYLE2 nstcsMask,
NSTCSTYLE2 nstcsStyle)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
if((nstcsStyle & nstcsMask) & unsupported_styles2)
FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
(nstcsStyle & nstcsMask),
(nstcsStyle & nstcsMask) & unsupported_styles2);
This->style2 &= ~nstcsMask;
This->style2 |= (nstcsStyle & nstcsMask);
return S_OK;
}
static HRESULT WINAPI NSTC2_fnGetControlStyle2(INameSpaceTreeControl2* iface,
NSTCSTYLE2 nstcsMask,
NSTCSTYLE2 *pnstcsStyle)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
*pnstcsStyle = (This->style2 & nstcsMask);
return S_OK;
}
static const INameSpaceTreeControl2Vtbl vt_INameSpaceTreeControl2 = {
NSTC2_fnQueryInterface,
NSTC2_fnAddRef,
NSTC2_fnRelease,
NSTC2_fnInitialize,
NSTC2_fnTreeAdvise,
NSTC2_fnTreeUnadvise,
NSTC2_fnAppendRoot,
NSTC2_fnInsertRoot,
NSTC2_fnRemoveRoot,
NSTC2_fnRemoveAllRoots,
NSTC2_fnGetRootItems,
NSTC2_fnSetItemState,
NSTC2_fnGetItemState,
NSTC2_fnGetSelectedItems,
NSTC2_fnGetItemCustomState,
NSTC2_fnSetItemCustomState,
NSTC2_fnEnsureItemVisible,
NSTC2_fnSetTheme,
NSTC2_fnGetNextItem,
NSTC2_fnHitTest,
NSTC2_fnGetItemRect,
NSTC2_fnCollapseAll,
NSTC2_fnSetControlStyle,
NSTC2_fnGetControlStyle,
NSTC2_fnSetControlStyle2,
NSTC2_fnGetControlStyle2
};
/**************************************************************************
* IOleWindow Implementation
*/
static inline NSTC2Impl *impl_from_IOleWindow(IOleWindow *iface)
{
return (NSTC2Impl *)((char*)iface - FIELD_OFFSET(NSTC2Impl, lpowVtbl));
}
static HRESULT WINAPI IOW_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
{
NSTC2Impl *This = impl_from_IOleWindow(iface);
TRACE("%p\n", This);
return NSTC2_fnQueryInterface((INameSpaceTreeControl2*)This, riid, ppvObject);
}
static ULONG WINAPI IOW_fnAddRef(IOleWindow *iface)
{
NSTC2Impl *This = impl_from_IOleWindow(iface);
TRACE("%p\n", This);
return NSTC2_fnAddRef((INameSpaceTreeControl2*)This);
}
static ULONG WINAPI IOW_fnRelease(IOleWindow *iface)
{
NSTC2Impl *This = impl_from_IOleWindow(iface);
TRACE("%p\n", This);
return NSTC2_fnRelease((INameSpaceTreeControl2*)This);
}
static HRESULT WINAPI IOW_fnGetWindow(IOleWindow *iface, HWND *phwnd)
{
NSTC2Impl *This = impl_from_IOleWindow(iface);
TRACE("%p (%p)\n", This, phwnd);
*phwnd = This->hwnd_main;
return S_OK;
}
static HRESULT WINAPI IOW_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMode)
{
NSTC2Impl *This = impl_from_IOleWindow(iface);
TRACE("%p (%d)\n", This, fEnterMode);
/* Not implemented */
return E_NOTIMPL;
}
static const IOleWindowVtbl vt_IOleWindow = {
IOW_fnQueryInterface,
IOW_fnAddRef,
IOW_fnRelease,
IOW_fnGetWindow,
IOW_fnContextSensitiveHelp
};
HRESULT NamespaceTreeControl_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
NSTC2Impl *nstc;
HRESULT ret;
TRACE ("%p %s %p\n", pUnkOuter, debugstr_guid(riid), ppv);
if(!ppv)
return E_POINTER;
if(pUnkOuter)
return CLASS_E_NOAGGREGATION;
EFRAME_LockModule();
nstc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NSTC2Impl));
nstc->ref = 1;
nstc->lpVtbl = &vt_INameSpaceTreeControl2;
nstc->lpowVtbl = &vt_IOleWindow;
list_init(&nstc->roots);
ret = INameSpaceTreeControl_QueryInterface((INameSpaceTreeControl*)nstc, riid, ppv);
INameSpaceTreeControl_Release((INameSpaceTreeControl*)nstc);
TRACE("--(%p)\n", ppv);
return ret;
}