explorerframe: Implement EnsureItemVisible.
[wine] / dlls / explorerframe / nstc.c
1 /*
2  * NamespaceTreeControl implementation.
3  *
4  * Copyright 2010 David Hedberg
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "shellapi.h"
32
33 #include "wine/list.h"
34 #include "wine/debug.h"
35
36 #include "explorerframe_main.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(nstc);
39
40 typedef struct nstc_root {
41     IShellItem *psi;
42     HTREEITEM htreeitem;
43     SHCONTF enum_flags;
44     NSTCROOTSTYLE root_style;
45     IShellItemFilter *pif;
46     struct list entry;
47 } nstc_root;
48
49 typedef struct {
50     const INameSpaceTreeControl2Vtbl *lpVtbl;
51     const IOleWindowVtbl *lpowVtbl;
52     LONG ref;
53
54     HWND hwnd_main;
55     HWND hwnd_tv;
56
57     WNDPROC tv_oldwndproc;
58
59     NSTCSTYLE style;
60     NSTCSTYLE2 style2;
61     struct list roots;
62
63     INameSpaceTreeControlEvents *pnstce;
64 } NSTC2Impl;
65
66 static const DWORD unsupported_styles =
67     NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM | NSTCS_FAVORITESMODE |
68     NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS | NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON |
69     NSTCS_SHOWREFRESHBUTTON | NSTCS_SPRINGEXPAND | NSTCS_RICHTOOLTIP | NSTCS_NOINDENTCHECKS;
70 static const DWORD unsupported_styles2 =
71     NSTCS2_INTERRUPTNOTIFICATIONS | NSTCS2_SHOWNULLSPACEMENU | NSTCS2_DISPLAYPADDING |
72     NSTCS2_DISPLAYPINNEDONLY | NTSCS2_NOSINGLETONAUTOEXPAND | NTSCS2_NEVERINSERTNONENUMERATED;
73
74 /* Forward declarations */
75 static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
76
77 /*************************************************************************
78 * NamespaceTree event wrappers
79 */
80 static HRESULT events_OnGetDefaultIconIndex(NSTC2Impl *This, IShellItem *psi,
81                                             int *piDefaultIcon, int *piOpenIcon)
82 {
83     HRESULT ret;
84     LONG refcount;
85     if(!This->pnstce) return E_NOTIMPL;
86
87     refcount = IShellItem_AddRef(psi);
88     ret = INameSpaceTreeControlEvents_OnGetDefaultIconIndex(This->pnstce, psi, piDefaultIcon, piOpenIcon);
89     if(IShellItem_Release(psi) < refcount - 1)
90         ERR("ShellItem was released by client - please file a bug.\n");
91     return ret;
92 }
93
94 static HRESULT events_OnItemAdded(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot)
95 {
96     HRESULT ret;
97     LONG refcount;
98     if(!This->pnstce) return S_OK;
99
100     refcount = IShellItem_AddRef(psi);
101     ret = INameSpaceTreeControlEvents_OnItemAdded(This->pnstce, psi, fIsRoot);
102     if(IShellItem_Release(psi) < refcount - 1)
103         ERR("ShellItem was released by client - please file a bug.\n");
104     return ret;
105 }
106
107 static HRESULT events_OnItemDeleted(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot)
108 {
109     HRESULT ret;
110     LONG refcount;
111     if(!This->pnstce) return S_OK;
112
113     refcount = IShellItem_AddRef(psi);
114     ret = INameSpaceTreeControlEvents_OnItemDeleted(This->pnstce, psi, fIsRoot);
115     if(IShellItem_Release(psi) < refcount - 1)
116         ERR("ShellItem was released by client - please file a bug.\n");
117     return ret;
118 }
119
120 static HRESULT events_OnBeforeExpand(NSTC2Impl *This, IShellItem *psi)
121 {
122     HRESULT ret;
123     LONG refcount;
124     if(!This->pnstce) return S_OK;
125
126     refcount = IShellItem_AddRef(psi);
127     ret = INameSpaceTreeControlEvents_OnBeforeExpand(This->pnstce, psi);
128     if(IShellItem_Release(psi) < refcount - 1)
129         ERR("ShellItem was released by client - please file a bug.\n");
130     return ret;
131 }
132
133 static HRESULT events_OnAfterExpand(NSTC2Impl *This, IShellItem *psi)
134 {
135     HRESULT ret;
136     LONG refcount;
137     if(!This->pnstce) return S_OK;
138
139     refcount = IShellItem_AddRef(psi);
140     ret = INameSpaceTreeControlEvents_OnAfterExpand(This->pnstce, psi);
141     if(IShellItem_Release(psi) < refcount - 1)
142         ERR("ShellItem was released by client - please file a bug.\n");
143     return ret;
144 }
145
146 static HRESULT events_OnItemClick(NSTC2Impl *This, IShellItem *psi,
147                                   NSTCEHITTEST nstceHitTest, NSTCECLICKTYPE nstceClickType)
148 {
149     HRESULT ret;
150     LONG refcount;
151     if(!This->pnstce) return S_OK;
152
153     refcount = IShellItem_AddRef(psi);
154     ret = INameSpaceTreeControlEvents_OnItemClick(This->pnstce, psi, nstceHitTest, nstceClickType);
155     if(IShellItem_Release(psi) < refcount - 1)
156         ERR("ShellItem was released by client - please file a bug.\n");
157     return ret;
158 }
159
160 static HRESULT events_OnSelectionChanged(NSTC2Impl *This, IShellItemArray *psia)
161 {
162     if(!This->pnstce) return S_OK;
163
164     return INameSpaceTreeControlEvents_OnSelectionChanged(This->pnstce, psia);
165 }
166
167 static HRESULT events_OnKeyboardInput(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
168 {
169     if(!This->pnstce) return S_OK;
170
171     return INameSpaceTreeControlEvents_OnKeyboardInput(This->pnstce, uMsg, wParam, lParam);
172 }
173
174 /*************************************************************************
175  * NamespaceTree helper functions
176  */
177 static DWORD treeview_style_from_nstcs(NSTC2Impl *This, NSTCSTYLE nstcs,
178                                        NSTCSTYLE nstcs_mask, DWORD *new_style)
179 {
180     DWORD old_style, tv_mask = 0;
181     TRACE("%p, %x, %x, %p\n", This, nstcs, nstcs_mask, new_style);
182
183     if(This->hwnd_tv)
184         old_style = GetWindowLongPtrW(This->hwnd_tv, GWL_STYLE);
185     else
186         old_style = /* The default */
187             WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
188             WS_TABSTOP | TVS_NOHSCROLL | TVS_NONEVENHEIGHT | TVS_INFOTIP |
189             TVS_EDITLABELS | TVS_TRACKSELECT;
190
191     if(nstcs_mask & NSTCS_HASEXPANDOS)         tv_mask |= TVS_HASBUTTONS;
192     if(nstcs_mask & NSTCS_HASLINES)            tv_mask |= TVS_HASLINES;
193     if(nstcs_mask & NSTCS_FULLROWSELECT)       tv_mask |= TVS_FULLROWSELECT;
194     if(nstcs_mask & NSTCS_HORIZONTALSCROLL)    tv_mask |= TVS_NOHSCROLL;
195     if(nstcs_mask & NSTCS_ROOTHASEXPANDO)      tv_mask |= TVS_LINESATROOT;
196     if(nstcs_mask & NSTCS_SHOWSELECTIONALWAYS) tv_mask |= TVS_SHOWSELALWAYS;
197     if(nstcs_mask & NSTCS_NOINFOTIP)           tv_mask |= TVS_INFOTIP;
198     if(nstcs_mask & NSTCS_EVENHEIGHT)          tv_mask |= TVS_NONEVENHEIGHT;
199     if(nstcs_mask & NSTCS_DISABLEDRAGDROP)     tv_mask |= TVS_DISABLEDRAGDROP;
200     if(nstcs_mask & NSTCS_NOEDITLABELS)        tv_mask |= TVS_EDITLABELS;
201     if(nstcs_mask & NSTCS_CHECKBOXES)          tv_mask |= TVS_CHECKBOXES;
202
203     *new_style = 0;
204
205     if(nstcs & NSTCS_HASEXPANDOS)         *new_style |= TVS_HASBUTTONS;
206     if(nstcs & NSTCS_HASLINES)            *new_style |= TVS_HASLINES;
207     if(nstcs & NSTCS_FULLROWSELECT)       *new_style |= TVS_FULLROWSELECT;
208     if(!(nstcs & NSTCS_HORIZONTALSCROLL)) *new_style |= TVS_NOHSCROLL;
209     if(nstcs & NSTCS_ROOTHASEXPANDO)      *new_style |= TVS_LINESATROOT;
210     if(nstcs & NSTCS_SHOWSELECTIONALWAYS) *new_style |= TVS_SHOWSELALWAYS;
211     if(!(nstcs & NSTCS_NOINFOTIP))        *new_style |= TVS_INFOTIP;
212     if(!(nstcs & NSTCS_EVENHEIGHT))       *new_style |= TVS_NONEVENHEIGHT;
213     if(nstcs & NSTCS_DISABLEDRAGDROP)     *new_style |= TVS_DISABLEDRAGDROP;
214     if(!(nstcs & NSTCS_NOEDITLABELS))     *new_style |= TVS_EDITLABELS;
215     if(nstcs & NSTCS_CHECKBOXES)          *new_style |= TVS_CHECKBOXES;
216
217     *new_style = (old_style & ~tv_mask) | (*new_style & tv_mask);
218
219     TRACE("old: %08x, new: %08x\n", old_style, *new_style);
220
221     return old_style^*new_style;
222 }
223
224 static IShellItem *shellitem_from_treeitem(NSTC2Impl *This, HTREEITEM hitem)
225 {
226     TVITEMEXW tvi;
227
228     tvi.mask = TVIF_PARAM;
229     tvi.lParam = 0;
230     tvi.hItem = hitem;
231
232     SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
233
234     TRACE("ShellItem: %p\n", (void*)tvi.lParam);
235     return (IShellItem*)tvi.lParam;
236 }
237
238 /* Returns the root that the given treeitem belongs to. */
239 static nstc_root *root_for_treeitem(NSTC2Impl *This, HTREEITEM hitem)
240 {
241     HTREEITEM tmp, hroot = hitem;
242     nstc_root *root;
243
244     /* Work our way up the hierarchy */
245     for(tmp = hitem; tmp != NULL; hroot = tmp?tmp:hroot)
246         tmp = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hroot);
247
248     /* Search through the list of roots for a match */
249     LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
250         if(root->htreeitem == hroot)
251             break;
252
253     TRACE("root is %p\n", root);
254     return root;
255 }
256
257 /* Find a shellitem in the tree, starting from the given node. */
258 static HTREEITEM search_for_shellitem(NSTC2Impl *This, HTREEITEM node,
259                                       IShellItem *psi)
260 {
261     IShellItem *psi_node;
262     HTREEITEM next, result = NULL;
263     HRESULT hr;
264     int cmpo;
265     TRACE("%p, %p, %p\n", This, node, psi);
266
267     /* Check this node */
268     psi_node = shellitem_from_treeitem(This, node);
269     hr = IShellItem_Compare(psi, psi_node, SICHINT_DISPLAY, &cmpo);
270     if(hr == S_OK)
271         return node;
272
273     /* Any children? */
274     next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
275                                    TVGN_CHILD, (LPARAM)node);
276     if(next)
277     {
278         result = search_for_shellitem(This, next, psi);
279         if(result) return result;
280     }
281
282     /* Try our next sibling. */
283     next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
284                                    TVGN_NEXT, (LPARAM)node);
285     if(next)
286         result = search_for_shellitem(This, next, psi);
287
288     return result;
289 }
290
291 static HTREEITEM treeitem_from_shellitem(NSTC2Impl *This, IShellItem *psi)
292 {
293     HTREEITEM root;
294     TRACE("%p, %p\n", This, psi);
295
296     root = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM,
297                                    TVGN_ROOT, 0);
298     if(!root)
299         return NULL;
300
301     return search_for_shellitem(This, root, psi);
302 }
303
304 static int get_icon(LPCITEMIDLIST lpi, UINT extra_flags)
305 {
306     SHFILEINFOW sfi;
307     UINT flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
308     SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), flags | extra_flags);
309     return sfi.iIcon;
310 }
311
312 /* Insert a shellitem into the given place in the tree and return the
313    resulting treeitem. */
314 static HTREEITEM insert_shellitem(NSTC2Impl *This, IShellItem *psi,
315                                   HTREEITEM hParent, HTREEITEM hInsertAfter)
316 {
317     TVINSERTSTRUCTW tvins;
318     TVITEMEXW *tvi = &tvins.u.itemex;
319     HTREEITEM hinserted;
320     TRACE("%p (%p, %p)\n", psi, hParent, hInsertAfter);
321
322     tvi->mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
323     tvi->cChildren = I_CHILDRENCALLBACK;
324     tvi->iImage = tvi->iSelectedImage = I_IMAGECALLBACK;
325     tvi->pszText = LPSTR_TEXTCALLBACKW;
326
327     /* Every treeitem contains a pointer to the corresponding ShellItem. */
328     tvi->lParam = (LPARAM)psi;
329     tvins.hParent = hParent;
330     tvins.hInsertAfter = hInsertAfter;
331
332     hinserted = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_INSERTITEMW, 0,
333                                         (LPARAM)(LPTVINSERTSTRUCTW)&tvins);
334     if(hinserted)
335         IShellItem_AddRef(psi);
336
337     return hinserted;
338 }
339
340 /* Enumerates the children of the folder represented by hitem
341  * according to the settings for the root, and adds them to the
342  * treeview. Returns the number of children added. */
343 static UINT fill_sublevel(NSTC2Impl *This, HTREEITEM hitem)
344 {
345     IShellItem *psiParent = shellitem_from_treeitem(This, hitem);
346     nstc_root *root = root_for_treeitem(This, hitem);
347     LPITEMIDLIST pidl_parent;
348     IShellFolder *psf;
349     IEnumIDList *peidl;
350     UINT added = 0;
351     HRESULT hr;
352
353     hr = SHGetIDListFromObject((IUnknown*)psiParent, &pidl_parent);
354     if(SUCCEEDED(hr))
355     {
356         hr = IShellItem_BindToHandler(psiParent, NULL, &BHID_SFObject, &IID_IShellFolder, (void**)&psf);
357         if(SUCCEEDED(hr))
358         {
359             hr = IShellFolder_EnumObjects(psf, NULL, root->enum_flags, &peidl);
360             if(SUCCEEDED(hr))
361             {
362                 LPITEMIDLIST pidl;
363                 IShellItem *psi;
364                 ULONG fetched;
365
366                 while( S_OK == IEnumIDList_Next(peidl, 1, &pidl, &fetched) )
367                 {
368                     hr = SHCreateShellItem(NULL, psf , pidl, &psi);
369                     ILFree(pidl);
370                     if(SUCCEEDED(hr))
371                     {
372                         if(insert_shellitem(This, psi, hitem, NULL))
373                         {
374                             events_OnItemAdded(This, psi, FALSE);
375                             added++;
376                         }
377
378                         IShellItem_Release(psi);
379                     }
380                     else
381                         ERR("SHCreateShellItem failed with 0x%08x\n", hr);
382                 }
383                 IEnumIDList_Release(peidl);
384             }
385             else
386                 ERR("EnumObjects failed with 0x%08x\n", hr);
387
388             IShellFolder_Release(psf);
389         }
390         else
391             ERR("BindToHandler failed with 0x%08x\n", hr);
392
393         ILFree(pidl_parent);
394     }
395     else
396         ERR("SHGetIDListFromObject failed with 0x%08x\n", hr);
397
398     return added;
399 }
400
401 static HTREEITEM get_selected_treeitem(NSTC2Impl *This)
402 {
403     return (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CARET, 0);
404 }
405
406 static IShellItem *get_selected_shellitem(NSTC2Impl *This)
407 {
408     return shellitem_from_treeitem(This, get_selected_treeitem(This));
409 }
410
411 static void collapse_all(NSTC2Impl *This, HTREEITEM node)
412 {
413     HTREEITEM next;
414
415     /* Collapse this node first, and then first child/next sibling. */
416     SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_COLLAPSE, (LPARAM)node);
417
418     next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
419     if(next) collapse_all(This, next);
420
421     next = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)node);
422     if(next) collapse_all(This, next);
423 }
424
425 static HTREEITEM treeitem_from_point(NSTC2Impl *This, POINT *pt, UINT *hitflag)
426 {
427     TVHITTESTINFO tviht;
428     tviht.pt.x = pt->x;
429     tviht.pt.y = pt->y;
430     tviht.hItem = NULL;
431
432     SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tviht);
433     if(hitflag) *hitflag = tviht.flags;
434     return tviht.hItem;
435 }
436
437 /*************************************************************************
438  * NamespaceTree window functions
439  */
440 static LRESULT create_namespacetree(HWND hWnd, CREATESTRUCTW *crs)
441 {
442     NSTC2Impl *This = crs->lpCreateParams;
443     HIMAGELIST ShellSmallIconList;
444     DWORD treeview_style, treeview_ex_style;
445
446     TRACE("%p (%p)\n", This, crs);
447     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LPARAM)This);
448     This->hwnd_main = hWnd;
449
450     treeview_style_from_nstcs(This, This->style, 0xFFFFFFFF, &treeview_style);
451
452     This->hwnd_tv = CreateWindowExW(0, WC_TREEVIEWW, NULL, treeview_style,
453                                     0, 0, crs->cx, crs->cy,
454                                     hWnd, NULL, explorerframe_hinstance, NULL);
455
456     if(!This->hwnd_tv)
457     {
458         ERR("Failed to create treeview!\n");
459         return HRESULT_FROM_WIN32(GetLastError());
460     }
461
462     treeview_ex_style = TVS_EX_DRAWIMAGEASYNC | TVS_EX_RICHTOOLTIP |
463         TVS_EX_DOUBLEBUFFER | TVS_EX_NOSINGLECOLLAPSE;
464
465     if(This->style & NSTCS_AUTOHSCROLL)
466         treeview_ex_style |= TVS_EX_AUTOHSCROLL;
467     if(This->style & NSTCS_FADEINOUTEXPANDOS)
468         treeview_ex_style |= TVS_EX_FADEINOUTEXPANDOS;
469     if(This->style & NSTCS_PARTIALCHECKBOXES)
470         treeview_ex_style |= TVS_EX_PARTIALCHECKBOXES;
471     if(This->style & NSTCS_EXCLUSIONCHECKBOXES)
472         treeview_ex_style |= TVS_EX_EXCLUSIONCHECKBOXES;
473     if(This->style & NSTCS_DIMMEDCHECKBOXES)
474         treeview_ex_style |= TVS_EX_DIMMEDCHECKBOXES;
475
476     SendMessageW(This->hwnd_tv, TVM_SETEXTENDEDSTYLE, treeview_ex_style, 0xffff);
477
478     if(Shell_GetImageLists(NULL, &ShellSmallIconList))
479     {
480         SendMessageW(This->hwnd_tv, TVM_SETIMAGELIST,
481                      (WPARAM)TVSIL_NORMAL, (LPARAM)ShellSmallIconList);
482     }
483     else
484     {
485         ERR("Failed to get the System Image List.\n");
486     }
487
488     INameSpaceTreeControl_AddRef((INameSpaceTreeControl*)This);
489
490     /* Subclass the treeview to get the keybord events. */
491     This->tv_oldwndproc = (WNDPROC)SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC,
492                                                      (ULONG_PTR)tv_wndproc);
493     if(This->tv_oldwndproc)
494         SetPropA(This->hwnd_tv, "PROP_THIS", This);
495
496     return TRUE;
497 }
498
499 static LRESULT resize_namespacetree(NSTC2Impl *This)
500 {
501     RECT rc;
502     TRACE("%p\n", This);
503
504     GetClientRect(This->hwnd_main, &rc);
505     MoveWindow(This->hwnd_tv, 0, 0, rc.right-rc.left, rc.bottom-rc.top, TRUE);
506
507     return TRUE;
508 }
509
510 static LRESULT destroy_namespacetree(NSTC2Impl *This)
511 {
512     TRACE("%p\n", This);
513
514     /* Undo the subclassing */
515     if(This->tv_oldwndproc)
516     {
517         SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC, (ULONG_PTR)This->tv_oldwndproc);
518         RemovePropA(This->hwnd_tv, "PROP_THIS");
519     }
520
521     INameSpaceTreeControl_RemoveAllRoots((INameSpaceTreeControl*)This);
522
523     /* This reference was added in create_namespacetree */
524     INameSpaceTreeControl_Release((INameSpaceTreeControl*)This);
525     return TRUE;
526 }
527
528 static LRESULT on_tvn_deleteitemw(NSTC2Impl *This, LPARAM lParam)
529 {
530     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
531     TRACE("%p\n", This);
532
533     IShellItem_Release((IShellItem*)nmtv->itemOld.lParam);
534     return TRUE;
535 }
536
537 static LRESULT on_tvn_getdispinfow(NSTC2Impl *This, LPARAM lParam)
538 {
539     NMTVDISPINFOW *dispinfo = (NMTVDISPINFOW*)lParam;
540     TVITEMEXW *item = (TVITEMEXW*)&dispinfo->item;
541     IShellItem *psi = shellitem_from_treeitem(This, item->hItem);
542     HRESULT hr;
543
544     TRACE("%p, %p (mask: %x)\n", This, dispinfo, item->mask);
545
546     if(item->mask & TVIF_CHILDREN)
547     {
548         SFGAOF sfgao;
549
550         hr = IShellItem_GetAttributes(psi, SFGAO_HASSUBFOLDER, &sfgao);
551         if(SUCCEEDED(hr))
552             item->cChildren = (sfgao & SFGAO_HASSUBFOLDER)?1:0;
553         else
554             item->cChildren = 1;
555
556         item->mask |= TVIF_DI_SETITEM;
557     }
558
559     if(item->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE))
560     {
561         LPITEMIDLIST pidl;
562
563         hr = events_OnGetDefaultIconIndex(This, psi, &item->iImage, &item->iSelectedImage);
564         if(FAILED(hr))
565         {
566             hr = SHGetIDListFromObject((IUnknown*)psi, &pidl);
567             if(SUCCEEDED(hr))
568             {
569                 item->iImage = item->iSelectedImage = get_icon(pidl, 0);
570                 item->mask |= TVIF_DI_SETITEM;
571                 ILFree(pidl);
572             }
573             else
574                 ERR("Failed to get IDList (%08x).\n", hr);
575         }
576     }
577
578     if(item->mask & TVIF_TEXT)
579     {
580         LPWSTR display_name;
581
582         hr = IShellItem_GetDisplayName(psi, SIGDN_NORMALDISPLAY, &display_name);
583         if(SUCCEEDED(hr))
584         {
585             lstrcpynW(item->pszText, display_name, MAX_PATH);
586             item->mask |= TVIF_DI_SETITEM;
587             CoTaskMemFree(display_name);
588         }
589         else
590             ERR("Failed to get display name (%08x).\n", hr);
591     }
592
593     return TRUE;
594 }
595
596 static BOOL treenode_has_subfolders(NSTC2Impl *This, HTREEITEM node)
597 {
598     return SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
599 }
600
601 static LRESULT on_tvn_itemexpandingw(NSTC2Impl *This, LPARAM lParam)
602 {
603     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
604     IShellItem *psi;
605     TRACE("%p\n", This);
606
607     psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
608     events_OnBeforeExpand(This, psi);
609
610     if(!treenode_has_subfolders(This, nmtv->itemNew.hItem))
611     {
612         /* The node has no children, try to find some */
613         if(!fill_sublevel(This, nmtv->itemNew.hItem))
614         {
615             TVITEMEXW tvi;
616             /* Failed to enumerate any children, remove the expando
617              * (if any). */
618             tvi.hItem = nmtv->itemNew.hItem;
619             tvi.mask = TVIF_CHILDREN;
620             tvi.cChildren = 0;
621             SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
622
623             return TRUE;
624         }
625     }
626     return FALSE;
627 }
628
629 static LRESULT on_tvn_itemexpandedw(NSTC2Impl *This, LPARAM lParam)
630 {
631     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
632     IShellItem *psi;
633     TRACE("%p\n", This);
634
635     psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
636     events_OnAfterExpand(This, psi);
637     return TRUE;
638 }
639
640 static LRESULT on_tvn_selchangedw(NSTC2Impl *This, LPARAM lParam)
641 {
642     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
643     IShellItemArray *psia;
644     IShellItem *psi;
645     HRESULT hr;
646     TRACE("%p\n", This);
647
648     /* Note: Only supports one selected item. */
649     psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
650     hr = SHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, (void**)&psia);
651     if(SUCCEEDED(hr))
652     {
653         events_OnSelectionChanged(This, psia);
654         IShellItemArray_Release(psia);
655     }
656
657     return TRUE;
658 }
659
660 static LRESULT on_nm_click(NSTC2Impl *This, NMHDR *nmhdr)
661 {
662     TVHITTESTINFO tvhit;
663     IShellItem *psi;
664     HRESULT hr;
665     TRACE("%p (%p)\n", This, nmhdr);
666
667     GetCursorPos(&tvhit.pt);
668     ScreenToClient(This->hwnd_tv, &tvhit.pt);
669     SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
670
671     if(tvhit.flags & (TVHT_NOWHERE|TVHT_ABOVE|TVHT_BELOW))
672         return TRUE;
673
674     /* TVHT_ maps onto the corresponding NSTCEHT_ */
675     psi = shellitem_from_treeitem(This, tvhit.hItem);
676     hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_LBUTTON);
677
678     /* The expando should not be expanded unless
679      * double-clicked. */
680     if(tvhit.flags == TVHT_ONITEMBUTTON)
681         return TRUE;
682
683     if(SUCCEEDED(hr))
684         return FALSE;
685     else
686         return TRUE;
687 }
688
689 static LRESULT on_wm_mbuttonup(NSTC2Impl *This, WPARAM wParam, LPARAM lParam)
690 {
691     TVHITTESTINFO tvhit;
692     IShellItem *psi;
693     HRESULT hr;
694     TRACE("%p (%lx, %lx)\n", This, wParam, lParam);
695
696     tvhit.pt.x = (int)(short)LOWORD(lParam);
697     tvhit.pt.y = (int)(short)HIWORD(lParam);
698
699     SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
700
701     /* Seems to generate only ONITEM and ONITEMICON */
702     if( !(tvhit.flags & (TVHT_ONITEM|TVHT_ONITEMICON)) )
703         return FALSE;
704
705     psi = shellitem_from_treeitem(This, tvhit.hItem);
706     hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_MBUTTON);
707
708     if(SUCCEEDED(hr))
709         return FALSE;
710     else
711         return TRUE;
712 }
713
714 static LRESULT on_kbd_event(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
715 {
716     IShellItem *psi;
717     HTREEITEM hitem;
718     TRACE("%p : %d, %lx, %lx\n", This, uMsg, wParam, lParam);
719
720     /* Handled by the client? */
721     if(FAILED(events_OnKeyboardInput(This, uMsg, wParam, lParam)))
722         return TRUE;
723
724     if(uMsg == WM_KEYDOWN)
725     {
726         switch(wParam)
727         {
728         case VK_DELETE:
729             psi = get_selected_shellitem(This);
730             FIXME("Deletion of file requested (shellitem: %p).\n", psi);
731             return TRUE;
732
733         case VK_F2:
734             hitem = get_selected_treeitem(This);
735             SendMessageW(This->hwnd_tv, TVM_EDITLABELW, 0, (LPARAM)hitem);
736             return TRUE;
737         }
738     }
739
740     /* Let the TreeView handle the key */
741     return FALSE;
742 }
743
744 static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
745 {
746     NSTC2Impl *This = (NSTC2Impl*)GetPropA(hWnd, "PROP_THIS");
747
748     switch(uMessage) {
749     case WM_KEYDOWN:
750     case WM_KEYUP:
751     case WM_CHAR:
752     case WM_SYSKEYDOWN:
753     case WM_SYSKEYUP:
754     case WM_SYSCHAR:
755         if(on_kbd_event(This, uMessage, wParam, lParam))
756             return TRUE;
757         break;
758
759     case WM_MBUTTONUP:        return on_wm_mbuttonup(This, wParam, lParam);
760     }
761
762     /* Pass the message on to the treeview */
763     return CallWindowProcW(This->tv_oldwndproc, hWnd, uMessage, wParam, lParam);
764 }
765
766 static LRESULT CALLBACK NSTC2_WndProc(HWND hWnd, UINT uMessage,
767                                       WPARAM wParam, LPARAM lParam)
768 {
769     NSTC2Impl *This = (NSTC2Impl*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
770     NMHDR *nmhdr;
771
772     switch(uMessage)
773     {
774     case WM_NCCREATE:         return create_namespacetree(hWnd, (CREATESTRUCTW*)lParam);
775     case WM_SIZE:             return resize_namespacetree(This);
776     case WM_DESTROY:          return destroy_namespacetree(This);
777     case WM_NOTIFY:
778         nmhdr = (NMHDR*)lParam;
779         switch(nmhdr->code)
780         {
781         case NM_CLICK:            return on_nm_click(This, nmhdr);
782         case TVN_DELETEITEMW:     return on_tvn_deleteitemw(This, lParam);
783         case TVN_GETDISPINFOW:    return on_tvn_getdispinfow(This, lParam);
784         case TVN_ITEMEXPANDINGW:  return on_tvn_itemexpandingw(This, lParam);
785         case TVN_ITEMEXPANDEDW:   return on_tvn_itemexpandedw(This, lParam);
786         case TVN_SELCHANGEDW:     return on_tvn_selchangedw(This, lParam);
787         default:                  break;
788         }
789         break;
790     default:                  return DefWindowProcW(hWnd, uMessage, wParam, lParam);
791     }
792     return 0;
793 }
794
795 /**************************************************************************
796  * INameSpaceTreeControl2 Implementation
797  */
798 static HRESULT WINAPI NSTC2_fnQueryInterface(INameSpaceTreeControl2* iface,
799                                              REFIID riid,
800                                              void **ppvObject)
801 {
802     NSTC2Impl *This = (NSTC2Impl*)iface;
803     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
804
805     *ppvObject = NULL;
806     if(IsEqualIID(riid, &IID_INameSpaceTreeControl2) ||
807        IsEqualIID(riid, &IID_INameSpaceTreeControl) ||
808        IsEqualIID(riid, &IID_IUnknown))
809     {
810         *ppvObject = This;
811     }
812     else if(IsEqualIID(riid, &IID_IOleWindow))
813     {
814         *ppvObject = &This->lpowVtbl;
815     }
816
817     if(*ppvObject)
818     {
819         IUnknown_AddRef((IUnknown*)*ppvObject);
820         return S_OK;
821     }
822
823     return E_NOINTERFACE;
824 }
825
826 static ULONG WINAPI NSTC2_fnAddRef(INameSpaceTreeControl2* iface)
827 {
828     NSTC2Impl *This = (NSTC2Impl*)iface;
829     LONG ref = InterlockedIncrement(&This->ref);
830
831     TRACE("%p - ref %d\n", This, ref);
832
833     return ref;
834 }
835
836 static ULONG WINAPI NSTC2_fnRelease(INameSpaceTreeControl2* iface)
837 {
838     NSTC2Impl *This = (NSTC2Impl*)iface;
839     LONG ref = InterlockedDecrement(&This->ref);
840
841     TRACE("%p - ref: %d\n", This, ref);
842
843     if(!ref)
844     {
845         TRACE("Freeing.\n");
846         HeapFree(GetProcessHeap(), 0, This);
847         EFRAME_UnlockModule();
848         return 0;
849     }
850
851     return ref;
852 }
853
854 static HRESULT WINAPI NSTC2_fnInitialize(INameSpaceTreeControl2* iface,
855                                          HWND hwndParent,
856                                          RECT *prc,
857                                          NSTCSTYLE nstcsFlags)
858 {
859     NSTC2Impl *This = (NSTC2Impl*)iface;
860     WNDCLASSW wc;
861     DWORD window_style, window_ex_style;
862     RECT rc;
863     static const WCHAR NSTC2_CLASS_NAME[] =
864         {'N','a','m','e','s','p','a','c','e','T','r','e','e',
865          'C','o','n','t','r','o','l',0};
866
867     TRACE("%p (%p, %p, %x)\n", This, hwndParent, prc, nstcsFlags);
868
869     if(nstcsFlags & unsupported_styles)
870         FIXME("0x%08x contains the unsupported style(s) 0x%08x\n",
871               nstcsFlags, nstcsFlags & unsupported_styles);
872
873     This->style = nstcsFlags;
874
875     if(!GetClassInfoW(explorerframe_hinstance, NSTC2_CLASS_NAME, &wc))
876     {
877         wc.style            = CS_HREDRAW | CS_VREDRAW;
878         wc.lpfnWndProc      = NSTC2_WndProc;
879         wc.cbClsExtra       = 0;
880         wc.cbWndExtra       = 0;
881         wc.hInstance        = explorerframe_hinstance;
882         wc.hIcon            = 0;
883         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
884         wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW + 1);
885         wc.lpszMenuName     = NULL;
886         wc.lpszClassName    = NSTC2_CLASS_NAME;
887
888         if (!RegisterClassW(&wc)) return E_FAIL;
889     }
890
891     /* NSTCS_TABSTOP and NSTCS_BORDER affects the host window */
892     window_style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
893         (nstcsFlags & NSTCS_BORDER ? WS_BORDER : 0);
894     window_ex_style = nstcsFlags & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
895
896     if(prc)
897         CopyRect(&rc, prc);
898     else
899         rc.left = rc.right = rc.top = rc.bottom = 0;
900
901     This->hwnd_main = CreateWindowExW(window_ex_style, NSTC2_CLASS_NAME, NULL, window_style,
902                                       rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
903                                       hwndParent, 0, explorerframe_hinstance, This);
904
905     if(!This->hwnd_main)
906     {
907         ERR("Failed to create the window.\n");
908         return HRESULT_FROM_WIN32(GetLastError());
909     }
910
911     return S_OK;
912 }
913
914 static HRESULT WINAPI NSTC2_fnTreeAdvise(INameSpaceTreeControl2* iface,
915                                          IUnknown *punk,
916                                          DWORD *pdwCookie)
917 {
918     NSTC2Impl *This = (NSTC2Impl*)iface;
919     HRESULT hr;
920     TRACE("%p (%p, %p)\n", This, punk, pdwCookie);
921
922     *pdwCookie = 0;
923
924     /* Only one client supported */
925     if(This->pnstce)
926         return E_FAIL;
927
928     hr = IUnknown_QueryInterface(punk, &IID_INameSpaceTreeControlEvents,(void**)&This->pnstce);
929     if(SUCCEEDED(hr))
930     {
931         *pdwCookie = 1;
932         return hr;
933     }
934
935     return E_FAIL;
936 }
937
938 static HRESULT WINAPI NSTC2_fnTreeUnadvise(INameSpaceTreeControl2* iface,
939                                            DWORD dwCookie)
940 {
941     NSTC2Impl *This = (NSTC2Impl*)iface;
942     TRACE("%p (%x)\n", This, dwCookie);
943
944     /* The cookie is ignored. */
945
946     if(This->pnstce)
947     {
948         INameSpaceTreeControlEvents_Release(This->pnstce);
949         This->pnstce = NULL;
950     }
951
952     return S_OK;
953 }
954
955 static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface,
956                                          int iIndex,
957                                          IShellItem *psiRoot,
958                                          SHCONTF grfEnumFlags,
959                                          NSTCROOTSTYLE grfRootStyle,
960                                          IShellItemFilter *pif)
961 {
962     NSTC2Impl *This = (NSTC2Impl*)iface;
963     nstc_root *new_root;
964     struct list *add_after_entry;
965     HTREEITEM add_after_hitem;
966     UINT i;
967
968     TRACE("%p, %d, %p, %x, %x, %p\n", This, iIndex, psiRoot, grfEnumFlags, grfRootStyle, pif);
969
970     new_root = HeapAlloc(GetProcessHeap(), 0, sizeof(nstc_root));
971     if(!new_root)
972         return E_OUTOFMEMORY;
973
974     new_root->psi = psiRoot;
975     new_root->enum_flags = grfEnumFlags;
976     new_root->root_style = grfRootStyle;
977     new_root->pif = pif;
978
979     /* We want to keep the roots in the internal list and in the
980      * treeview in the same order. */
981     add_after_entry = &This->roots;
982     for(i = 0; i < max(0, iIndex) && list_next(&This->roots, add_after_entry); i++)
983         add_after_entry = list_next(&This->roots, add_after_entry);
984
985     if(add_after_entry == &This->roots)
986         add_after_hitem = TVI_FIRST;
987     else
988         add_after_hitem = LIST_ENTRY(add_after_entry, nstc_root, entry)->htreeitem;
989
990     new_root->htreeitem = insert_shellitem(This, psiRoot, TVI_ROOT, add_after_hitem);
991     if(!new_root->htreeitem)
992     {
993         WARN("Failed to add the root.\n");
994         HeapFree(GetProcessHeap(), 0, new_root);
995         return E_FAIL;
996     }
997
998     list_add_after(add_after_entry, &new_root->entry);
999     events_OnItemAdded(This, psiRoot, TRUE);
1000
1001     if(grfRootStyle & NSTCRS_HIDDEN)
1002     {
1003         TVITEMEXW tvi;
1004         tvi.mask = TVIF_STATEEX;
1005         tvi.uStateEx = TVIS_EX_FLAT;
1006         tvi.hItem = new_root->htreeitem;
1007
1008         SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
1009     }
1010
1011     if(grfRootStyle & NSTCRS_EXPANDED)
1012         SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_EXPAND,
1013                      (LPARAM)new_root->htreeitem);
1014
1015     return S_OK;
1016 }
1017
1018 static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface,
1019                                          IShellItem *psiRoot,
1020                                          SHCONTF grfEnumFlags,
1021                                          NSTCROOTSTYLE grfRootStyle,
1022                                          IShellItemFilter *pif)
1023 {
1024     NSTC2Impl *This = (NSTC2Impl*)iface;
1025     UINT root_count;
1026     TRACE("%p, %p, %x, %x, %p\n",
1027           This, psiRoot, grfEnumFlags, grfRootStyle, pif);
1028
1029     root_count = list_count(&This->roots);
1030
1031     return NSTC2_fnInsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif);
1032 }
1033
1034 static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface,
1035                                          IShellItem *psiRoot)
1036 {
1037     NSTC2Impl *This = (NSTC2Impl*)iface;
1038     nstc_root *cursor, *root = NULL;
1039     TRACE("%p (%p)\n", This, psiRoot);
1040
1041     if(!psiRoot)
1042         return E_NOINTERFACE;
1043
1044     LIST_FOR_EACH_ENTRY(cursor, &This->roots, nstc_root, entry)
1045     {
1046         HRESULT hr;
1047         int order;
1048         hr = IShellItem_Compare(psiRoot, cursor->psi, SICHINT_DISPLAY, &order);
1049         if(hr == S_OK)
1050         {
1051             root = cursor;
1052             break;
1053         }
1054     }
1055
1056     TRACE("root %p\n", root);
1057     if(root)
1058     {
1059         events_OnItemDeleted(This, root->psi, TRUE);
1060         SendMessageW(This->hwnd_tv, TVM_DELETEITEM, 0, (LPARAM)root->htreeitem);
1061         list_remove(&root->entry);
1062         HeapFree(GetProcessHeap(), 0, root);
1063         return S_OK;
1064     }
1065     else
1066     {
1067         WARN("No matching root found.\n");
1068         return E_FAIL;
1069     }
1070 }
1071
1072 static HRESULT WINAPI NSTC2_fnRemoveAllRoots(INameSpaceTreeControl2* iface)
1073 {
1074     NSTC2Impl *This = (NSTC2Impl*)iface;
1075     nstc_root *cur1, *cur2;
1076     UINT removed = 0;
1077     TRACE("%p\n", This);
1078
1079     LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->roots, nstc_root, entry)
1080     {
1081         NSTC2_fnRemoveRoot(iface, cur1->psi);
1082         removed++;
1083     }
1084
1085     if(removed)
1086         return S_OK;
1087     else
1088         return E_INVALIDARG;
1089 }
1090
1091 static HRESULT WINAPI NSTC2_fnGetRootItems(INameSpaceTreeControl2* iface,
1092                                            IShellItemArray **ppsiaRootItems)
1093 {
1094     NSTC2Impl *This = (NSTC2Impl*)iface;
1095     IShellFolder *psf;
1096     LPITEMIDLIST *array;
1097     nstc_root *root;
1098     UINT count, i;
1099     HRESULT hr;
1100     TRACE("%p (%p)\n", This, ppsiaRootItems);
1101
1102     count = list_count(&This->roots);
1103
1104     if(!count)
1105         return E_INVALIDARG;
1106
1107     array = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST*)*count);
1108
1109     i = 0;
1110     LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
1111         SHGetIDListFromObject((IUnknown*)root->psi, &array[i++]);
1112
1113     SHGetDesktopFolder(&psf);
1114     hr = SHCreateShellItemArray(NULL, psf, count, (PCUITEMID_CHILD_ARRAY)array,
1115                                 ppsiaRootItems);
1116     IShellFolder_Release(psf);
1117
1118     for(i = 0; i < count; i++)
1119         ILFree(array[i]);
1120
1121     HeapFree(GetProcessHeap(), 0, array);
1122
1123     return hr;
1124 }
1125
1126 static HRESULT WINAPI NSTC2_fnSetItemState(INameSpaceTreeControl2* iface,
1127                                            IShellItem *psi,
1128                                            NSTCITEMSTATE nstcisMask,
1129                                            NSTCITEMSTATE nstcisFlags)
1130 {
1131     NSTC2Impl *This = (NSTC2Impl*)iface;
1132     TVITEMEXW tvi;
1133     HTREEITEM hitem;
1134
1135     TRACE("%p (%p, %x, %x)\n", This, psi, nstcisMask, nstcisFlags);
1136
1137     hitem = treeitem_from_shellitem(This, psi);
1138     if(!hitem) return E_INVALIDARG;
1139
1140     /* Passing both NSTCIS_SELECTED and NSTCIS_SELECTEDNOEXPAND results
1141        in two TVM_SETITEMW's */
1142     if((nstcisMask&nstcisFlags) & NSTCIS_SELECTED)
1143     {
1144         SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hitem);
1145         SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (LPARAM)hitem);
1146     }
1147     if((nstcisMask&nstcisFlags) & NSTCIS_SELECTEDNOEXPAND)
1148     {
1149         SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET|TVSI_NOSINGLEEXPAND, (LPARAM)hitem);
1150     }
1151
1152     /* If NSTCIS_EXPANDED is among the flags, the mask is ignored. */
1153     if((nstcisMask|nstcisFlags) & NSTCIS_EXPANDED)
1154     {
1155         WPARAM arg = nstcisFlags&NSTCIS_EXPANDED ? TVE_EXPAND:TVE_COLLAPSE;
1156         SendMessageW(This->hwnd_tv, TVM_EXPAND, arg, (LPARAM)hitem);
1157     }
1158
1159     if(nstcisMask & NSTCIS_DISABLED)
1160         tvi.mask = TVIF_STATE | TVIF_STATEEX;
1161     else if( ((nstcisMask^nstcisFlags) & (NSTCIS_SELECTED|NSTCIS_EXPANDED|NSTCIS_SELECTEDNOEXPAND)) ||
1162              ((nstcisMask|nstcisFlags) & NSTCIS_BOLD) ||
1163              (nstcisFlags & NSTCIS_DISABLED) )
1164         tvi.mask = TVIF_STATE;
1165     else
1166         tvi.mask = 0;
1167
1168     if(tvi.mask)
1169     {
1170         tvi.stateMask = tvi.state = 0;
1171         tvi.stateMask |= ((nstcisFlags^nstcisMask)&NSTCIS_SELECTED) ? TVIS_SELECTED : 0;
1172         tvi.stateMask |= (nstcisMask|nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
1173         tvi.state     |= (nstcisMask&nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
1174
1175         if((nstcisMask&NSTCIS_EXPANDED)^(nstcisFlags&NSTCIS_EXPANDED))
1176         {
1177             tvi.stateMask = 0;
1178         }
1179
1180         tvi.uStateEx = (nstcisFlags&nstcisMask)&NSTCIS_DISABLED?TVIS_EX_DISABLED:0;
1181         tvi.hItem = hitem;
1182
1183         SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
1184     }
1185
1186     return S_OK;
1187 }
1188
1189 static HRESULT WINAPI NSTC2_fnGetItemState(INameSpaceTreeControl2* iface,
1190                                            IShellItem *psi,
1191                                            NSTCITEMSTATE nstcisMask,
1192                                            NSTCITEMSTATE *pnstcisFlags)
1193 {
1194     NSTC2Impl *This = (NSTC2Impl*)iface;
1195     HTREEITEM hitem;
1196     TVITEMEXW tvi;
1197     TRACE("%p (%p, %x, %p)\n", This, psi, nstcisMask, pnstcisFlags);
1198
1199     hitem = treeitem_from_shellitem(This, psi);
1200     if(!hitem)
1201         return E_INVALIDARG;
1202
1203     *pnstcisFlags = 0;
1204
1205     tvi.hItem = hitem;
1206     tvi.mask = TVIF_STATE;
1207     tvi.stateMask = TVIS_SELECTED|TVIS_EXPANDED|TVIS_BOLD;
1208
1209     if(nstcisMask & NSTCIS_DISABLED)
1210         tvi.mask |= TVIF_STATEEX;
1211
1212     SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
1213     *pnstcisFlags |= (tvi.state & TVIS_SELECTED)?NSTCIS_SELECTED:0;
1214     *pnstcisFlags |= (tvi.state & TVIS_EXPANDED)?NSTCIS_EXPANDED:0;
1215     *pnstcisFlags |= (tvi.state & TVIS_BOLD)?NSTCIS_BOLD:0;
1216     *pnstcisFlags |= (tvi.uStateEx & TVIS_EX_DISABLED)?NSTCIS_DISABLED:0;
1217
1218     *pnstcisFlags &= nstcisMask;
1219
1220     return S_OK;
1221 }
1222
1223 static HRESULT WINAPI NSTC2_fnGetSelectedItems(INameSpaceTreeControl2* iface,
1224                                                IShellItemArray **psiaItems)
1225 {
1226     NSTC2Impl *This = (NSTC2Impl*)iface;
1227     IShellItem *psiselected;
1228     HRESULT hr;
1229     TRACE("%p (%p)\n", This, psiaItems);
1230
1231     psiselected = get_selected_shellitem(This);
1232     if(!psiselected)
1233         return E_FAIL;
1234
1235     hr = SHCreateShellItemArrayFromShellItem(psiselected, &IID_IShellItemArray,
1236                                              (void**)psiaItems);
1237     return hr;
1238 }
1239
1240 static HRESULT WINAPI NSTC2_fnGetItemCustomState(INameSpaceTreeControl2* iface,
1241                                                  IShellItem *psi,
1242                                                  int *piStateNumber)
1243 {
1244     NSTC2Impl *This = (NSTC2Impl*)iface;
1245     FIXME("stub, %p (%p, %p)\n", This, psi, piStateNumber);
1246     return E_NOTIMPL;
1247 }
1248
1249 static HRESULT WINAPI NSTC2_fnSetItemCustomState(INameSpaceTreeControl2* iface,
1250                                                  IShellItem *psi,
1251                                                  int iStateNumber)
1252 {
1253     NSTC2Impl *This = (NSTC2Impl*)iface;
1254     FIXME("stub, %p (%p, %d)\n", This, psi, iStateNumber);
1255     return E_NOTIMPL;
1256 }
1257
1258 static HRESULT WINAPI NSTC2_fnEnsureItemVisible(INameSpaceTreeControl2* iface,
1259                                                 IShellItem *psi)
1260 {
1261     NSTC2Impl *This = (NSTC2Impl*)iface;
1262     HTREEITEM hitem;
1263
1264     TRACE("%p (%p)\n", This, psi);
1265
1266     hitem = treeitem_from_shellitem(This, psi);
1267     if(hitem)
1268     {
1269         SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (WPARAM)hitem);
1270         return S_OK;
1271     }
1272
1273     return E_INVALIDARG;
1274 }
1275
1276 static HRESULT WINAPI NSTC2_fnSetTheme(INameSpaceTreeControl2* iface,
1277                                        LPCWSTR pszTheme)
1278 {
1279     NSTC2Impl *This = (NSTC2Impl*)iface;
1280     FIXME("stub, %p (%p)\n", This, pszTheme);
1281     return E_NOTIMPL;
1282 }
1283
1284 static HRESULT WINAPI NSTC2_fnGetNextItem(INameSpaceTreeControl2* iface,
1285                                           IShellItem *psi,
1286                                           NSTCGNI nstcgi,
1287                                           IShellItem **ppsiNext)
1288 {
1289     NSTC2Impl *This = (NSTC2Impl*)iface;
1290     HTREEITEM hitem, hnext;
1291     UINT tvgn;
1292     TRACE("%p (%p, %x, %p)\n", This, psi, nstcgi, ppsiNext);
1293
1294     if(!ppsiNext) return E_POINTER;
1295     if(!psi)      return E_FAIL;
1296
1297     *ppsiNext = NULL;
1298
1299     hitem = treeitem_from_shellitem(This, psi);
1300     if(!hitem)
1301         return E_INVALIDARG;
1302
1303     switch(nstcgi)
1304     {
1305     case NSTCGNI_NEXT:         tvgn = TVGN_NEXT; break;
1306     case NSTCGNI_NEXTVISIBLE:  tvgn = TVGN_NEXTVISIBLE; break;
1307     case NSTCGNI_PREV:         tvgn = TVGN_PREVIOUS; break;
1308     case NSTCGNI_PREVVISIBLE:  tvgn = TVGN_PREVIOUSVISIBLE; break;
1309     case NSTCGNI_PARENT:       tvgn = TVGN_PARENT; break;
1310     case NSTCGNI_CHILD:        tvgn = TVGN_CHILD; break;
1311     case NSTCGNI_FIRSTVISIBLE: tvgn = TVGN_FIRSTVISIBLE; break;
1312     case NSTCGNI_LASTVISIBLE:  tvgn = TVGN_LASTVISIBLE; break;
1313     default:
1314         FIXME("Unknown nstcgi value %d\n", nstcgi);
1315         return E_FAIL;
1316     }
1317
1318     hnext = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, tvgn, (WPARAM)hitem);
1319     if(hnext)
1320     {
1321         *ppsiNext = shellitem_from_treeitem(This, hnext);
1322         IShellItem_AddRef(*ppsiNext);
1323         return S_OK;
1324     }
1325
1326     return E_FAIL;
1327 }
1328
1329 static HRESULT WINAPI NSTC2_fnHitTest(INameSpaceTreeControl2* iface,
1330                                       POINT *ppt,
1331                                       IShellItem **ppsiOut)
1332 {
1333     NSTC2Impl *This = (NSTC2Impl*)iface;
1334     HTREEITEM hitem;
1335     TRACE("%p (%p, %p)\n", This, ppsiOut, ppt);
1336
1337     if(!ppt || !ppsiOut)
1338         return E_POINTER;
1339
1340     *ppsiOut = NULL;
1341
1342     hitem = treeitem_from_point(This, ppt, NULL);
1343     if(hitem)
1344         *ppsiOut = shellitem_from_treeitem(This, hitem);
1345
1346     if(*ppsiOut)
1347     {
1348         IShellItem_AddRef(*ppsiOut);
1349         return S_OK;
1350     }
1351
1352     return S_FALSE;
1353 }
1354
1355 static HRESULT WINAPI NSTC2_fnGetItemRect(INameSpaceTreeControl2* iface,
1356                                           IShellItem *psi,
1357                                           RECT *prect)
1358 {
1359     NSTC2Impl *This = (NSTC2Impl*)iface;
1360     HTREEITEM hitem;
1361     TRACE("%p (%p, %p)\n", This, psi, prect);
1362
1363     if(!psi || !prect)
1364         return E_POINTER;
1365
1366     hitem = treeitem_from_shellitem(This, psi);
1367     if(hitem)
1368     {
1369         *(HTREEITEM*)prect = hitem;
1370         if(SendMessageW(This->hwnd_tv, TVM_GETITEMRECT, FALSE, (LPARAM)prect))
1371         {
1372             MapWindowPoints(This->hwnd_tv, HWND_DESKTOP, (POINT*)prect, 2);
1373             return S_OK;
1374         }
1375     }
1376
1377     return E_INVALIDARG;
1378 }
1379
1380 static HRESULT WINAPI NSTC2_fnCollapseAll(INameSpaceTreeControl2* iface)
1381 {
1382     NSTC2Impl *This = (NSTC2Impl*)iface;
1383     nstc_root *root;
1384     TRACE("%p\n", This);
1385
1386     LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
1387         collapse_all(This, root->htreeitem);
1388
1389     return S_OK;
1390 }
1391
1392 static HRESULT WINAPI NSTC2_fnSetControlStyle(INameSpaceTreeControl2* iface,
1393                                               NSTCSTYLE nstcsMask,
1394                                               NSTCSTYLE nstcsStyle)
1395 {
1396     NSTC2Impl *This = (NSTC2Impl*)iface;
1397     static const DWORD tv_style_flags =
1398         NSTCS_HASEXPANDOS | NSTCS_HASLINES | NSTCS_FULLROWSELECT |
1399         NSTCS_HORIZONTALSCROLL | NSTCS_ROOTHASEXPANDO |
1400         NSTCS_SHOWSELECTIONALWAYS | NSTCS_NOINFOTIP | NSTCS_EVENHEIGHT |
1401         NSTCS_DISABLEDRAGDROP | NSTCS_NOEDITLABELS | NSTCS_CHECKBOXES;
1402     static const DWORD host_style_flags = NSTCS_TABSTOP | NSTCS_BORDER;
1403     static const DWORD nstc_flags =
1404         NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM |
1405         NSTCS_FAVORITESMODE | NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS |
1406         NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON | NSTCS_SHOWREFRESHBUTTON;
1407     TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
1408
1409     /* Fail if there is an attempt to set an unknown style. */
1410     if(nstcsMask & ~(tv_style_flags | host_style_flags | nstc_flags))
1411         return E_FAIL;
1412
1413     if(nstcsMask & tv_style_flags)
1414     {
1415         DWORD new_style;
1416         treeview_style_from_nstcs(This, nstcsStyle, nstcsMask, &new_style);
1417         SetWindowLongPtrW(This->hwnd_tv, GWL_STYLE, new_style);
1418     }
1419
1420     /* Flags affecting the host window */
1421     if(nstcsMask & NSTCS_BORDER)
1422     {
1423         DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_STYLE);
1424         new_style &= ~WS_BORDER;
1425         new_style |= nstcsStyle & NSTCS_BORDER ? WS_BORDER : 0;
1426         SetWindowLongPtrW(This->hwnd_main, GWL_STYLE, new_style);
1427     }
1428     if(nstcsMask & NSTCS_TABSTOP)
1429     {
1430         DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE);
1431         new_style &= ~WS_EX_CONTROLPARENT;
1432         new_style |= nstcsStyle & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
1433         SetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE, new_style);
1434     }
1435
1436     if((nstcsStyle & nstcsMask) & unsupported_styles)
1437         FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
1438               (nstcsStyle & nstcsMask),
1439               (nstcsStyle & nstcsMask) & unsupported_styles);
1440
1441     This->style &= ~nstcsMask;
1442     This->style |= (nstcsStyle & nstcsMask);
1443
1444     return S_OK;
1445 }
1446
1447 static HRESULT WINAPI NSTC2_fnGetControlStyle(INameSpaceTreeControl2* iface,
1448                                               NSTCSTYLE nstcsMask,
1449                                               NSTCSTYLE *pnstcsStyle)
1450 {
1451     NSTC2Impl *This = (NSTC2Impl*)iface;
1452     TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
1453
1454     *pnstcsStyle = (This->style & nstcsMask);
1455
1456     return S_OK;
1457 }
1458
1459 static HRESULT WINAPI NSTC2_fnSetControlStyle2(INameSpaceTreeControl2* iface,
1460                                                NSTCSTYLE2 nstcsMask,
1461                                                NSTCSTYLE2 nstcsStyle)
1462 {
1463     NSTC2Impl *This = (NSTC2Impl*)iface;
1464     TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
1465
1466     if((nstcsStyle & nstcsMask) & unsupported_styles2)
1467         FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
1468               (nstcsStyle & nstcsMask),
1469               (nstcsStyle & nstcsMask) & unsupported_styles2);
1470
1471     This->style2 &= ~nstcsMask;
1472     This->style2 |= (nstcsStyle & nstcsMask);
1473
1474     return S_OK;
1475 }
1476
1477 static HRESULT WINAPI NSTC2_fnGetControlStyle2(INameSpaceTreeControl2* iface,
1478                                                NSTCSTYLE2 nstcsMask,
1479                                                NSTCSTYLE2 *pnstcsStyle)
1480 {
1481     NSTC2Impl *This = (NSTC2Impl*)iface;
1482     TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
1483
1484     *pnstcsStyle = (This->style2 & nstcsMask);
1485
1486     return S_OK;
1487 }
1488
1489 static const INameSpaceTreeControl2Vtbl vt_INameSpaceTreeControl2 = {
1490     NSTC2_fnQueryInterface,
1491     NSTC2_fnAddRef,
1492     NSTC2_fnRelease,
1493     NSTC2_fnInitialize,
1494     NSTC2_fnTreeAdvise,
1495     NSTC2_fnTreeUnadvise,
1496     NSTC2_fnAppendRoot,
1497     NSTC2_fnInsertRoot,
1498     NSTC2_fnRemoveRoot,
1499     NSTC2_fnRemoveAllRoots,
1500     NSTC2_fnGetRootItems,
1501     NSTC2_fnSetItemState,
1502     NSTC2_fnGetItemState,
1503     NSTC2_fnGetSelectedItems,
1504     NSTC2_fnGetItemCustomState,
1505     NSTC2_fnSetItemCustomState,
1506     NSTC2_fnEnsureItemVisible,
1507     NSTC2_fnSetTheme,
1508     NSTC2_fnGetNextItem,
1509     NSTC2_fnHitTest,
1510     NSTC2_fnGetItemRect,
1511     NSTC2_fnCollapseAll,
1512     NSTC2_fnSetControlStyle,
1513     NSTC2_fnGetControlStyle,
1514     NSTC2_fnSetControlStyle2,
1515     NSTC2_fnGetControlStyle2
1516 };
1517
1518 /**************************************************************************
1519  * IOleWindow Implementation
1520  */
1521
1522 static inline NSTC2Impl *impl_from_IOleWindow(IOleWindow *iface)
1523 {
1524     return (NSTC2Impl *)((char*)iface - FIELD_OFFSET(NSTC2Impl, lpowVtbl));
1525 }
1526
1527 static HRESULT WINAPI IOW_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
1528 {
1529     NSTC2Impl *This = impl_from_IOleWindow(iface);
1530     TRACE("%p\n", This);
1531     return NSTC2_fnQueryInterface((INameSpaceTreeControl2*)This, riid, ppvObject);
1532 }
1533
1534 static ULONG WINAPI IOW_fnAddRef(IOleWindow *iface)
1535 {
1536     NSTC2Impl *This = impl_from_IOleWindow(iface);
1537     TRACE("%p\n", This);
1538     return NSTC2_fnAddRef((INameSpaceTreeControl2*)This);
1539 }
1540
1541 static ULONG WINAPI IOW_fnRelease(IOleWindow *iface)
1542 {
1543     NSTC2Impl *This = impl_from_IOleWindow(iface);
1544     TRACE("%p\n", This);
1545     return NSTC2_fnRelease((INameSpaceTreeControl2*)This);
1546 }
1547
1548 static HRESULT WINAPI IOW_fnGetWindow(IOleWindow *iface, HWND *phwnd)
1549 {
1550     NSTC2Impl *This = impl_from_IOleWindow(iface);
1551     TRACE("%p (%p)\n", This, phwnd);
1552
1553     *phwnd = This->hwnd_main;
1554     return S_OK;
1555 }
1556
1557 static HRESULT WINAPI IOW_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMode)
1558 {
1559     NSTC2Impl *This = impl_from_IOleWindow(iface);
1560     TRACE("%p (%d)\n", This, fEnterMode);
1561
1562     /* Not implemented */
1563     return E_NOTIMPL;
1564 }
1565
1566 static const IOleWindowVtbl vt_IOleWindow = {
1567     IOW_fnQueryInterface,
1568     IOW_fnAddRef,
1569     IOW_fnRelease,
1570     IOW_fnGetWindow,
1571     IOW_fnContextSensitiveHelp
1572 };
1573
1574 HRESULT NamespaceTreeControl_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
1575 {
1576     NSTC2Impl *nstc;
1577     HRESULT ret;
1578
1579     TRACE ("%p %s %p\n", pUnkOuter, debugstr_guid(riid), ppv);
1580
1581     if(!ppv)
1582         return E_POINTER;
1583     if(pUnkOuter)
1584         return CLASS_E_NOAGGREGATION;
1585
1586     EFRAME_LockModule();
1587
1588     nstc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NSTC2Impl));
1589     nstc->ref = 1;
1590     nstc->lpVtbl = &vt_INameSpaceTreeControl2;
1591     nstc->lpowVtbl = &vt_IOleWindow;
1592
1593     list_init(&nstc->roots);
1594
1595     ret = INameSpaceTreeControl_QueryInterface((INameSpaceTreeControl*)nstc, riid, ppv);
1596     INameSpaceTreeControl_Release((INameSpaceTreeControl*)nstc);
1597
1598     TRACE("--(%p)\n", ppv);
1599     return ret;
1600 }