1546 lines
48 KiB
C
1546 lines
48 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;
|
|
|
|
WNDPROC tv_oldwndproc;
|
|
|
|
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;
|
|
|
|
/* Forward declarations */
|
|
static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
|
|
|
|
/*************************************************************************
|
|
* 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_OnItemClick(NSTC2Impl *This, IShellItem *psi,
|
|
NSTCEHITTEST nstceHitTest, NSTCECLICKTYPE nstceClickType)
|
|
{
|
|
HRESULT ret;
|
|
LONG refcount;
|
|
if(!This->pnstce) return S_OK;
|
|
|
|
refcount = IShellItem_AddRef(psi);
|
|
ret = INameSpaceTreeControlEvents_OnItemClick(This->pnstce, psi, nstceHitTest, nstceClickType);
|
|
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);
|
|
}
|
|
|
|
static HRESULT events_OnKeyboardInput(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if(!This->pnstce) return S_OK;
|
|
|
|
return INameSpaceTreeControlEvents_OnKeyboardInput(This->pnstce, uMsg, wParam, lParam);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* 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;
|
|
}
|
|
|
|
/* Find a shellitem in the tree, starting from the given node. */
|
|
static HTREEITEM search_for_shellitem(NSTC2Impl *This, HTREEITEM node,
|
|
IShellItem *psi)
|
|
{
|
|
IShellItem *psi_node;
|
|
HTREEITEM next, result = NULL;
|
|
HRESULT hr;
|
|
int cmpo;
|
|
TRACE("%p, %p, %p\n", This, node, psi);
|
|
|
|
/* Check this node */
|
|
psi_node = shellitem_from_treeitem(This, node);
|
|
hr = IShellItem_Compare(psi, psi_node, SICHINT_DISPLAY, &cmpo);
|
|
if(hr == S_OK)
|
|
return node;
|
|
|
|
/* Any children? */
|
|
next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
|
|
TVGN_CHILD, (LPARAM)node);
|
|
if(next)
|
|
{
|
|
result = search_for_shellitem(This, next, psi);
|
|
if(result) return result;
|
|
}
|
|
|
|
/* Try our next sibling. */
|
|
next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
|
|
TVGN_NEXT, (LPARAM)node);
|
|
if(next)
|
|
result = search_for_shellitem(This, next, psi);
|
|
|
|
return result;
|
|
}
|
|
|
|
static HTREEITEM treeitem_from_shellitem(NSTC2Impl *This, IShellItem *psi)
|
|
{
|
|
HTREEITEM root;
|
|
TRACE("%p, %p\n", This, psi);
|
|
|
|
root = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
|
|
TVGN_ROOT, 0);
|
|
if(!root)
|
|
return NULL;
|
|
|
|
return search_for_shellitem(This, root, psi);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static HTREEITEM get_selected_treeitem(NSTC2Impl *This)
|
|
{
|
|
return (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CARET, 0);
|
|
}
|
|
|
|
static IShellItem *get_selected_shellitem(NSTC2Impl *This)
|
|
{
|
|
return shellitem_from_treeitem(This, get_selected_treeitem(This));
|
|
}
|
|
|
|
static void collapse_all(NSTC2Impl *This, HTREEITEM node)
|
|
{
|
|
HTREEITEM next;
|
|
|
|
/* Collapse this node first, and then first child/next sibling. */
|
|
SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)node);
|
|
|
|
next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
|
|
if(next) collapse_all(This, next);
|
|
|
|
next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)node);
|
|
if(next) collapse_all(This, next);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* 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);
|
|
|
|
/* Subclass the treeview to get the keybord events. */
|
|
This->tv_oldwndproc = (WNDPROC)SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC,
|
|
(ULONG_PTR)tv_wndproc);
|
|
if(This->tv_oldwndproc)
|
|
SetPropA(This->hwnd_tv, "PROP_THIS", 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);
|
|
|
|
/* Undo the subclassing */
|
|
if(This->tv_oldwndproc)
|
|
{
|
|
SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC, (ULONG_PTR)This->tv_oldwndproc);
|
|
RemovePropA(This->hwnd_tv, "PROP_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 on_nm_click(NSTC2Impl *This, NMHDR *nmhdr)
|
|
{
|
|
TVHITTESTINFO tvhit;
|
|
IShellItem *psi;
|
|
HRESULT hr;
|
|
TRACE("%p (%p)\n", This, nmhdr);
|
|
|
|
GetCursorPos(&tvhit.pt);
|
|
ScreenToClient(This->hwnd_tv, &tvhit.pt);
|
|
SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
|
|
|
|
if(tvhit.flags & (TVHT_NOWHERE|TVHT_ABOVE|TVHT_BELOW))
|
|
return TRUE;
|
|
|
|
/* TVHT_ maps onto the corresponding NSTCEHT_ */
|
|
psi = shellitem_from_treeitem(This, tvhit.hItem);
|
|
hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_LBUTTON);
|
|
|
|
/* The expando should not be expanded unless
|
|
* double-clicked. */
|
|
if(tvhit.flags == TVHT_ONITEMBUTTON)
|
|
return TRUE;
|
|
|
|
if(SUCCEEDED(hr))
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
static LRESULT on_wm_mbuttonup(NSTC2Impl *This, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
TVHITTESTINFO tvhit;
|
|
IShellItem *psi;
|
|
HRESULT hr;
|
|
TRACE("%p (%lx, %lx)\n", This, wParam, lParam);
|
|
|
|
tvhit.pt.x = (int)(short)LOWORD(lParam);
|
|
tvhit.pt.y = (int)(short)HIWORD(lParam);
|
|
|
|
SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
|
|
|
|
/* Seems to generate only ONITEM and ONITEMICON */
|
|
if( !(tvhit.flags & (TVHT_ONITEM|TVHT_ONITEMICON)) )
|
|
return FALSE;
|
|
|
|
psi = shellitem_from_treeitem(This, tvhit.hItem);
|
|
hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_MBUTTON);
|
|
|
|
if(SUCCEEDED(hr))
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
static LRESULT on_kbd_event(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
IShellItem *psi;
|
|
HTREEITEM hitem;
|
|
TRACE("%p : %d, %lx, %lx\n", This, uMsg, wParam, lParam);
|
|
|
|
/* Handled by the client? */
|
|
if(FAILED(events_OnKeyboardInput(This, uMsg, wParam, lParam)))
|
|
return TRUE;
|
|
|
|
if(uMsg == WM_KEYDOWN)
|
|
{
|
|
switch(wParam)
|
|
{
|
|
case VK_DELETE:
|
|
psi = get_selected_shellitem(This);
|
|
FIXME("Deletion of file requested (shellitem: %p).\n", psi);
|
|
return TRUE;
|
|
|
|
case VK_F2:
|
|
hitem = get_selected_treeitem(This);
|
|
SendMessageW(This->hwnd_tv, TVM_EDITLABELW, 0, (LPARAM)hitem);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* Let the TreeView handle the key */
|
|
return FALSE;
|
|
}
|
|
|
|
static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
NSTC2Impl *This = (NSTC2Impl*)GetPropA(hWnd, "PROP_THIS");
|
|
|
|
switch(uMessage) {
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
case WM_CHAR:
|
|
case WM_SYSKEYDOWN:
|
|
case WM_SYSKEYUP:
|
|
case WM_SYSCHAR:
|
|
if(on_kbd_event(This, uMessage, wParam, lParam))
|
|
return TRUE;
|
|
break;
|
|
|
|
case WM_MBUTTONUP: return on_wm_mbuttonup(This, wParam, lParam);
|
|
}
|
|
|
|
/* Pass the message on to the treeview */
|
|
return CallWindowProcW(This->tv_oldwndproc, hWnd, uMessage, wParam, lParam);
|
|
}
|
|
|
|
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 NM_CLICK: return on_nm_click(This, nmhdr);
|
|
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;
|
|
TVITEMEXW tvi;
|
|
HTREEITEM hitem;
|
|
|
|
TRACE("%p (%p, %x, %x)\n", This, psi, nstcisMask, nstcisFlags);
|
|
|
|
hitem = treeitem_from_shellitem(This, psi);
|
|
if(!hitem) return E_INVALIDARG;
|
|
|
|
/* Passing both NSTCIS_SELECTED and NSTCIS_SELECTEDNOEXPAND results
|
|
in two TVM_SETITEMW's */
|
|
if((nstcisMask&nstcisFlags) & NSTCIS_SELECTED)
|
|
{
|
|
SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hitem);
|
|
SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (LPARAM)hitem);
|
|
}
|
|
if((nstcisMask&nstcisFlags) & NSTCIS_SELECTEDNOEXPAND)
|
|
{
|
|
SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET|TVSI_NOSINGLEEXPAND, (LPARAM)hitem);
|
|
}
|
|
|
|
/* If NSTCIS_EXPANDED is among the flags, the mask is ignored. */
|
|
if((nstcisMask|nstcisFlags) & NSTCIS_EXPANDED)
|
|
{
|
|
WPARAM arg = nstcisFlags&NSTCIS_EXPANDED ? TVE_EXPAND:TVE_COLLAPSE;
|
|
SendMessageW(This->hwnd_tv, TVM_EXPAND, arg, (LPARAM)hitem);
|
|
}
|
|
|
|
if(nstcisMask & NSTCIS_DISABLED)
|
|
tvi.mask = TVIF_STATE | TVIF_STATEEX;
|
|
else if( ((nstcisMask^nstcisFlags) & (NSTCIS_SELECTED|NSTCIS_EXPANDED|NSTCIS_SELECTEDNOEXPAND)) ||
|
|
((nstcisMask|nstcisFlags) & NSTCIS_BOLD) ||
|
|
(nstcisFlags & NSTCIS_DISABLED) )
|
|
tvi.mask = TVIF_STATE;
|
|
else
|
|
tvi.mask = 0;
|
|
|
|
if(tvi.mask)
|
|
{
|
|
tvi.stateMask = tvi.state = 0;
|
|
tvi.stateMask |= ((nstcisFlags^nstcisMask)&NSTCIS_SELECTED) ? TVIS_SELECTED : 0;
|
|
tvi.stateMask |= (nstcisMask|nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
|
|
tvi.state |= (nstcisMask&nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
|
|
|
|
if((nstcisMask&NSTCIS_EXPANDED)^(nstcisFlags&NSTCIS_EXPANDED))
|
|
{
|
|
tvi.stateMask = 0;
|
|
}
|
|
|
|
tvi.uStateEx = (nstcisFlags&nstcisMask)&NSTCIS_DISABLED?TVIS_EX_DISABLED:0;
|
|
tvi.hItem = hitem;
|
|
|
|
SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI NSTC2_fnGetItemState(INameSpaceTreeControl2* iface,
|
|
IShellItem *psi,
|
|
NSTCITEMSTATE nstcisMask,
|
|
NSTCITEMSTATE *pnstcisFlags)
|
|
{
|
|
NSTC2Impl *This = (NSTC2Impl*)iface;
|
|
HTREEITEM hitem;
|
|
TVITEMEXW tvi;
|
|
TRACE("%p (%p, %x, %p)\n", This, psi, nstcisMask, pnstcisFlags);
|
|
|
|
hitem = treeitem_from_shellitem(This, psi);
|
|
if(!hitem)
|
|
return E_INVALIDARG;
|
|
|
|
*pnstcisFlags = 0;
|
|
|
|
tvi.hItem = hitem;
|
|
tvi.mask = TVIF_STATE;
|
|
tvi.stateMask = TVIS_SELECTED|TVIS_EXPANDED|TVIS_BOLD;
|
|
|
|
if(nstcisMask & NSTCIS_DISABLED)
|
|
tvi.mask |= TVIF_STATEEX;
|
|
|
|
SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
|
|
*pnstcisFlags |= (tvi.state & TVIS_SELECTED)?NSTCIS_SELECTED:0;
|
|
*pnstcisFlags |= (tvi.state & TVIS_EXPANDED)?NSTCIS_EXPANDED:0;
|
|
*pnstcisFlags |= (tvi.state & TVIS_BOLD)?NSTCIS_BOLD:0;
|
|
*pnstcisFlags |= (tvi.uStateEx & TVIS_EX_DISABLED)?NSTCIS_DISABLED:0;
|
|
|
|
*pnstcisFlags &= nstcisMask;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI NSTC2_fnGetSelectedItems(INameSpaceTreeControl2* iface,
|
|
IShellItemArray **psiaItems)
|
|
{
|
|
NSTC2Impl *This = (NSTC2Impl*)iface;
|
|
IShellItem *psiselected;
|
|
HRESULT hr;
|
|
TRACE("%p (%p)\n", This, psiaItems);
|
|
|
|
psiselected = get_selected_shellitem(This);
|
|
if(!psiselected)
|
|
return E_FAIL;
|
|
|
|
hr = SHCreateShellItemArrayFromShellItem(psiselected, &IID_IShellItemArray,
|
|
(void**)psiaItems);
|
|
return hr;
|
|
}
|
|
|
|
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;
|
|
HTREEITEM hitem, hnext;
|
|
UINT tvgn;
|
|
TRACE("%p (%p, %x, %p)\n", This, psi, nstcgi, ppsiNext);
|
|
|
|
if(!ppsiNext) return E_POINTER;
|
|
if(!psi) return E_FAIL;
|
|
|
|
*ppsiNext = NULL;
|
|
|
|
hitem = treeitem_from_shellitem(This, psi);
|
|
if(!hitem)
|
|
return E_INVALIDARG;
|
|
|
|
switch(nstcgi)
|
|
{
|
|
case NSTCGNI_NEXT: tvgn = TVGN_NEXT; break;
|
|
case NSTCGNI_NEXTVISIBLE: tvgn = TVGN_NEXTVISIBLE; break;
|
|
case NSTCGNI_PREV: tvgn = TVGN_PREVIOUS; break;
|
|
case NSTCGNI_PREVVISIBLE: tvgn = TVGN_PREVIOUSVISIBLE; break;
|
|
case NSTCGNI_PARENT: tvgn = TVGN_PARENT; break;
|
|
case NSTCGNI_CHILD: tvgn = TVGN_CHILD; break;
|
|
case NSTCGNI_FIRSTVISIBLE: tvgn = TVGN_FIRSTVISIBLE; break;
|
|
case NSTCGNI_LASTVISIBLE: tvgn = TVGN_LASTVISIBLE; break;
|
|
default:
|
|
FIXME("Unknown nstcgi value %d\n", nstcgi);
|
|
return E_FAIL;
|
|
}
|
|
|
|
hnext = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, tvgn, (WPARAM)hitem);
|
|
if(hnext)
|
|
{
|
|
*ppsiNext = shellitem_from_treeitem(This, hnext);
|
|
IShellItem_AddRef(*ppsiNext);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
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;
|
|
nstc_root *root;
|
|
TRACE("%p\n", This);
|
|
|
|
LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
|
|
collapse_all(This, root->htreeitem);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
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;
|
|
}
|