| /* | |
| * 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 | |
| #include "winerror.h" | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "shellapi.h" | |
| #include "commctrl.h" | |
| #include "commoncontrols.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 { | |
| INameSpaceTreeControl2 INameSpaceTreeControl2_iface; | |
| IOleWindow IOleWindow_iface; | |
| LONG ref; | |
| HWND hwnd_main; | |
| HWND hwnd_tv; | |
| WNDPROC tv_oldwndproc; | |
| NSTCSTYLE style; | |
| NSTCSTYLE2 style2; | |
| struct list roots; | |
| INameSpaceTreeControlCustomDraw *customdraw; | |
| INameSpaceTreeControlDropHandler *dragdrop; | |
| INameSpaceTreeControlEvents *events; | |
| } 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; | |
| static const WCHAR thispropW[] = {'P','R','O','P','_','T','H','I','S',0}; | |
| static inline NSTC2Impl *impl_from_INameSpaceTreeControl2(INameSpaceTreeControl2 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, NSTC2Impl, INameSpaceTreeControl2_iface); | |
| } | |
| static inline NSTC2Impl *impl_from_IOleWindow(IOleWindow *iface) | |
| { | |
| return CONTAINING_RECORD(iface, NSTC2Impl, IOleWindow_iface); | |
| } | |
| /* 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->events) return E_NOTIMPL; | |
| refcount = IShellItem_AddRef(psi); | |
| ret = INameSpaceTreeControlEvents_OnGetDefaultIconIndex(This->events, 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->events) return S_OK; | |
| refcount = IShellItem_AddRef(psi); | |
| ret = INameSpaceTreeControlEvents_OnItemAdded(This->events, 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->events) return S_OK; | |
| refcount = IShellItem_AddRef(psi); | |
| ret = INameSpaceTreeControlEvents_OnItemDeleted(This->events, 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->events) return S_OK; | |
| refcount = IShellItem_AddRef(psi); | |
| ret = INameSpaceTreeControlEvents_OnBeforeExpand(This->events, 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->events) return S_OK; | |
| refcount = IShellItem_AddRef(psi); | |
| ret = INameSpaceTreeControlEvents_OnAfterExpand(This->events, 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->events) return S_OK; | |
| refcount = IShellItem_AddRef(psi); | |
| ret = INameSpaceTreeControlEvents_OnItemClick(This->events, 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->events) return S_OK; | |
| return INameSpaceTreeControlEvents_OnSelectionChanged(This->events, psia); | |
| } | |
| static HRESULT events_OnKeyboardInput(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| if(!This->events) return S_OK; | |
| return INameSpaceTreeControlEvents_OnKeyboardInput(This->events, 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; | |
| IImageList *list; | |
| list = (IImageList *)SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), flags | extra_flags); | |
| if (list) IImageList_Release(list); | |
| 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); | |
| } | |
| static HTREEITEM treeitem_from_point(NSTC2Impl *This, const POINT *pt, UINT *hitflag) | |
| { | |
| TVHITTESTINFO tviht; | |
| tviht.pt.x = pt->x; | |
| tviht.pt.y = pt->y; | |
| tviht.hItem = NULL; | |
| SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tviht); | |
| if(hitflag) *hitflag = tviht.flags; | |
| return tviht.hItem; | |
| } | |
| /************************************************************************* | |
| * 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"); | |
| } | |
| INameSpaceTreeControl2_AddRef(&This->INameSpaceTreeControl2_iface); | |
| /* Subclass the treeview to get the keyboard events. */ | |
| This->tv_oldwndproc = (WNDPROC)SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC, | |
| (ULONG_PTR)tv_wndproc); | |
| if(This->tv_oldwndproc) | |
| SetPropW(This->hwnd_tv, thispropW, 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); | |
| RemovePropW(This->hwnd_tv, thispropW); | |
| } | |
| INameSpaceTreeControl2_RemoveAllRoots(&This->INameSpaceTreeControl2_iface); | |
| /* This reference was added in create_namespacetree */ | |
| INameSpaceTreeControl2_Release(&This->INameSpaceTreeControl2_iface); | |
| 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*)GetPropW(hWnd, thispropW); | |
| 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 = impl_from_INameSpaceTreeControl2(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->INameSpaceTreeControl2_iface; | |
| } | |
| else if(IsEqualIID(riid, &IID_IOleWindow)) | |
| { | |
| *ppvObject = &This->IOleWindow_iface; | |
| } | |
| if(*ppvObject) | |
| { | |
| IUnknown_AddRef((IUnknown*)*ppvObject); | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI NSTC2_fnAddRef(INameSpaceTreeControl2* iface) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface); | |
| LONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("%p - ref %d\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI NSTC2_fnRelease(INameSpaceTreeControl2* iface) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(iface); | |
| WNDCLASSW wc; | |
| DWORD window_style, window_ex_style; | |
| INITCOMMONCONTROLSEX icex; | |
| 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; | |
| icex.dwSize = sizeof( icex ); | |
| icex.dwICC = ICC_TREEVIEW_CLASSES; | |
| InitCommonControlsEx( &icex ); | |
| 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) | |
| rc = *prc; | |
| else | |
| SetRectEmpty(&rc); | |
| 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 *handler, DWORD *cookie) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface); | |
| TRACE("%p (%p, %p)\n", This, handler, cookie); | |
| *cookie = 0; | |
| /* Only one client supported */ | |
| if (This->events || This->customdraw || This->dragdrop) | |
| return E_FAIL; | |
| /* FIXME: request INameSpaceTreeAccessible too */ | |
| IUnknown_QueryInterface(handler, &IID_INameSpaceTreeControlEvents, (void**)&This->events); | |
| IUnknown_QueryInterface(handler, &IID_INameSpaceTreeControlCustomDraw, (void**)&This->customdraw); | |
| IUnknown_QueryInterface(handler, &IID_INameSpaceTreeControlDropHandler, (void**)&This->dragdrop); | |
| if (This->events || This->customdraw || This->dragdrop) | |
| *cookie = 1; | |
| return *cookie ? S_OK : E_FAIL; | |
| } | |
| static HRESULT WINAPI NSTC2_fnTreeUnadvise(INameSpaceTreeControl2* iface, DWORD cookie) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface); | |
| TRACE("%p (%x)\n", This, cookie); | |
| /* The cookie is ignored. */ | |
| if (This->events) | |
| { | |
| INameSpaceTreeControlEvents_Release(This->events); | |
| This->events = NULL; | |
| } | |
| if (This->customdraw) | |
| { | |
| INameSpaceTreeControlCustomDraw_Release(This->customdraw); | |
| This->customdraw = NULL; | |
| } | |
| if (This->dragdrop) | |
| { | |
| INameSpaceTreeControlDropHandler_Release(This->dragdrop); | |
| This->dragdrop = NULL; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface, | |
| int iIndex, | |
| IShellItem *psiRoot, | |
| SHCONTF grfEnumFlags, | |
| NSTCROOTSTYLE grfRootStyle, | |
| IShellItemFilter *pif) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface); | |
| nstc_root *new_root; | |
| struct list *add_after_entry; | |
| HTREEITEM add_after_hitem; | |
| int 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 = impl_from_INameSpaceTreeControl2(iface); | |
| UINT root_count; | |
| TRACE("%p, %p, %x, %x, %p\n", | |
| This, psiRoot, grfEnumFlags, grfRootStyle, pif); | |
| root_count = list_count(&This->roots); | |
| return INameSpaceTreeControl2_InsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif); | |
| } | |
| static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface, | |
| IShellItem *psiRoot) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(iface); | |
| nstc_root *cur1, *cur2; | |
| TRACE("%p\n", This); | |
| if (list_empty(&This->roots)) | |
| return E_INVALIDARG; | |
| LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->roots, nstc_root, entry) | |
| INameSpaceTreeControl2_RemoveRoot(iface, cur1->psi); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI NSTC2_fnGetRootItems(INameSpaceTreeControl2* iface, | |
| IShellItemArray **ppsiaRootItems) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(iface); | |
| IShellItem *psiselected; | |
| TRACE("%p (%p)\n", This, psiaItems); | |
| psiselected = get_selected_shellitem(This); | |
| if(!psiselected) | |
| { | |
| *psiaItems = NULL; | |
| return E_FAIL; | |
| } | |
| return SHCreateShellItemArrayFromShellItem(psiselected, &IID_IShellItemArray, | |
| (void**)psiaItems); | |
| } | |
| static HRESULT WINAPI NSTC2_fnGetItemCustomState(INameSpaceTreeControl2* iface, | |
| IShellItem *psi, | |
| int *piStateNumber) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(iface); | |
| FIXME("stub, %p (%p, %d)\n", This, psi, iStateNumber); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI NSTC2_fnEnsureItemVisible(INameSpaceTreeControl2* iface, | |
| IShellItem *psi) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface); | |
| HTREEITEM hitem; | |
| TRACE("%p (%p)\n", This, psi); | |
| hitem = treeitem_from_shellitem(This, psi); | |
| if(hitem) | |
| { | |
| SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (WPARAM)hitem); | |
| return S_OK; | |
| } | |
| return E_INVALIDARG; | |
| } | |
| static HRESULT WINAPI NSTC2_fnSetTheme(INameSpaceTreeControl2* iface, | |
| LPCWSTR pszTheme) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(iface); | |
| HTREEITEM hitem; | |
| TRACE("%p (%p, %p)\n", This, ppsiOut, ppt); | |
| if(!ppt || !ppsiOut) | |
| return E_POINTER; | |
| *ppsiOut = NULL; | |
| hitem = treeitem_from_point(This, ppt, NULL); | |
| if(hitem) | |
| *ppsiOut = shellitem_from_treeitem(This, hitem); | |
| if(*ppsiOut) | |
| { | |
| IShellItem_AddRef(*ppsiOut); | |
| return S_OK; | |
| } | |
| return S_FALSE; | |
| } | |
| static HRESULT WINAPI NSTC2_fnGetItemRect(INameSpaceTreeControl2* iface, | |
| IShellItem *psi, | |
| RECT *prect) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface); | |
| HTREEITEM hitem; | |
| TRACE("%p (%p, %p)\n", This, psi, prect); | |
| if(!psi || !prect) | |
| return E_POINTER; | |
| hitem = treeitem_from_shellitem(This, psi); | |
| if(hitem) | |
| { | |
| *(HTREEITEM*)prect = hitem; | |
| if(SendMessageW(This->hwnd_tv, TVM_GETITEMRECT, FALSE, (LPARAM)prect)) | |
| { | |
| MapWindowPoints(This->hwnd_tv, HWND_DESKTOP, (POINT*)prect, 2); | |
| return S_OK; | |
| } | |
| } | |
| return E_INVALIDARG; | |
| } | |
| static HRESULT WINAPI NSTC2_fnCollapseAll(INameSpaceTreeControl2* iface) | |
| { | |
| NSTC2Impl *This = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 = impl_from_INameSpaceTreeControl2(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 HRESULT WINAPI IOW_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject) | |
| { | |
| NSTC2Impl *This = impl_from_IOleWindow(iface); | |
| return INameSpaceTreeControl2_QueryInterface(&This->INameSpaceTreeControl2_iface, riid, ppvObject); | |
| } | |
| static ULONG WINAPI IOW_fnAddRef(IOleWindow *iface) | |
| { | |
| NSTC2Impl *This = impl_from_IOleWindow(iface); | |
| return INameSpaceTreeControl2_AddRef(&This->INameSpaceTreeControl2_iface); | |
| } | |
| static ULONG WINAPI IOW_fnRelease(IOleWindow *iface) | |
| { | |
| NSTC2Impl *This = impl_from_IOleWindow(iface); | |
| return INameSpaceTreeControl2_Release(&This->INameSpaceTreeControl2_iface); | |
| } | |
| 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)); | |
| if (!nstc) | |
| return E_OUTOFMEMORY; | |
| nstc->ref = 1; | |
| nstc->INameSpaceTreeControl2_iface.lpVtbl = &vt_INameSpaceTreeControl2; | |
| nstc->IOleWindow_iface.lpVtbl = &vt_IOleWindow; | |
| list_init(&nstc->roots); | |
| ret = INameSpaceTreeControl2_QueryInterface(&nstc->INameSpaceTreeControl2_iface, riid, ppv); | |
| INameSpaceTreeControl2_Release(&nstc->INameSpaceTreeControl2_iface); | |
| TRACE("--(%p)\n", ppv); | |
| return ret; | |
| } |