gdiplus/tests: Add tests for GdipMeasureString with StringFormatFlagsNoWrap.
[wine.git] / dlls / explorerframe / nstc.c
blob0abaded85a6432af1308db547cc0539496286410
1 /*
2 * NamespaceTreeControl implementation.
4 * Copyright 2010 David Hedberg
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
24 #define NONAMELESSUNION
26 #include "winerror.h"
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "shellapi.h"
31 #include "commctrl.h"
32 #include "commoncontrols.h"
34 #include "wine/list.h"
35 #include "wine/debug.h"
36 #include "wine/heap.h"
38 #include "explorerframe_main.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(nstc);
42 typedef struct nstc_root {
43 IShellItem *psi;
44 HTREEITEM htreeitem;
45 SHCONTF enum_flags;
46 NSTCROOTSTYLE root_style;
47 IShellItemFilter *pif;
48 struct list entry;
49 } nstc_root;
51 typedef struct {
52 INameSpaceTreeControl2 INameSpaceTreeControl2_iface;
53 IOleWindow IOleWindow_iface;
54 LONG ref;
56 HWND hwnd_main;
57 HWND hwnd_tv;
59 WNDPROC tv_oldwndproc;
61 NSTCSTYLE style;
62 NSTCSTYLE2 style2;
63 struct list roots;
65 INameSpaceTreeControlCustomDraw *customdraw;
66 INameSpaceTreeControlDropHandler *dragdrop;
67 INameSpaceTreeControlEvents *events;
68 } NSTC2Impl;
70 static const DWORD unsupported_styles =
71 NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM | NSTCS_FAVORITESMODE |
72 NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS | NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON |
73 NSTCS_SHOWREFRESHBUTTON | NSTCS_SPRINGEXPAND | NSTCS_RICHTOOLTIP | NSTCS_NOINDENTCHECKS;
74 static const DWORD unsupported_styles2 =
75 NSTCS2_INTERRUPTNOTIFICATIONS | NSTCS2_SHOWNULLSPACEMENU | NSTCS2_DISPLAYPADDING |
76 NSTCS2_DISPLAYPINNEDONLY | NTSCS2_NOSINGLETONAUTOEXPAND | NTSCS2_NEVERINSERTNONENUMERATED;
78 static inline NSTC2Impl *impl_from_INameSpaceTreeControl2(INameSpaceTreeControl2 *iface)
80 return CONTAINING_RECORD(iface, NSTC2Impl, INameSpaceTreeControl2_iface);
83 static inline NSTC2Impl *impl_from_IOleWindow(IOleWindow *iface)
85 return CONTAINING_RECORD(iface, NSTC2Impl, IOleWindow_iface);
88 /* Forward declarations */
89 static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
91 /*************************************************************************
92 * NamespaceTree event wrappers
94 static HRESULT events_OnGetDefaultIconIndex(NSTC2Impl *This, IShellItem *psi,
95 int *piDefaultIcon, int *piOpenIcon)
97 HRESULT ret;
98 LONG refcount;
99 if(!This->events) return E_NOTIMPL;
101 refcount = IShellItem_AddRef(psi);
102 ret = INameSpaceTreeControlEvents_OnGetDefaultIconIndex(This->events, psi, piDefaultIcon, piOpenIcon);
103 if(IShellItem_Release(psi) < refcount - 1)
104 ERR("ShellItem was released by client - please file a bug.\n");
105 return ret;
108 static HRESULT events_OnItemAdded(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot)
110 HRESULT ret;
111 LONG refcount;
112 if(!This->events) return S_OK;
114 refcount = IShellItem_AddRef(psi);
115 ret = INameSpaceTreeControlEvents_OnItemAdded(This->events, psi, fIsRoot);
116 if(IShellItem_Release(psi) < refcount - 1)
117 ERR("ShellItem was released by client - please file a bug.\n");
118 return ret;
121 static HRESULT events_OnItemDeleted(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot)
123 HRESULT ret;
124 LONG refcount;
125 if(!This->events) return S_OK;
127 refcount = IShellItem_AddRef(psi);
128 ret = INameSpaceTreeControlEvents_OnItemDeleted(This->events, psi, fIsRoot);
129 if(IShellItem_Release(psi) < refcount - 1)
130 ERR("ShellItem was released by client - please file a bug.\n");
131 return ret;
134 static HRESULT events_OnBeforeExpand(NSTC2Impl *This, IShellItem *psi)
136 HRESULT ret;
137 LONG refcount;
138 if(!This->events) return S_OK;
140 refcount = IShellItem_AddRef(psi);
141 ret = INameSpaceTreeControlEvents_OnBeforeExpand(This->events, psi);
142 if(IShellItem_Release(psi) < refcount - 1)
143 ERR("ShellItem was released by client - please file a bug.\n");
144 return ret;
147 static HRESULT events_OnAfterExpand(NSTC2Impl *This, IShellItem *psi)
149 HRESULT ret;
150 LONG refcount;
151 if(!This->events) return S_OK;
153 refcount = IShellItem_AddRef(psi);
154 ret = INameSpaceTreeControlEvents_OnAfterExpand(This->events, psi);
155 if(IShellItem_Release(psi) < refcount - 1)
156 ERR("ShellItem was released by client - please file a bug.\n");
157 return ret;
160 static HRESULT events_OnItemClick(NSTC2Impl *This, IShellItem *psi,
161 NSTCEHITTEST nstceHitTest, NSTCECLICKTYPE nstceClickType)
163 HRESULT ret;
164 LONG refcount;
165 if(!This->events) return S_OK;
167 refcount = IShellItem_AddRef(psi);
168 ret = INameSpaceTreeControlEvents_OnItemClick(This->events, psi, nstceHitTest, nstceClickType);
169 if(IShellItem_Release(psi) < refcount - 1)
170 ERR("ShellItem was released by client - please file a bug.\n");
171 return ret;
174 static HRESULT events_OnSelectionChanged(NSTC2Impl *This, IShellItemArray *psia)
176 if(!This->events) return S_OK;
178 return INameSpaceTreeControlEvents_OnSelectionChanged(This->events, psia);
181 static HRESULT events_OnKeyboardInput(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
183 if(!This->events) return S_OK;
185 return INameSpaceTreeControlEvents_OnKeyboardInput(This->events, uMsg, wParam, lParam);
188 /*************************************************************************
189 * NamespaceTree helper functions
191 static DWORD treeview_style_from_nstcs(NSTC2Impl *This, NSTCSTYLE nstcs,
192 NSTCSTYLE nstcs_mask, DWORD *new_style)
194 DWORD old_style, tv_mask = 0;
195 TRACE("%p, %x, %x, %p\n", This, nstcs, nstcs_mask, new_style);
197 if(This->hwnd_tv)
198 old_style = GetWindowLongPtrW(This->hwnd_tv, GWL_STYLE);
199 else
200 old_style = /* The default */
201 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
202 WS_TABSTOP | TVS_NOHSCROLL | TVS_NONEVENHEIGHT | TVS_INFOTIP |
203 TVS_EDITLABELS | TVS_TRACKSELECT;
205 if(nstcs_mask & NSTCS_HASEXPANDOS) tv_mask |= TVS_HASBUTTONS;
206 if(nstcs_mask & NSTCS_HASLINES) tv_mask |= TVS_HASLINES;
207 if(nstcs_mask & NSTCS_FULLROWSELECT) tv_mask |= TVS_FULLROWSELECT;
208 if(nstcs_mask & NSTCS_HORIZONTALSCROLL) tv_mask |= TVS_NOHSCROLL;
209 if(nstcs_mask & NSTCS_ROOTHASEXPANDO) tv_mask |= TVS_LINESATROOT;
210 if(nstcs_mask & NSTCS_SHOWSELECTIONALWAYS) tv_mask |= TVS_SHOWSELALWAYS;
211 if(nstcs_mask & NSTCS_NOINFOTIP) tv_mask |= TVS_INFOTIP;
212 if(nstcs_mask & NSTCS_EVENHEIGHT) tv_mask |= TVS_NONEVENHEIGHT;
213 if(nstcs_mask & NSTCS_DISABLEDRAGDROP) tv_mask |= TVS_DISABLEDRAGDROP;
214 if(nstcs_mask & NSTCS_NOEDITLABELS) tv_mask |= TVS_EDITLABELS;
215 if(nstcs_mask & NSTCS_CHECKBOXES) tv_mask |= TVS_CHECKBOXES;
217 *new_style = 0;
219 if(nstcs & NSTCS_HASEXPANDOS) *new_style |= TVS_HASBUTTONS;
220 if(nstcs & NSTCS_HASLINES) *new_style |= TVS_HASLINES;
221 if(nstcs & NSTCS_FULLROWSELECT) *new_style |= TVS_FULLROWSELECT;
222 if(!(nstcs & NSTCS_HORIZONTALSCROLL)) *new_style |= TVS_NOHSCROLL;
223 if(nstcs & NSTCS_ROOTHASEXPANDO) *new_style |= TVS_LINESATROOT;
224 if(nstcs & NSTCS_SHOWSELECTIONALWAYS) *new_style |= TVS_SHOWSELALWAYS;
225 if(!(nstcs & NSTCS_NOINFOTIP)) *new_style |= TVS_INFOTIP;
226 if(!(nstcs & NSTCS_EVENHEIGHT)) *new_style |= TVS_NONEVENHEIGHT;
227 if(nstcs & NSTCS_DISABLEDRAGDROP) *new_style |= TVS_DISABLEDRAGDROP;
228 if(!(nstcs & NSTCS_NOEDITLABELS)) *new_style |= TVS_EDITLABELS;
229 if(nstcs & NSTCS_CHECKBOXES) *new_style |= TVS_CHECKBOXES;
231 *new_style = (old_style & ~tv_mask) | (*new_style & tv_mask);
233 TRACE("old: %08x, new: %08x\n", old_style, *new_style);
235 return old_style^*new_style;
238 static IShellItem *shellitem_from_treeitem(NSTC2Impl *This, HTREEITEM hitem)
240 TVITEMEXW tvi;
242 tvi.mask = TVIF_PARAM;
243 tvi.lParam = 0;
244 tvi.hItem = hitem;
246 SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
248 TRACE("ShellItem: %p\n", (void*)tvi.lParam);
249 return (IShellItem*)tvi.lParam;
252 /* Returns the root that the given treeitem belongs to. */
253 static nstc_root *root_for_treeitem(NSTC2Impl *This, HTREEITEM hitem)
255 HTREEITEM tmp, hroot = hitem;
256 nstc_root *root;
258 /* Work our way up the hierarchy */
259 for(tmp = hitem; tmp != NULL; hroot = tmp?tmp:hroot)
260 tmp = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hroot);
262 /* Search through the list of roots for a match */
263 LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
264 if(root->htreeitem == hroot)
265 break;
267 TRACE("root is %p\n", root);
268 return root;
271 /* Find a shellitem in the tree, starting from the given node. */
272 static HTREEITEM search_for_shellitem(NSTC2Impl *This, HTREEITEM node,
273 IShellItem *psi)
275 IShellItem *psi_node;
276 HTREEITEM next, result = NULL;
277 HRESULT hr;
278 int cmpo;
279 TRACE("%p, %p, %p\n", This, node, psi);
281 /* Check this node */
282 psi_node = shellitem_from_treeitem(This, node);
283 hr = IShellItem_Compare(psi, psi_node, SICHINT_DISPLAY, &cmpo);
284 if(hr == S_OK)
285 return node;
287 /* Any children? */
288 next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
289 TVGN_CHILD, (LPARAM)node);
290 if(next)
292 result = search_for_shellitem(This, next, psi);
293 if(result) return result;
296 /* Try our next sibling. */
297 next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
298 TVGN_NEXT, (LPARAM)node);
299 if(next)
300 result = search_for_shellitem(This, next, psi);
302 return result;
305 static HTREEITEM treeitem_from_shellitem(NSTC2Impl *This, IShellItem *psi)
307 HTREEITEM root;
308 TRACE("%p, %p\n", This, psi);
310 root = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
311 TVGN_ROOT, 0);
312 if(!root)
313 return NULL;
315 return search_for_shellitem(This, root, psi);
318 static int get_icon(LPCITEMIDLIST lpi, UINT extra_flags)
320 SHFILEINFOW sfi;
321 UINT flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
322 IImageList *list;
324 list = (IImageList *)SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), flags | extra_flags);
325 if (list) IImageList_Release(list);
326 return sfi.iIcon;
329 /* Insert a shellitem into the given place in the tree and return the
330 resulting treeitem. */
331 static HTREEITEM insert_shellitem(NSTC2Impl *This, IShellItem *psi,
332 HTREEITEM hParent, HTREEITEM hInsertAfter)
334 TVINSERTSTRUCTW tvins;
335 TVITEMEXW *tvi = &tvins.u.itemex;
336 HTREEITEM hinserted;
337 TRACE("%p (%p, %p)\n", psi, hParent, hInsertAfter);
339 tvi->mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
340 tvi->cChildren = I_CHILDRENCALLBACK;
341 tvi->iImage = tvi->iSelectedImage = I_IMAGECALLBACK;
342 tvi->pszText = LPSTR_TEXTCALLBACKW;
344 /* Every treeitem contains a pointer to the corresponding ShellItem. */
345 tvi->lParam = (LPARAM)psi;
346 tvins.hParent = hParent;
347 tvins.hInsertAfter = hInsertAfter;
349 hinserted = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_INSERTITEMW, 0,
350 (LPARAM)(LPTVINSERTSTRUCTW)&tvins);
351 if(hinserted)
352 IShellItem_AddRef(psi);
354 return hinserted;
357 /* Enumerates the children of the folder represented by hitem
358 * according to the settings for the root, and adds them to the
359 * treeview. Returns the number of children added. */
360 static UINT fill_sublevel(NSTC2Impl *This, HTREEITEM hitem)
362 IShellItem *psiParent = shellitem_from_treeitem(This, hitem);
363 nstc_root *root = root_for_treeitem(This, hitem);
364 LPITEMIDLIST pidl_parent;
365 IShellFolder *psf;
366 IEnumIDList *peidl;
367 UINT added = 0;
368 HRESULT hr;
370 hr = SHGetIDListFromObject((IUnknown*)psiParent, &pidl_parent);
371 if(SUCCEEDED(hr))
373 hr = IShellItem_BindToHandler(psiParent, NULL, &BHID_SFObject, &IID_IShellFolder, (void**)&psf);
374 if(SUCCEEDED(hr))
376 hr = IShellFolder_EnumObjects(psf, NULL, root->enum_flags, &peidl);
377 if(SUCCEEDED(hr))
379 LPITEMIDLIST pidl;
380 IShellItem *psi;
381 ULONG fetched;
383 while( S_OK == IEnumIDList_Next(peidl, 1, &pidl, &fetched) )
385 hr = SHCreateShellItem(NULL, psf , pidl, &psi);
386 ILFree(pidl);
387 if(SUCCEEDED(hr))
389 if(insert_shellitem(This, psi, hitem, NULL))
391 events_OnItemAdded(This, psi, FALSE);
392 added++;
395 IShellItem_Release(psi);
397 else
398 ERR("SHCreateShellItem failed with 0x%08x\n", hr);
400 IEnumIDList_Release(peidl);
402 else
403 ERR("EnumObjects failed with 0x%08x\n", hr);
405 IShellFolder_Release(psf);
407 else
408 ERR("BindToHandler failed with 0x%08x\n", hr);
410 ILFree(pidl_parent);
412 else
413 ERR("SHGetIDListFromObject failed with 0x%08x\n", hr);
415 return added;
418 static HTREEITEM get_selected_treeitem(NSTC2Impl *This)
420 return (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CARET, 0);
423 static IShellItem *get_selected_shellitem(NSTC2Impl *This)
425 return shellitem_from_treeitem(This, get_selected_treeitem(This));
428 static void collapse_all(NSTC2Impl *This, HTREEITEM node)
430 HTREEITEM next;
432 /* Collapse this node first, and then first child/next sibling. */
433 SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)node);
435 next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
436 if(next) collapse_all(This, next);
438 next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)node);
439 if(next) collapse_all(This, next);
442 static HTREEITEM treeitem_from_point(NSTC2Impl *This, const POINT *pt, UINT *hitflag)
444 TVHITTESTINFO tviht;
445 tviht.pt.x = pt->x;
446 tviht.pt.y = pt->y;
447 tviht.hItem = NULL;
449 SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tviht);
450 if(hitflag) *hitflag = tviht.flags;
451 return tviht.hItem;
454 /*************************************************************************
455 * NamespaceTree window functions
457 static LRESULT create_namespacetree(HWND hWnd, CREATESTRUCTW *crs)
459 NSTC2Impl *This = crs->lpCreateParams;
460 HIMAGELIST ShellSmallIconList;
461 DWORD treeview_style, treeview_ex_style;
463 TRACE("%p (%p)\n", This, crs);
464 SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LPARAM)This);
465 This->hwnd_main = hWnd;
467 treeview_style_from_nstcs(This, This->style, 0xFFFFFFFF, &treeview_style);
469 This->hwnd_tv = CreateWindowExW(0, WC_TREEVIEWW, NULL, treeview_style,
470 0, 0, crs->cx, crs->cy,
471 hWnd, NULL, explorerframe_hinstance, NULL);
473 if(!This->hwnd_tv)
475 ERR("Failed to create treeview!\n");
476 return HRESULT_FROM_WIN32(GetLastError());
479 treeview_ex_style = TVS_EX_DRAWIMAGEASYNC | TVS_EX_RICHTOOLTIP |
480 TVS_EX_DOUBLEBUFFER | TVS_EX_NOSINGLECOLLAPSE;
482 if(This->style & NSTCS_AUTOHSCROLL)
483 treeview_ex_style |= TVS_EX_AUTOHSCROLL;
484 if(This->style & NSTCS_FADEINOUTEXPANDOS)
485 treeview_ex_style |= TVS_EX_FADEINOUTEXPANDOS;
486 if(This->style & NSTCS_PARTIALCHECKBOXES)
487 treeview_ex_style |= TVS_EX_PARTIALCHECKBOXES;
488 if(This->style & NSTCS_EXCLUSIONCHECKBOXES)
489 treeview_ex_style |= TVS_EX_EXCLUSIONCHECKBOXES;
490 if(This->style & NSTCS_DIMMEDCHECKBOXES)
491 treeview_ex_style |= TVS_EX_DIMMEDCHECKBOXES;
493 SendMessageW(This->hwnd_tv, TVM_SETEXTENDEDSTYLE, treeview_ex_style, 0xffff);
495 if(Shell_GetImageLists(NULL, &ShellSmallIconList))
497 SendMessageW(This->hwnd_tv, TVM_SETIMAGELIST,
498 (WPARAM)TVSIL_NORMAL, (LPARAM)ShellSmallIconList);
500 else
502 ERR("Failed to get the System Image List.\n");
505 INameSpaceTreeControl2_AddRef(&This->INameSpaceTreeControl2_iface);
507 /* Subclass the treeview to get the keyboard events. */
508 This->tv_oldwndproc = (WNDPROC)SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC,
509 (ULONG_PTR)tv_wndproc);
510 if(This->tv_oldwndproc)
511 SetPropW(This->hwnd_tv, L"PROP_THIS", This);
513 return TRUE;
516 static LRESULT resize_namespacetree(NSTC2Impl *This)
518 RECT rc;
519 TRACE("%p\n", This);
521 GetClientRect(This->hwnd_main, &rc);
522 MoveWindow(This->hwnd_tv, 0, 0, rc.right-rc.left, rc.bottom-rc.top, TRUE);
524 return TRUE;
527 static LRESULT destroy_namespacetree(NSTC2Impl *This)
529 TRACE("%p\n", This);
531 /* Undo the subclassing */
532 if(This->tv_oldwndproc)
534 SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC, (ULONG_PTR)This->tv_oldwndproc);
535 RemovePropW(This->hwnd_tv, L"PROP_THIS");
538 INameSpaceTreeControl2_RemoveAllRoots(&This->INameSpaceTreeControl2_iface);
540 /* This reference was added in create_namespacetree */
541 INameSpaceTreeControl2_Release(&This->INameSpaceTreeControl2_iface);
542 return TRUE;
545 static LRESULT on_tvn_deleteitemw(NSTC2Impl *This, LPARAM lParam)
547 NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
548 TRACE("%p\n", This);
550 IShellItem_Release((IShellItem*)nmtv->itemOld.lParam);
551 return TRUE;
554 static LRESULT on_tvn_getdispinfow(NSTC2Impl *This, LPARAM lParam)
556 NMTVDISPINFOW *dispinfo = (NMTVDISPINFOW*)lParam;
557 TVITEMEXW *item = (TVITEMEXW*)&dispinfo->item;
558 IShellItem *psi = shellitem_from_treeitem(This, item->hItem);
559 HRESULT hr;
561 TRACE("%p, %p (mask: %x)\n", This, dispinfo, item->mask);
563 if(item->mask & TVIF_CHILDREN)
565 SFGAOF sfgao;
567 hr = IShellItem_GetAttributes(psi, SFGAO_HASSUBFOLDER, &sfgao);
568 if(SUCCEEDED(hr))
569 item->cChildren = (sfgao & SFGAO_HASSUBFOLDER)?1:0;
570 else
571 item->cChildren = 1;
573 item->mask |= TVIF_DI_SETITEM;
576 if(item->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE))
578 LPITEMIDLIST pidl;
580 hr = events_OnGetDefaultIconIndex(This, psi, &item->iImage, &item->iSelectedImage);
581 if(FAILED(hr))
583 hr = SHGetIDListFromObject((IUnknown*)psi, &pidl);
584 if(SUCCEEDED(hr))
586 item->iImage = item->iSelectedImage = get_icon(pidl, 0);
587 item->mask |= TVIF_DI_SETITEM;
588 ILFree(pidl);
590 else
591 ERR("Failed to get IDList (%08x).\n", hr);
595 if(item->mask & TVIF_TEXT)
597 LPWSTR display_name;
599 hr = IShellItem_GetDisplayName(psi, SIGDN_NORMALDISPLAY, &display_name);
600 if(SUCCEEDED(hr))
602 lstrcpynW(item->pszText, display_name, MAX_PATH);
603 item->mask |= TVIF_DI_SETITEM;
604 CoTaskMemFree(display_name);
606 else
607 ERR("Failed to get display name (%08x).\n", hr);
610 return TRUE;
613 static BOOL treenode_has_subfolders(NSTC2Impl *This, HTREEITEM node)
615 return SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
618 static LRESULT on_tvn_itemexpandingw(NSTC2Impl *This, LPARAM lParam)
620 NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
621 IShellItem *psi;
622 TRACE("%p\n", This);
624 psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
625 events_OnBeforeExpand(This, psi);
627 if(!treenode_has_subfolders(This, nmtv->itemNew.hItem))
629 /* The node has no children, try to find some */
630 if(!fill_sublevel(This, nmtv->itemNew.hItem))
632 TVITEMEXW tvi;
633 /* Failed to enumerate any children, remove the expando
634 * (if any). */
635 tvi.hItem = nmtv->itemNew.hItem;
636 tvi.mask = TVIF_CHILDREN;
637 tvi.cChildren = 0;
638 SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
640 return TRUE;
643 return FALSE;
646 static LRESULT on_tvn_itemexpandedw(NSTC2Impl *This, LPARAM lParam)
648 NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
649 IShellItem *psi;
650 TRACE("%p\n", This);
652 psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
653 events_OnAfterExpand(This, psi);
654 return TRUE;
657 static LRESULT on_tvn_selchangedw(NSTC2Impl *This, LPARAM lParam)
659 NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
660 IShellItemArray *psia;
661 IShellItem *psi;
662 HRESULT hr;
663 TRACE("%p\n", This);
665 /* Note: Only supports one selected item. */
666 psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
667 hr = SHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, (void**)&psia);
668 if(SUCCEEDED(hr))
670 events_OnSelectionChanged(This, psia);
671 IShellItemArray_Release(psia);
674 return TRUE;
677 static LRESULT on_nm_click(NSTC2Impl *This, NMHDR *nmhdr)
679 TVHITTESTINFO tvhit;
680 IShellItem *psi;
681 HRESULT hr;
682 TRACE("%p (%p)\n", This, nmhdr);
684 GetCursorPos(&tvhit.pt);
685 ScreenToClient(This->hwnd_tv, &tvhit.pt);
686 SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
688 if(tvhit.flags & (TVHT_NOWHERE|TVHT_ABOVE|TVHT_BELOW))
689 return TRUE;
691 /* TVHT_ maps onto the corresponding NSTCEHT_ */
692 psi = shellitem_from_treeitem(This, tvhit.hItem);
693 hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_LBUTTON);
695 /* The expando should not be expanded unless
696 * double-clicked. */
697 if(tvhit.flags == TVHT_ONITEMBUTTON)
698 return TRUE;
700 if(SUCCEEDED(hr))
701 return FALSE;
702 else
703 return TRUE;
706 static LRESULT on_wm_mbuttonup(NSTC2Impl *This, WPARAM wParam, LPARAM lParam)
708 TVHITTESTINFO tvhit;
709 IShellItem *psi;
710 HRESULT hr;
711 TRACE("%p (%lx, %lx)\n", This, wParam, lParam);
713 tvhit.pt.x = (int)(short)LOWORD(lParam);
714 tvhit.pt.y = (int)(short)HIWORD(lParam);
716 SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
718 /* Seems to generate only ONITEM and ONITEMICON */
719 if( !(tvhit.flags & (TVHT_ONITEM|TVHT_ONITEMICON)) )
720 return FALSE;
722 psi = shellitem_from_treeitem(This, tvhit.hItem);
723 hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_MBUTTON);
725 if(SUCCEEDED(hr))
726 return FALSE;
727 else
728 return TRUE;
731 static LRESULT on_kbd_event(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
733 IShellItem *psi;
734 HTREEITEM hitem;
735 TRACE("%p : %d, %lx, %lx\n", This, uMsg, wParam, lParam);
737 /* Handled by the client? */
738 if(FAILED(events_OnKeyboardInput(This, uMsg, wParam, lParam)))
739 return TRUE;
741 if(uMsg == WM_KEYDOWN)
743 switch(wParam)
745 case VK_DELETE:
746 psi = get_selected_shellitem(This);
747 FIXME("Deletion of file requested (shellitem: %p).\n", psi);
748 return TRUE;
750 case VK_F2:
751 hitem = get_selected_treeitem(This);
752 SendMessageW(This->hwnd_tv, TVM_EDITLABELW, 0, (LPARAM)hitem);
753 return TRUE;
757 /* Let the TreeView handle the key */
758 return FALSE;
761 static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
763 NSTC2Impl *This = (NSTC2Impl*)GetPropW(hWnd, L"PROP_THIS");
765 switch(uMessage) {
766 case WM_KEYDOWN:
767 case WM_KEYUP:
768 case WM_CHAR:
769 case WM_SYSKEYDOWN:
770 case WM_SYSKEYUP:
771 case WM_SYSCHAR:
772 if(on_kbd_event(This, uMessage, wParam, lParam))
773 return TRUE;
774 break;
776 case WM_MBUTTONUP: return on_wm_mbuttonup(This, wParam, lParam);
779 /* Pass the message on to the treeview */
780 return CallWindowProcW(This->tv_oldwndproc, hWnd, uMessage, wParam, lParam);
783 static LRESULT CALLBACK NSTC2_WndProc(HWND hWnd, UINT uMessage,
784 WPARAM wParam, LPARAM lParam)
786 NSTC2Impl *This = (NSTC2Impl*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
787 NMHDR *nmhdr;
789 switch(uMessage)
791 case WM_NCCREATE: return create_namespacetree(hWnd, (CREATESTRUCTW*)lParam);
792 case WM_SIZE: return resize_namespacetree(This);
793 case WM_DESTROY: return destroy_namespacetree(This);
794 case WM_NOTIFY:
795 nmhdr = (NMHDR*)lParam;
796 switch(nmhdr->code)
798 case NM_CLICK: return on_nm_click(This, nmhdr);
799 case TVN_DELETEITEMW: return on_tvn_deleteitemw(This, lParam);
800 case TVN_GETDISPINFOW: return on_tvn_getdispinfow(This, lParam);
801 case TVN_ITEMEXPANDINGW: return on_tvn_itemexpandingw(This, lParam);
802 case TVN_ITEMEXPANDEDW: return on_tvn_itemexpandedw(This, lParam);
803 case TVN_SELCHANGEDW: return on_tvn_selchangedw(This, lParam);
804 default: break;
806 break;
807 default: return DefWindowProcW(hWnd, uMessage, wParam, lParam);
809 return 0;
812 /**************************************************************************
813 * INameSpaceTreeControl2 Implementation
815 static HRESULT WINAPI NSTC2_fnQueryInterface(INameSpaceTreeControl2* iface,
816 REFIID riid,
817 void **ppvObject)
819 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
820 TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
822 *ppvObject = NULL;
823 if(IsEqualIID(riid, &IID_INameSpaceTreeControl2) ||
824 IsEqualIID(riid, &IID_INameSpaceTreeControl) ||
825 IsEqualIID(riid, &IID_IUnknown))
827 *ppvObject = &This->INameSpaceTreeControl2_iface;
829 else if(IsEqualIID(riid, &IID_IOleWindow))
831 *ppvObject = &This->IOleWindow_iface;
834 if(*ppvObject)
836 IUnknown_AddRef((IUnknown*)*ppvObject);
837 return S_OK;
840 return E_NOINTERFACE;
843 static ULONG WINAPI NSTC2_fnAddRef(INameSpaceTreeControl2* iface)
845 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
846 LONG ref = InterlockedIncrement(&This->ref);
848 TRACE("%p - ref %d\n", This, ref);
850 return ref;
853 static ULONG WINAPI NSTC2_fnRelease(INameSpaceTreeControl2* iface)
855 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
856 LONG ref = InterlockedDecrement(&This->ref);
858 TRACE("%p - ref: %d\n", This, ref);
860 if(!ref)
862 TRACE("Freeing.\n");
863 heap_free(This);
864 EFRAME_UnlockModule();
867 return ref;
870 static HRESULT WINAPI NSTC2_fnInitialize(INameSpaceTreeControl2* iface,
871 HWND hwndParent,
872 RECT *prc,
873 NSTCSTYLE nstcsFlags)
875 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
876 WNDCLASSW wc;
877 DWORD window_style, window_ex_style;
878 INITCOMMONCONTROLSEX icex;
879 RECT rc;
880 static const WCHAR NSTC2_CLASS_NAME[] = L"NamespaceTreeControl";
882 TRACE("%p (%p, %p, %x)\n", This, hwndParent, prc, nstcsFlags);
884 if(nstcsFlags & unsupported_styles)
885 FIXME("0x%08x contains the unsupported style(s) 0x%08x\n",
886 nstcsFlags, nstcsFlags & unsupported_styles);
888 This->style = nstcsFlags;
890 icex.dwSize = sizeof( icex );
891 icex.dwICC = ICC_TREEVIEW_CLASSES;
892 InitCommonControlsEx( &icex );
894 if(!GetClassInfoW(explorerframe_hinstance, NSTC2_CLASS_NAME, &wc))
896 wc.style = CS_HREDRAW | CS_VREDRAW;
897 wc.lpfnWndProc = NSTC2_WndProc;
898 wc.cbClsExtra = 0;
899 wc.cbWndExtra = 0;
900 wc.hInstance = explorerframe_hinstance;
901 wc.hIcon = 0;
902 wc.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
903 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
904 wc.lpszMenuName = NULL;
905 wc.lpszClassName = NSTC2_CLASS_NAME;
907 if (!RegisterClassW(&wc)) return E_FAIL;
910 /* NSTCS_TABSTOP and NSTCS_BORDER affects the host window */
911 window_style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
912 (nstcsFlags & NSTCS_BORDER ? WS_BORDER : 0);
913 window_ex_style = nstcsFlags & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
915 if(prc)
916 rc = *prc;
917 else
918 SetRectEmpty(&rc);
920 This->hwnd_main = CreateWindowExW(window_ex_style, NSTC2_CLASS_NAME, NULL, window_style,
921 rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
922 hwndParent, 0, explorerframe_hinstance, This);
924 if(!This->hwnd_main)
926 ERR("Failed to create the window.\n");
927 return HRESULT_FROM_WIN32(GetLastError());
930 return S_OK;
933 static HRESULT WINAPI NSTC2_fnTreeAdvise(INameSpaceTreeControl2* iface, IUnknown *handler, DWORD *cookie)
935 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
937 TRACE("%p (%p, %p)\n", This, handler, cookie);
939 *cookie = 0;
941 /* Only one client supported */
942 if (This->events || This->customdraw || This->dragdrop)
943 return E_FAIL;
945 /* FIXME: request INameSpaceTreeAccessible too */
946 IUnknown_QueryInterface(handler, &IID_INameSpaceTreeControlEvents, (void**)&This->events);
947 IUnknown_QueryInterface(handler, &IID_INameSpaceTreeControlCustomDraw, (void**)&This->customdraw);
948 IUnknown_QueryInterface(handler, &IID_INameSpaceTreeControlDropHandler, (void**)&This->dragdrop);
950 if (This->events || This->customdraw || This->dragdrop)
951 *cookie = 1;
953 return *cookie ? S_OK : E_FAIL;
956 static HRESULT WINAPI NSTC2_fnTreeUnadvise(INameSpaceTreeControl2* iface, DWORD cookie)
958 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
960 TRACE("%p (%x)\n", This, cookie);
962 /* The cookie is ignored. */
964 if (This->events)
966 INameSpaceTreeControlEvents_Release(This->events);
967 This->events = NULL;
970 if (This->customdraw)
972 INameSpaceTreeControlCustomDraw_Release(This->customdraw);
973 This->customdraw = NULL;
976 if (This->dragdrop)
978 INameSpaceTreeControlDropHandler_Release(This->dragdrop);
979 This->dragdrop = NULL;
982 return S_OK;
985 static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface,
986 int iIndex,
987 IShellItem *psiRoot,
988 SHCONTF grfEnumFlags,
989 NSTCROOTSTYLE grfRootStyle,
990 IShellItemFilter *pif)
992 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
993 nstc_root *new_root;
994 struct list *add_after_entry;
995 HTREEITEM add_after_hitem;
996 int i;
998 TRACE("%p, %d, %p, %x, %x, %p\n", This, iIndex, psiRoot, grfEnumFlags, grfRootStyle, pif);
1000 new_root = heap_alloc(sizeof(*new_root));
1001 if(!new_root)
1002 return E_OUTOFMEMORY;
1004 new_root->psi = psiRoot;
1005 new_root->enum_flags = grfEnumFlags;
1006 new_root->root_style = grfRootStyle;
1007 new_root->pif = pif;
1009 /* We want to keep the roots in the internal list and in the
1010 * treeview in the same order. */
1011 add_after_entry = &This->roots;
1012 for(i = 0; i < max(0, iIndex) && list_next(&This->roots, add_after_entry); i++)
1013 add_after_entry = list_next(&This->roots, add_after_entry);
1015 if(add_after_entry == &This->roots)
1016 add_after_hitem = TVI_FIRST;
1017 else
1018 add_after_hitem = LIST_ENTRY(add_after_entry, nstc_root, entry)->htreeitem;
1020 new_root->htreeitem = insert_shellitem(This, psiRoot, TVI_ROOT, add_after_hitem);
1021 if(!new_root->htreeitem)
1023 WARN("Failed to add the root.\n");
1024 heap_free(new_root);
1025 return E_FAIL;
1028 list_add_after(add_after_entry, &new_root->entry);
1029 events_OnItemAdded(This, psiRoot, TRUE);
1031 if(grfRootStyle & NSTCRS_HIDDEN)
1033 TVITEMEXW tvi;
1034 tvi.mask = TVIF_STATEEX;
1035 tvi.uStateEx = TVIS_EX_FLAT;
1036 tvi.hItem = new_root->htreeitem;
1038 SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
1041 if(grfRootStyle & NSTCRS_EXPANDED)
1042 SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_EXPAND,
1043 (LPARAM)new_root->htreeitem);
1045 return S_OK;
1048 static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface,
1049 IShellItem *psiRoot,
1050 SHCONTF grfEnumFlags,
1051 NSTCROOTSTYLE grfRootStyle,
1052 IShellItemFilter *pif)
1054 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1055 UINT root_count;
1056 TRACE("%p, %p, %x, %x, %p\n",
1057 This, psiRoot, grfEnumFlags, grfRootStyle, pif);
1059 root_count = list_count(&This->roots);
1061 return INameSpaceTreeControl2_InsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif);
1064 static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface,
1065 IShellItem *psiRoot)
1067 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1068 nstc_root *cursor, *root = NULL;
1069 TRACE("%p (%p)\n", This, psiRoot);
1071 if(!psiRoot)
1072 return E_NOINTERFACE;
1074 LIST_FOR_EACH_ENTRY(cursor, &This->roots, nstc_root, entry)
1076 HRESULT hr;
1077 int order;
1078 hr = IShellItem_Compare(psiRoot, cursor->psi, SICHINT_DISPLAY, &order);
1079 if(hr == S_OK)
1081 root = cursor;
1082 break;
1086 TRACE("root %p\n", root);
1087 if(root)
1089 events_OnItemDeleted(This, root->psi, TRUE);
1090 SendMessageW(This->hwnd_tv, TVM_DELETEITEM, 0, (LPARAM)root->htreeitem);
1091 list_remove(&root->entry);
1092 heap_free(root);
1093 return S_OK;
1095 else
1097 WARN("No matching root found.\n");
1098 return E_FAIL;
1102 static HRESULT WINAPI NSTC2_fnRemoveAllRoots(INameSpaceTreeControl2* iface)
1104 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1105 nstc_root *cur1, *cur2;
1107 TRACE("%p\n", This);
1109 if (list_empty(&This->roots))
1110 return E_INVALIDARG;
1112 LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->roots, nstc_root, entry)
1113 INameSpaceTreeControl2_RemoveRoot(iface, cur1->psi);
1115 return S_OK;
1118 static HRESULT WINAPI NSTC2_fnGetRootItems(INameSpaceTreeControl2* iface,
1119 IShellItemArray **ppsiaRootItems)
1121 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1122 IShellFolder *psf;
1123 LPITEMIDLIST *array;
1124 nstc_root *root;
1125 UINT count, i;
1126 HRESULT hr;
1127 TRACE("%p (%p)\n", This, ppsiaRootItems);
1129 count = list_count(&This->roots);
1131 if(!count)
1132 return E_INVALIDARG;
1134 array = heap_alloc(sizeof(LPITEMIDLIST)*count);
1136 i = 0;
1137 LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
1138 SHGetIDListFromObject((IUnknown*)root->psi, &array[i++]);
1140 SHGetDesktopFolder(&psf);
1141 hr = SHCreateShellItemArray(NULL, psf, count, (PCUITEMID_CHILD_ARRAY)array,
1142 ppsiaRootItems);
1143 IShellFolder_Release(psf);
1145 for(i = 0; i < count; i++)
1146 ILFree(array[i]);
1148 heap_free(array);
1150 return hr;
1153 static HRESULT WINAPI NSTC2_fnSetItemState(INameSpaceTreeControl2* iface,
1154 IShellItem *psi,
1155 NSTCITEMSTATE nstcisMask,
1156 NSTCITEMSTATE nstcisFlags)
1158 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1159 TVITEMEXW tvi;
1160 HTREEITEM hitem;
1162 TRACE("%p (%p, %x, %x)\n", This, psi, nstcisMask, nstcisFlags);
1164 hitem = treeitem_from_shellitem(This, psi);
1165 if(!hitem) return E_INVALIDARG;
1167 /* Passing both NSTCIS_SELECTED and NSTCIS_SELECTEDNOEXPAND results
1168 in two TVM_SETITEMW's */
1169 if((nstcisMask&nstcisFlags) & NSTCIS_SELECTED)
1171 SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hitem);
1172 SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (LPARAM)hitem);
1174 if((nstcisMask&nstcisFlags) & NSTCIS_SELECTEDNOEXPAND)
1176 SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET|TVSI_NOSINGLEEXPAND, (LPARAM)hitem);
1179 /* If NSTCIS_EXPANDED is among the flags, the mask is ignored. */
1180 if((nstcisMask|nstcisFlags) & NSTCIS_EXPANDED)
1182 WPARAM arg = nstcisFlags&NSTCIS_EXPANDED ? TVE_EXPAND:TVE_COLLAPSE;
1183 SendMessageW(This->hwnd_tv, TVM_EXPAND, arg, (LPARAM)hitem);
1186 if(nstcisMask & NSTCIS_DISABLED)
1187 tvi.mask = TVIF_STATE | TVIF_STATEEX;
1188 else if( ((nstcisMask^nstcisFlags) & (NSTCIS_SELECTED|NSTCIS_EXPANDED|NSTCIS_SELECTEDNOEXPAND)) ||
1189 ((nstcisMask|nstcisFlags) & NSTCIS_BOLD) ||
1190 (nstcisFlags & NSTCIS_DISABLED) )
1191 tvi.mask = TVIF_STATE;
1192 else
1193 tvi.mask = 0;
1195 if(tvi.mask)
1197 tvi.stateMask = tvi.state = 0;
1198 tvi.stateMask |= ((nstcisFlags^nstcisMask)&NSTCIS_SELECTED) ? TVIS_SELECTED : 0;
1199 tvi.stateMask |= (nstcisMask|nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
1200 tvi.state |= (nstcisMask&nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
1202 if((nstcisMask&NSTCIS_EXPANDED)^(nstcisFlags&NSTCIS_EXPANDED))
1204 tvi.stateMask = 0;
1207 tvi.uStateEx = (nstcisFlags&nstcisMask)&NSTCIS_DISABLED?TVIS_EX_DISABLED:0;
1208 tvi.hItem = hitem;
1210 SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
1213 return S_OK;
1216 static HRESULT WINAPI NSTC2_fnGetItemState(INameSpaceTreeControl2* iface,
1217 IShellItem *psi,
1218 NSTCITEMSTATE nstcisMask,
1219 NSTCITEMSTATE *pnstcisFlags)
1221 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1222 HTREEITEM hitem;
1223 TVITEMEXW tvi;
1224 TRACE("%p (%p, %x, %p)\n", This, psi, nstcisMask, pnstcisFlags);
1226 hitem = treeitem_from_shellitem(This, psi);
1227 if(!hitem)
1228 return E_INVALIDARG;
1230 *pnstcisFlags = 0;
1232 tvi.hItem = hitem;
1233 tvi.mask = TVIF_STATE;
1234 tvi.stateMask = TVIS_SELECTED|TVIS_EXPANDED|TVIS_BOLD;
1236 if(nstcisMask & NSTCIS_DISABLED)
1237 tvi.mask |= TVIF_STATEEX;
1239 SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
1240 *pnstcisFlags |= (tvi.state & TVIS_SELECTED)?NSTCIS_SELECTED:0;
1241 *pnstcisFlags |= (tvi.state & TVIS_EXPANDED)?NSTCIS_EXPANDED:0;
1242 *pnstcisFlags |= (tvi.state & TVIS_BOLD)?NSTCIS_BOLD:0;
1243 *pnstcisFlags |= (tvi.uStateEx & TVIS_EX_DISABLED)?NSTCIS_DISABLED:0;
1245 *pnstcisFlags &= nstcisMask;
1247 return S_OK;
1250 static HRESULT WINAPI NSTC2_fnGetSelectedItems(INameSpaceTreeControl2* iface,
1251 IShellItemArray **psiaItems)
1253 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1254 IShellItem *psiselected;
1256 TRACE("%p (%p)\n", This, psiaItems);
1258 psiselected = get_selected_shellitem(This);
1259 if(!psiselected)
1261 *psiaItems = NULL;
1262 return E_FAIL;
1265 return SHCreateShellItemArrayFromShellItem(psiselected, &IID_IShellItemArray,
1266 (void**)psiaItems);
1269 static HRESULT WINAPI NSTC2_fnGetItemCustomState(INameSpaceTreeControl2* iface,
1270 IShellItem *psi,
1271 int *piStateNumber)
1273 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1274 FIXME("stub, %p (%p, %p)\n", This, psi, piStateNumber);
1275 return E_NOTIMPL;
1278 static HRESULT WINAPI NSTC2_fnSetItemCustomState(INameSpaceTreeControl2* iface,
1279 IShellItem *psi,
1280 int iStateNumber)
1282 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1283 FIXME("stub, %p (%p, %d)\n", This, psi, iStateNumber);
1284 return E_NOTIMPL;
1287 static HRESULT WINAPI NSTC2_fnEnsureItemVisible(INameSpaceTreeControl2* iface,
1288 IShellItem *psi)
1290 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1291 HTREEITEM hitem;
1293 TRACE("%p (%p)\n", This, psi);
1295 hitem = treeitem_from_shellitem(This, psi);
1296 if(hitem)
1298 SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (WPARAM)hitem);
1299 return S_OK;
1302 return E_INVALIDARG;
1305 static HRESULT WINAPI NSTC2_fnSetTheme(INameSpaceTreeControl2* iface,
1306 LPCWSTR pszTheme)
1308 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1309 FIXME("stub, %p (%p)\n", This, pszTheme);
1310 return E_NOTIMPL;
1313 static HRESULT WINAPI NSTC2_fnGetNextItem(INameSpaceTreeControl2* iface,
1314 IShellItem *psi,
1315 NSTCGNI nstcgi,
1316 IShellItem **ppsiNext)
1318 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1319 HTREEITEM hitem, hnext;
1320 UINT tvgn;
1321 TRACE("%p (%p, %x, %p)\n", This, psi, nstcgi, ppsiNext);
1323 if(!ppsiNext) return E_POINTER;
1324 if(!psi) return E_FAIL;
1326 *ppsiNext = NULL;
1328 hitem = treeitem_from_shellitem(This, psi);
1329 if(!hitem)
1330 return E_INVALIDARG;
1332 switch(nstcgi)
1334 case NSTCGNI_NEXT: tvgn = TVGN_NEXT; break;
1335 case NSTCGNI_NEXTVISIBLE: tvgn = TVGN_NEXTVISIBLE; break;
1336 case NSTCGNI_PREV: tvgn = TVGN_PREVIOUS; break;
1337 case NSTCGNI_PREVVISIBLE: tvgn = TVGN_PREVIOUSVISIBLE; break;
1338 case NSTCGNI_PARENT: tvgn = TVGN_PARENT; break;
1339 case NSTCGNI_CHILD: tvgn = TVGN_CHILD; break;
1340 case NSTCGNI_FIRSTVISIBLE: tvgn = TVGN_FIRSTVISIBLE; break;
1341 case NSTCGNI_LASTVISIBLE: tvgn = TVGN_LASTVISIBLE; break;
1342 default:
1343 FIXME("Unknown nstcgi value %d\n", nstcgi);
1344 return E_FAIL;
1347 hnext = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, tvgn, (WPARAM)hitem);
1348 if(hnext)
1350 *ppsiNext = shellitem_from_treeitem(This, hnext);
1351 IShellItem_AddRef(*ppsiNext);
1352 return S_OK;
1355 return E_FAIL;
1358 static HRESULT WINAPI NSTC2_fnHitTest(INameSpaceTreeControl2* iface,
1359 POINT *ppt,
1360 IShellItem **ppsiOut)
1362 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1363 HTREEITEM hitem;
1364 TRACE("%p (%p, %p)\n", This, ppsiOut, ppt);
1366 if(!ppt || !ppsiOut)
1367 return E_POINTER;
1369 *ppsiOut = NULL;
1371 hitem = treeitem_from_point(This, ppt, NULL);
1372 if(hitem)
1373 *ppsiOut = shellitem_from_treeitem(This, hitem);
1375 if(*ppsiOut)
1377 IShellItem_AddRef(*ppsiOut);
1378 return S_OK;
1381 return S_FALSE;
1384 static HRESULT WINAPI NSTC2_fnGetItemRect(INameSpaceTreeControl2* iface,
1385 IShellItem *psi,
1386 RECT *prect)
1388 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1389 HTREEITEM hitem;
1390 TRACE("%p (%p, %p)\n", This, psi, prect);
1392 if(!psi || !prect)
1393 return E_POINTER;
1395 hitem = treeitem_from_shellitem(This, psi);
1396 if(hitem)
1398 *(HTREEITEM*)prect = hitem;
1399 if(SendMessageW(This->hwnd_tv, TVM_GETITEMRECT, FALSE, (LPARAM)prect))
1401 MapWindowPoints(This->hwnd_tv, HWND_DESKTOP, (POINT*)prect, 2);
1402 return S_OK;
1406 return E_INVALIDARG;
1409 static HRESULT WINAPI NSTC2_fnCollapseAll(INameSpaceTreeControl2* iface)
1411 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1412 nstc_root *root;
1413 TRACE("%p\n", This);
1415 LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
1416 collapse_all(This, root->htreeitem);
1418 return S_OK;
1421 static HRESULT WINAPI NSTC2_fnSetControlStyle(INameSpaceTreeControl2* iface,
1422 NSTCSTYLE nstcsMask,
1423 NSTCSTYLE nstcsStyle)
1425 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1426 static const DWORD tv_style_flags =
1427 NSTCS_HASEXPANDOS | NSTCS_HASLINES | NSTCS_FULLROWSELECT |
1428 NSTCS_HORIZONTALSCROLL | NSTCS_ROOTHASEXPANDO |
1429 NSTCS_SHOWSELECTIONALWAYS | NSTCS_NOINFOTIP | NSTCS_EVENHEIGHT |
1430 NSTCS_DISABLEDRAGDROP | NSTCS_NOEDITLABELS | NSTCS_CHECKBOXES;
1431 static const DWORD host_style_flags = NSTCS_TABSTOP | NSTCS_BORDER;
1432 static const DWORD nstc_flags =
1433 NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM |
1434 NSTCS_FAVORITESMODE | NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS |
1435 NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON | NSTCS_SHOWREFRESHBUTTON;
1436 TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
1438 /* Fail if there is an attempt to set an unknown style. */
1439 if(nstcsMask & ~(tv_style_flags | host_style_flags | nstc_flags))
1440 return E_FAIL;
1442 if(nstcsMask & tv_style_flags)
1444 DWORD new_style;
1445 treeview_style_from_nstcs(This, nstcsStyle, nstcsMask, &new_style);
1446 SetWindowLongPtrW(This->hwnd_tv, GWL_STYLE, new_style);
1449 /* Flags affecting the host window */
1450 if(nstcsMask & NSTCS_BORDER)
1452 DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_STYLE);
1453 new_style &= ~WS_BORDER;
1454 new_style |= nstcsStyle & NSTCS_BORDER ? WS_BORDER : 0;
1455 SetWindowLongPtrW(This->hwnd_main, GWL_STYLE, new_style);
1457 if(nstcsMask & NSTCS_TABSTOP)
1459 DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE);
1460 new_style &= ~WS_EX_CONTROLPARENT;
1461 new_style |= nstcsStyle & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
1462 SetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE, new_style);
1465 if((nstcsStyle & nstcsMask) & unsupported_styles)
1466 FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
1467 (nstcsStyle & nstcsMask),
1468 (nstcsStyle & nstcsMask) & unsupported_styles);
1470 This->style &= ~nstcsMask;
1471 This->style |= (nstcsStyle & nstcsMask);
1473 return S_OK;
1476 static HRESULT WINAPI NSTC2_fnGetControlStyle(INameSpaceTreeControl2* iface,
1477 NSTCSTYLE nstcsMask,
1478 NSTCSTYLE *pnstcsStyle)
1480 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1481 TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
1483 *pnstcsStyle = (This->style & nstcsMask);
1485 return S_OK;
1488 static HRESULT WINAPI NSTC2_fnSetControlStyle2(INameSpaceTreeControl2* iface,
1489 NSTCSTYLE2 nstcsMask,
1490 NSTCSTYLE2 nstcsStyle)
1492 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1493 TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
1495 if((nstcsStyle & nstcsMask) & unsupported_styles2)
1496 FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
1497 (nstcsStyle & nstcsMask),
1498 (nstcsStyle & nstcsMask) & unsupported_styles2);
1500 This->style2 &= ~nstcsMask;
1501 This->style2 |= (nstcsStyle & nstcsMask);
1503 return S_OK;
1506 static HRESULT WINAPI NSTC2_fnGetControlStyle2(INameSpaceTreeControl2* iface,
1507 NSTCSTYLE2 nstcsMask,
1508 NSTCSTYLE2 *pnstcsStyle)
1510 NSTC2Impl *This = impl_from_INameSpaceTreeControl2(iface);
1511 TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
1513 *pnstcsStyle = (This->style2 & nstcsMask);
1515 return S_OK;
1518 static const INameSpaceTreeControl2Vtbl vt_INameSpaceTreeControl2 = {
1519 NSTC2_fnQueryInterface,
1520 NSTC2_fnAddRef,
1521 NSTC2_fnRelease,
1522 NSTC2_fnInitialize,
1523 NSTC2_fnTreeAdvise,
1524 NSTC2_fnTreeUnadvise,
1525 NSTC2_fnAppendRoot,
1526 NSTC2_fnInsertRoot,
1527 NSTC2_fnRemoveRoot,
1528 NSTC2_fnRemoveAllRoots,
1529 NSTC2_fnGetRootItems,
1530 NSTC2_fnSetItemState,
1531 NSTC2_fnGetItemState,
1532 NSTC2_fnGetSelectedItems,
1533 NSTC2_fnGetItemCustomState,
1534 NSTC2_fnSetItemCustomState,
1535 NSTC2_fnEnsureItemVisible,
1536 NSTC2_fnSetTheme,
1537 NSTC2_fnGetNextItem,
1538 NSTC2_fnHitTest,
1539 NSTC2_fnGetItemRect,
1540 NSTC2_fnCollapseAll,
1541 NSTC2_fnSetControlStyle,
1542 NSTC2_fnGetControlStyle,
1543 NSTC2_fnSetControlStyle2,
1544 NSTC2_fnGetControlStyle2
1547 /**************************************************************************
1548 * IOleWindow Implementation
1551 static HRESULT WINAPI IOW_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
1553 NSTC2Impl *This = impl_from_IOleWindow(iface);
1554 return INameSpaceTreeControl2_QueryInterface(&This->INameSpaceTreeControl2_iface, riid, ppvObject);
1557 static ULONG WINAPI IOW_fnAddRef(IOleWindow *iface)
1559 NSTC2Impl *This = impl_from_IOleWindow(iface);
1560 return INameSpaceTreeControl2_AddRef(&This->INameSpaceTreeControl2_iface);
1563 static ULONG WINAPI IOW_fnRelease(IOleWindow *iface)
1565 NSTC2Impl *This = impl_from_IOleWindow(iface);
1566 return INameSpaceTreeControl2_Release(&This->INameSpaceTreeControl2_iface);
1569 static HRESULT WINAPI IOW_fnGetWindow(IOleWindow *iface, HWND *phwnd)
1571 NSTC2Impl *This = impl_from_IOleWindow(iface);
1572 TRACE("%p (%p)\n", This, phwnd);
1574 *phwnd = This->hwnd_main;
1575 return S_OK;
1578 static HRESULT WINAPI IOW_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMode)
1580 NSTC2Impl *This = impl_from_IOleWindow(iface);
1581 TRACE("%p (%d)\n", This, fEnterMode);
1583 /* Not implemented */
1584 return E_NOTIMPL;
1587 static const IOleWindowVtbl vt_IOleWindow = {
1588 IOW_fnQueryInterface,
1589 IOW_fnAddRef,
1590 IOW_fnRelease,
1591 IOW_fnGetWindow,
1592 IOW_fnContextSensitiveHelp
1595 HRESULT NamespaceTreeControl_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
1597 NSTC2Impl *nstc;
1598 HRESULT ret;
1600 TRACE ("%p %s %p\n", pUnkOuter, debugstr_guid(riid), ppv);
1602 if(!ppv)
1603 return E_POINTER;
1604 if(pUnkOuter)
1605 return CLASS_E_NOAGGREGATION;
1607 EFRAME_LockModule();
1609 nstc = heap_alloc_zero(sizeof(*nstc));
1610 if (!nstc)
1611 return E_OUTOFMEMORY;
1613 nstc->ref = 1;
1614 nstc->INameSpaceTreeControl2_iface.lpVtbl = &vt_INameSpaceTreeControl2;
1615 nstc->IOleWindow_iface.lpVtbl = &vt_IOleWindow;
1617 list_init(&nstc->roots);
1619 ret = INameSpaceTreeControl2_QueryInterface(&nstc->INameSpaceTreeControl2_iface, riid, ppv);
1620 INameSpaceTreeControl2_Release(&nstc->INameSpaceTreeControl2_iface);
1622 TRACE("--(%p)\n", ppv);
1623 return ret;