explorerframe: Implement GetNextItem.
[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 /*************************************************************************
426  * NamespaceTree window functions
427  */
428 static LRESULT create_namespacetree(HWND hWnd, CREATESTRUCTW *crs)
429 {
430     NSTC2Impl *This = crs->lpCreateParams;
431     HIMAGELIST ShellSmallIconList;
432     DWORD treeview_style, treeview_ex_style;
433
434     TRACE("%p (%p)\n", This, crs);
435     SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LPARAM)This);
436     This->hwnd_main = hWnd;
437
438     treeview_style_from_nstcs(This, This->style, 0xFFFFFFFF, &treeview_style);
439
440     This->hwnd_tv = CreateWindowExW(0, WC_TREEVIEWW, NULL, treeview_style,
441                                     0, 0, crs->cx, crs->cy,
442                                     hWnd, NULL, explorerframe_hinstance, NULL);
443
444     if(!This->hwnd_tv)
445     {
446         ERR("Failed to create treeview!\n");
447         return HRESULT_FROM_WIN32(GetLastError());
448     }
449
450     treeview_ex_style = TVS_EX_DRAWIMAGEASYNC | TVS_EX_RICHTOOLTIP |
451         TVS_EX_DOUBLEBUFFER | TVS_EX_NOSINGLECOLLAPSE;
452
453     if(This->style & NSTCS_AUTOHSCROLL)
454         treeview_ex_style |= TVS_EX_AUTOHSCROLL;
455     if(This->style & NSTCS_FADEINOUTEXPANDOS)
456         treeview_ex_style |= TVS_EX_FADEINOUTEXPANDOS;
457     if(This->style & NSTCS_PARTIALCHECKBOXES)
458         treeview_ex_style |= TVS_EX_PARTIALCHECKBOXES;
459     if(This->style & NSTCS_EXCLUSIONCHECKBOXES)
460         treeview_ex_style |= TVS_EX_EXCLUSIONCHECKBOXES;
461     if(This->style & NSTCS_DIMMEDCHECKBOXES)
462         treeview_ex_style |= TVS_EX_DIMMEDCHECKBOXES;
463
464     SendMessageW(This->hwnd_tv, TVM_SETEXTENDEDSTYLE, treeview_ex_style, 0xffff);
465
466     if(Shell_GetImageLists(NULL, &ShellSmallIconList))
467     {
468         SendMessageW(This->hwnd_tv, TVM_SETIMAGELIST,
469                      (WPARAM)TVSIL_NORMAL, (LPARAM)ShellSmallIconList);
470     }
471     else
472     {
473         ERR("Failed to get the System Image List.\n");
474     }
475
476     INameSpaceTreeControl_AddRef((INameSpaceTreeControl*)This);
477
478     /* Subclass the treeview to get the keybord events. */
479     This->tv_oldwndproc = (WNDPROC)SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC,
480                                                      (ULONG_PTR)tv_wndproc);
481     if(This->tv_oldwndproc)
482         SetPropA(This->hwnd_tv, "PROP_THIS", This);
483
484     return TRUE;
485 }
486
487 static LRESULT resize_namespacetree(NSTC2Impl *This)
488 {
489     RECT rc;
490     TRACE("%p\n", This);
491
492     GetClientRect(This->hwnd_main, &rc);
493     MoveWindow(This->hwnd_tv, 0, 0, rc.right-rc.left, rc.bottom-rc.top, TRUE);
494
495     return TRUE;
496 }
497
498 static LRESULT destroy_namespacetree(NSTC2Impl *This)
499 {
500     TRACE("%p\n", This);
501
502     /* Undo the subclassing */
503     if(This->tv_oldwndproc)
504     {
505         SetWindowLongPtrW(This->hwnd_tv, GWLP_WNDPROC, (ULONG_PTR)This->tv_oldwndproc);
506         RemovePropA(This->hwnd_tv, "PROP_THIS");
507     }
508
509     INameSpaceTreeControl_RemoveAllRoots((INameSpaceTreeControl*)This);
510
511     /* This reference was added in create_namespacetree */
512     INameSpaceTreeControl_Release((INameSpaceTreeControl*)This);
513     return TRUE;
514 }
515
516 static LRESULT on_tvn_deleteitemw(NSTC2Impl *This, LPARAM lParam)
517 {
518     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
519     TRACE("%p\n", This);
520
521     IShellItem_Release((IShellItem*)nmtv->itemOld.lParam);
522     return TRUE;
523 }
524
525 static LRESULT on_tvn_getdispinfow(NSTC2Impl *This, LPARAM lParam)
526 {
527     NMTVDISPINFOW *dispinfo = (NMTVDISPINFOW*)lParam;
528     TVITEMEXW *item = (TVITEMEXW*)&dispinfo->item;
529     IShellItem *psi = shellitem_from_treeitem(This, item->hItem);
530     HRESULT hr;
531
532     TRACE("%p, %p (mask: %x)\n", This, dispinfo, item->mask);
533
534     if(item->mask & TVIF_CHILDREN)
535     {
536         SFGAOF sfgao;
537
538         hr = IShellItem_GetAttributes(psi, SFGAO_HASSUBFOLDER, &sfgao);
539         if(SUCCEEDED(hr))
540             item->cChildren = (sfgao & SFGAO_HASSUBFOLDER)?1:0;
541         else
542             item->cChildren = 1;
543
544         item->mask |= TVIF_DI_SETITEM;
545     }
546
547     if(item->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE))
548     {
549         LPITEMIDLIST pidl;
550
551         hr = events_OnGetDefaultIconIndex(This, psi, &item->iImage, &item->iSelectedImage);
552         if(FAILED(hr))
553         {
554             hr = SHGetIDListFromObject((IUnknown*)psi, &pidl);
555             if(SUCCEEDED(hr))
556             {
557                 item->iImage = item->iSelectedImage = get_icon(pidl, 0);
558                 item->mask |= TVIF_DI_SETITEM;
559                 ILFree(pidl);
560             }
561             else
562                 ERR("Failed to get IDList (%08x).\n", hr);
563         }
564     }
565
566     if(item->mask & TVIF_TEXT)
567     {
568         LPWSTR display_name;
569
570         hr = IShellItem_GetDisplayName(psi, SIGDN_NORMALDISPLAY, &display_name);
571         if(SUCCEEDED(hr))
572         {
573             lstrcpynW(item->pszText, display_name, MAX_PATH);
574             item->mask |= TVIF_DI_SETITEM;
575             CoTaskMemFree(display_name);
576         }
577         else
578             ERR("Failed to get display name (%08x).\n", hr);
579     }
580
581     return TRUE;
582 }
583
584 static BOOL treenode_has_subfolders(NSTC2Impl *This, HTREEITEM node)
585 {
586     return SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)node);
587 }
588
589 static LRESULT on_tvn_itemexpandingw(NSTC2Impl *This, LPARAM lParam)
590 {
591     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
592     IShellItem *psi;
593     TRACE("%p\n", This);
594
595     psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
596     events_OnBeforeExpand(This, psi);
597
598     if(!treenode_has_subfolders(This, nmtv->itemNew.hItem))
599     {
600         /* The node has no children, try to find some */
601         if(!fill_sublevel(This, nmtv->itemNew.hItem))
602         {
603             TVITEMEXW tvi;
604             /* Failed to enumerate any children, remove the expando
605              * (if any). */
606             tvi.hItem = nmtv->itemNew.hItem;
607             tvi.mask = TVIF_CHILDREN;
608             tvi.cChildren = 0;
609             SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
610
611             return TRUE;
612         }
613     }
614     return FALSE;
615 }
616
617 static LRESULT on_tvn_itemexpandedw(NSTC2Impl *This, LPARAM lParam)
618 {
619     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
620     IShellItem *psi;
621     TRACE("%p\n", This);
622
623     psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
624     events_OnAfterExpand(This, psi);
625     return TRUE;
626 }
627
628 static LRESULT on_tvn_selchangedw(NSTC2Impl *This, LPARAM lParam)
629 {
630     NMTREEVIEWW *nmtv = (NMTREEVIEWW*)lParam;
631     IShellItemArray *psia;
632     IShellItem *psi;
633     HRESULT hr;
634     TRACE("%p\n", This);
635
636     /* Note: Only supports one selected item. */
637     psi = shellitem_from_treeitem(This, nmtv->itemNew.hItem);
638     hr = SHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, (void**)&psia);
639     if(SUCCEEDED(hr))
640     {
641         events_OnSelectionChanged(This, psia);
642         IShellItemArray_Release(psia);
643     }
644
645     return TRUE;
646 }
647
648 static LRESULT on_nm_click(NSTC2Impl *This, NMHDR *nmhdr)
649 {
650     TVHITTESTINFO tvhit;
651     IShellItem *psi;
652     HRESULT hr;
653     TRACE("%p (%p)\n", This, nmhdr);
654
655     GetCursorPos(&tvhit.pt);
656     ScreenToClient(This->hwnd_tv, &tvhit.pt);
657     SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
658
659     if(tvhit.flags & (TVHT_NOWHERE|TVHT_ABOVE|TVHT_BELOW))
660         return TRUE;
661
662     /* TVHT_ maps onto the corresponding NSTCEHT_ */
663     psi = shellitem_from_treeitem(This, tvhit.hItem);
664     hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_LBUTTON);
665
666     /* The expando should not be expanded unless
667      * double-clicked. */
668     if(tvhit.flags == TVHT_ONITEMBUTTON)
669         return TRUE;
670
671     if(SUCCEEDED(hr))
672         return FALSE;
673     else
674         return TRUE;
675 }
676
677 static LRESULT on_wm_mbuttonup(NSTC2Impl *This, WPARAM wParam, LPARAM lParam)
678 {
679     TVHITTESTINFO tvhit;
680     IShellItem *psi;
681     HRESULT hr;
682     TRACE("%p (%lx, %lx)\n", This, wParam, lParam);
683
684     tvhit.pt.x = (int)(short)LOWORD(lParam);
685     tvhit.pt.y = (int)(short)HIWORD(lParam);
686
687     SendMessageW(This->hwnd_tv, TVM_HITTEST, 0, (LPARAM)&tvhit);
688
689     /* Seems to generate only ONITEM and ONITEMICON */
690     if( !(tvhit.flags & (TVHT_ONITEM|TVHT_ONITEMICON)) )
691         return FALSE;
692
693     psi = shellitem_from_treeitem(This, tvhit.hItem);
694     hr = events_OnItemClick(This, psi, tvhit.flags, NSTCECT_MBUTTON);
695
696     if(SUCCEEDED(hr))
697         return FALSE;
698     else
699         return TRUE;
700 }
701
702 static LRESULT on_kbd_event(NSTC2Impl *This, UINT uMsg, WPARAM wParam, LPARAM lParam)
703 {
704     IShellItem *psi;
705     HTREEITEM hitem;
706     TRACE("%p : %d, %lx, %lx\n", This, uMsg, wParam, lParam);
707
708     /* Handled by the client? */
709     if(FAILED(events_OnKeyboardInput(This, uMsg, wParam, lParam)))
710         return TRUE;
711
712     if(uMsg == WM_KEYDOWN)
713     {
714         switch(wParam)
715         {
716         case VK_DELETE:
717             psi = get_selected_shellitem(This);
718             FIXME("Deletion of file requested (shellitem: %p).\n", psi);
719             return TRUE;
720
721         case VK_F2:
722             hitem = get_selected_treeitem(This);
723             SendMessageW(This->hwnd_tv, TVM_EDITLABELW, 0, (LPARAM)hitem);
724             return TRUE;
725         }
726     }
727
728     /* Let the TreeView handle the key */
729     return FALSE;
730 }
731
732 static LRESULT CALLBACK tv_wndproc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
733 {
734     NSTC2Impl *This = (NSTC2Impl*)GetPropA(hWnd, "PROP_THIS");
735
736     switch(uMessage) {
737     case WM_KEYDOWN:
738     case WM_KEYUP:
739     case WM_CHAR:
740     case WM_SYSKEYDOWN:
741     case WM_SYSKEYUP:
742     case WM_SYSCHAR:
743         if(on_kbd_event(This, uMessage, wParam, lParam))
744             return TRUE;
745         break;
746
747     case WM_MBUTTONUP:        return on_wm_mbuttonup(This, wParam, lParam);
748     }
749
750     /* Pass the message on to the treeview */
751     return CallWindowProcW(This->tv_oldwndproc, hWnd, uMessage, wParam, lParam);
752 }
753
754 static LRESULT CALLBACK NSTC2_WndProc(HWND hWnd, UINT uMessage,
755                                       WPARAM wParam, LPARAM lParam)
756 {
757     NSTC2Impl *This = (NSTC2Impl*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
758     NMHDR *nmhdr;
759
760     switch(uMessage)
761     {
762     case WM_NCCREATE:         return create_namespacetree(hWnd, (CREATESTRUCTW*)lParam);
763     case WM_SIZE:             return resize_namespacetree(This);
764     case WM_DESTROY:          return destroy_namespacetree(This);
765     case WM_NOTIFY:
766         nmhdr = (NMHDR*)lParam;
767         switch(nmhdr->code)
768         {
769         case NM_CLICK:            return on_nm_click(This, nmhdr);
770         case TVN_DELETEITEMW:     return on_tvn_deleteitemw(This, lParam);
771         case TVN_GETDISPINFOW:    return on_tvn_getdispinfow(This, lParam);
772         case TVN_ITEMEXPANDINGW:  return on_tvn_itemexpandingw(This, lParam);
773         case TVN_ITEMEXPANDEDW:   return on_tvn_itemexpandedw(This, lParam);
774         case TVN_SELCHANGEDW:     return on_tvn_selchangedw(This, lParam);
775         default:                  break;
776         }
777         break;
778     default:                  return DefWindowProcW(hWnd, uMessage, wParam, lParam);
779     }
780     return 0;
781 }
782
783 /**************************************************************************
784  * INameSpaceTreeControl2 Implementation
785  */
786 static HRESULT WINAPI NSTC2_fnQueryInterface(INameSpaceTreeControl2* iface,
787                                              REFIID riid,
788                                              void **ppvObject)
789 {
790     NSTC2Impl *This = (NSTC2Impl*)iface;
791     TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppvObject);
792
793     *ppvObject = NULL;
794     if(IsEqualIID(riid, &IID_INameSpaceTreeControl2) ||
795        IsEqualIID(riid, &IID_INameSpaceTreeControl) ||
796        IsEqualIID(riid, &IID_IUnknown))
797     {
798         *ppvObject = This;
799     }
800     else if(IsEqualIID(riid, &IID_IOleWindow))
801     {
802         *ppvObject = &This->lpowVtbl;
803     }
804
805     if(*ppvObject)
806     {
807         IUnknown_AddRef((IUnknown*)*ppvObject);
808         return S_OK;
809     }
810
811     return E_NOINTERFACE;
812 }
813
814 static ULONG WINAPI NSTC2_fnAddRef(INameSpaceTreeControl2* iface)
815 {
816     NSTC2Impl *This = (NSTC2Impl*)iface;
817     LONG ref = InterlockedIncrement(&This->ref);
818
819     TRACE("%p - ref %d\n", This, ref);
820
821     return ref;
822 }
823
824 static ULONG WINAPI NSTC2_fnRelease(INameSpaceTreeControl2* iface)
825 {
826     NSTC2Impl *This = (NSTC2Impl*)iface;
827     LONG ref = InterlockedDecrement(&This->ref);
828
829     TRACE("%p - ref: %d\n", This, ref);
830
831     if(!ref)
832     {
833         TRACE("Freeing.\n");
834         HeapFree(GetProcessHeap(), 0, This);
835         EFRAME_UnlockModule();
836         return 0;
837     }
838
839     return ref;
840 }
841
842 static HRESULT WINAPI NSTC2_fnInitialize(INameSpaceTreeControl2* iface,
843                                          HWND hwndParent,
844                                          RECT *prc,
845                                          NSTCSTYLE nstcsFlags)
846 {
847     NSTC2Impl *This = (NSTC2Impl*)iface;
848     WNDCLASSW wc;
849     DWORD window_style, window_ex_style;
850     RECT rc;
851     static const WCHAR NSTC2_CLASS_NAME[] =
852         {'N','a','m','e','s','p','a','c','e','T','r','e','e',
853          'C','o','n','t','r','o','l',0};
854
855     TRACE("%p (%p, %p, %x)\n", This, hwndParent, prc, nstcsFlags);
856
857     if(nstcsFlags & unsupported_styles)
858         FIXME("0x%08x contains the unsupported style(s) 0x%08x\n",
859               nstcsFlags, nstcsFlags & unsupported_styles);
860
861     This->style = nstcsFlags;
862
863     if(!GetClassInfoW(explorerframe_hinstance, NSTC2_CLASS_NAME, &wc))
864     {
865         wc.style            = CS_HREDRAW | CS_VREDRAW;
866         wc.lpfnWndProc      = NSTC2_WndProc;
867         wc.cbClsExtra       = 0;
868         wc.cbWndExtra       = 0;
869         wc.hInstance        = explorerframe_hinstance;
870         wc.hIcon            = 0;
871         wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
872         wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW + 1);
873         wc.lpszMenuName     = NULL;
874         wc.lpszClassName    = NSTC2_CLASS_NAME;
875
876         if (!RegisterClassW(&wc)) return E_FAIL;
877     }
878
879     /* NSTCS_TABSTOP and NSTCS_BORDER affects the host window */
880     window_style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
881         (nstcsFlags & NSTCS_BORDER ? WS_BORDER : 0);
882     window_ex_style = nstcsFlags & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
883
884     if(prc)
885         CopyRect(&rc, prc);
886     else
887         rc.left = rc.right = rc.top = rc.bottom = 0;
888
889     This->hwnd_main = CreateWindowExW(window_ex_style, NSTC2_CLASS_NAME, NULL, window_style,
890                                       rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
891                                       hwndParent, 0, explorerframe_hinstance, This);
892
893     if(!This->hwnd_main)
894     {
895         ERR("Failed to create the window.\n");
896         return HRESULT_FROM_WIN32(GetLastError());
897     }
898
899     return S_OK;
900 }
901
902 static HRESULT WINAPI NSTC2_fnTreeAdvise(INameSpaceTreeControl2* iface,
903                                          IUnknown *punk,
904                                          DWORD *pdwCookie)
905 {
906     NSTC2Impl *This = (NSTC2Impl*)iface;
907     HRESULT hr;
908     TRACE("%p (%p, %p)\n", This, punk, pdwCookie);
909
910     *pdwCookie = 0;
911
912     /* Only one client supported */
913     if(This->pnstce)
914         return E_FAIL;
915
916     hr = IUnknown_QueryInterface(punk, &IID_INameSpaceTreeControlEvents,(void**)&This->pnstce);
917     if(SUCCEEDED(hr))
918     {
919         *pdwCookie = 1;
920         return hr;
921     }
922
923     return E_FAIL;
924 }
925
926 static HRESULT WINAPI NSTC2_fnTreeUnadvise(INameSpaceTreeControl2* iface,
927                                            DWORD dwCookie)
928 {
929     NSTC2Impl *This = (NSTC2Impl*)iface;
930     TRACE("%p (%x)\n", This, dwCookie);
931
932     /* The cookie is ignored. */
933
934     if(This->pnstce)
935     {
936         INameSpaceTreeControlEvents_Release(This->pnstce);
937         This->pnstce = NULL;
938     }
939
940     return S_OK;
941 }
942
943 static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface,
944                                          int iIndex,
945                                          IShellItem *psiRoot,
946                                          SHCONTF grfEnumFlags,
947                                          NSTCROOTSTYLE grfRootStyle,
948                                          IShellItemFilter *pif)
949 {
950     NSTC2Impl *This = (NSTC2Impl*)iface;
951     nstc_root *new_root;
952     struct list *add_after_entry;
953     HTREEITEM add_after_hitem;
954     UINT i;
955
956     TRACE("%p, %d, %p, %x, %x, %p\n", This, iIndex, psiRoot, grfEnumFlags, grfRootStyle, pif);
957
958     new_root = HeapAlloc(GetProcessHeap(), 0, sizeof(nstc_root));
959     if(!new_root)
960         return E_OUTOFMEMORY;
961
962     new_root->psi = psiRoot;
963     new_root->enum_flags = grfEnumFlags;
964     new_root->root_style = grfRootStyle;
965     new_root->pif = pif;
966
967     /* We want to keep the roots in the internal list and in the
968      * treeview in the same order. */
969     add_after_entry = &This->roots;
970     for(i = 0; i < max(0, iIndex) && list_next(&This->roots, add_after_entry); i++)
971         add_after_entry = list_next(&This->roots, add_after_entry);
972
973     if(add_after_entry == &This->roots)
974         add_after_hitem = TVI_FIRST;
975     else
976         add_after_hitem = LIST_ENTRY(add_after_entry, nstc_root, entry)->htreeitem;
977
978     new_root->htreeitem = insert_shellitem(This, psiRoot, TVI_ROOT, add_after_hitem);
979     if(!new_root->htreeitem)
980     {
981         WARN("Failed to add the root.\n");
982         HeapFree(GetProcessHeap(), 0, new_root);
983         return E_FAIL;
984     }
985
986     list_add_after(add_after_entry, &new_root->entry);
987     events_OnItemAdded(This, psiRoot, TRUE);
988
989     if(grfRootStyle & NSTCRS_HIDDEN)
990     {
991         TVITEMEXW tvi;
992         tvi.mask = TVIF_STATEEX;
993         tvi.uStateEx = TVIS_EX_FLAT;
994         tvi.hItem = new_root->htreeitem;
995
996         SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
997     }
998
999     if(grfRootStyle & NSTCRS_EXPANDED)
1000         SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_EXPAND,
1001                      (LPARAM)new_root->htreeitem);
1002
1003     return S_OK;
1004 }
1005
1006 static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface,
1007                                          IShellItem *psiRoot,
1008                                          SHCONTF grfEnumFlags,
1009                                          NSTCROOTSTYLE grfRootStyle,
1010                                          IShellItemFilter *pif)
1011 {
1012     NSTC2Impl *This = (NSTC2Impl*)iface;
1013     UINT root_count;
1014     TRACE("%p, %p, %x, %x, %p\n",
1015           This, psiRoot, grfEnumFlags, grfRootStyle, pif);
1016
1017     root_count = list_count(&This->roots);
1018
1019     return NSTC2_fnInsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif);
1020 }
1021
1022 static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface,
1023                                          IShellItem *psiRoot)
1024 {
1025     NSTC2Impl *This = (NSTC2Impl*)iface;
1026     nstc_root *cursor, *root = NULL;
1027     TRACE("%p (%p)\n", This, psiRoot);
1028
1029     if(!psiRoot)
1030         return E_NOINTERFACE;
1031
1032     LIST_FOR_EACH_ENTRY(cursor, &This->roots, nstc_root, entry)
1033     {
1034         HRESULT hr;
1035         int order;
1036         hr = IShellItem_Compare(psiRoot, cursor->psi, SICHINT_DISPLAY, &order);
1037         if(hr == S_OK)
1038         {
1039             root = cursor;
1040             break;
1041         }
1042     }
1043
1044     TRACE("root %p\n", root);
1045     if(root)
1046     {
1047         events_OnItemDeleted(This, root->psi, TRUE);
1048         SendMessageW(This->hwnd_tv, TVM_DELETEITEM, 0, (LPARAM)root->htreeitem);
1049         list_remove(&root->entry);
1050         HeapFree(GetProcessHeap(), 0, root);
1051         return S_OK;
1052     }
1053     else
1054     {
1055         WARN("No matching root found.\n");
1056         return E_FAIL;
1057     }
1058 }
1059
1060 static HRESULT WINAPI NSTC2_fnRemoveAllRoots(INameSpaceTreeControl2* iface)
1061 {
1062     NSTC2Impl *This = (NSTC2Impl*)iface;
1063     nstc_root *cur1, *cur2;
1064     UINT removed = 0;
1065     TRACE("%p\n", This);
1066
1067     LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->roots, nstc_root, entry)
1068     {
1069         NSTC2_fnRemoveRoot(iface, cur1->psi);
1070         removed++;
1071     }
1072
1073     if(removed)
1074         return S_OK;
1075     else
1076         return E_INVALIDARG;
1077 }
1078
1079 static HRESULT WINAPI NSTC2_fnGetRootItems(INameSpaceTreeControl2* iface,
1080                                            IShellItemArray **ppsiaRootItems)
1081 {
1082     NSTC2Impl *This = (NSTC2Impl*)iface;
1083     IShellFolder *psf;
1084     LPITEMIDLIST *array;
1085     nstc_root *root;
1086     UINT count, i;
1087     HRESULT hr;
1088     TRACE("%p (%p)\n", This, ppsiaRootItems);
1089
1090     count = list_count(&This->roots);
1091
1092     if(!count)
1093         return E_INVALIDARG;
1094
1095     array = HeapAlloc(GetProcessHeap(), 0, sizeof(LPITEMIDLIST*)*count);
1096
1097     i = 0;
1098     LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
1099         SHGetIDListFromObject((IUnknown*)root->psi, &array[i++]);
1100
1101     SHGetDesktopFolder(&psf);
1102     hr = SHCreateShellItemArray(NULL, psf, count, (PCUITEMID_CHILD_ARRAY)array,
1103                                 ppsiaRootItems);
1104     IShellFolder_Release(psf);
1105
1106     for(i = 0; i < count; i++)
1107         ILFree(array[i]);
1108
1109     HeapFree(GetProcessHeap(), 0, array);
1110
1111     return hr;
1112 }
1113
1114 static HRESULT WINAPI NSTC2_fnSetItemState(INameSpaceTreeControl2* iface,
1115                                            IShellItem *psi,
1116                                            NSTCITEMSTATE nstcisMask,
1117                                            NSTCITEMSTATE nstcisFlags)
1118 {
1119     NSTC2Impl *This = (NSTC2Impl*)iface;
1120     TVITEMEXW tvi;
1121     HTREEITEM hitem;
1122
1123     TRACE("%p (%p, %x, %x)\n", This, psi, nstcisMask, nstcisFlags);
1124
1125     hitem = treeitem_from_shellitem(This, psi);
1126     if(!hitem) return E_INVALIDARG;
1127
1128     /* Passing both NSTCIS_SELECTED and NSTCIS_SELECTEDNOEXPAND results
1129        in two TVM_SETITEMW's */
1130     if((nstcisMask&nstcisFlags) & NSTCIS_SELECTED)
1131     {
1132         SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hitem);
1133         SendMessageW(This->hwnd_tv, TVM_ENSUREVISIBLE, 0, (LPARAM)hitem);
1134     }
1135     if((nstcisMask&nstcisFlags) & NSTCIS_SELECTEDNOEXPAND)
1136     {
1137         SendMessageW(This->hwnd_tv, TVM_SELECTITEM, TVGN_CARET|TVSI_NOSINGLEEXPAND, (LPARAM)hitem);
1138     }
1139
1140     /* If NSTCIS_EXPANDED is among the flags, the mask is ignored. */
1141     if((nstcisMask|nstcisFlags) & NSTCIS_EXPANDED)
1142     {
1143         WPARAM arg = nstcisFlags&NSTCIS_EXPANDED ? TVE_EXPAND:TVE_COLLAPSE;
1144         SendMessageW(This->hwnd_tv, TVM_EXPAND, arg, (LPARAM)hitem);
1145     }
1146
1147     if(nstcisMask & NSTCIS_DISABLED)
1148         tvi.mask = TVIF_STATE | TVIF_STATEEX;
1149     else if( ((nstcisMask^nstcisFlags) & (NSTCIS_SELECTED|NSTCIS_EXPANDED|NSTCIS_SELECTEDNOEXPAND)) ||
1150              ((nstcisMask|nstcisFlags) & NSTCIS_BOLD) ||
1151              (nstcisFlags & NSTCIS_DISABLED) )
1152         tvi.mask = TVIF_STATE;
1153     else
1154         tvi.mask = 0;
1155
1156     if(tvi.mask)
1157     {
1158         tvi.stateMask = tvi.state = 0;
1159         tvi.stateMask |= ((nstcisFlags^nstcisMask)&NSTCIS_SELECTED) ? TVIS_SELECTED : 0;
1160         tvi.stateMask |= (nstcisMask|nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
1161         tvi.state     |= (nstcisMask&nstcisFlags)&NSTCIS_BOLD ? TVIS_BOLD:0;
1162
1163         if((nstcisMask&NSTCIS_EXPANDED)^(nstcisFlags&NSTCIS_EXPANDED))
1164         {
1165             tvi.stateMask = 0;
1166         }
1167
1168         tvi.uStateEx = (nstcisFlags&nstcisMask)&NSTCIS_DISABLED?TVIS_EX_DISABLED:0;
1169         tvi.hItem = hitem;
1170
1171         SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
1172     }
1173
1174     return S_OK;
1175 }
1176
1177 static HRESULT WINAPI NSTC2_fnGetItemState(INameSpaceTreeControl2* iface,
1178                                            IShellItem *psi,
1179                                            NSTCITEMSTATE nstcisMask,
1180                                            NSTCITEMSTATE *pnstcisFlags)
1181 {
1182     NSTC2Impl *This = (NSTC2Impl*)iface;
1183     HTREEITEM hitem;
1184     TVITEMEXW tvi;
1185     TRACE("%p (%p, %x, %p)\n", This, psi, nstcisMask, pnstcisFlags);
1186
1187     hitem = treeitem_from_shellitem(This, psi);
1188     if(!hitem)
1189         return E_INVALIDARG;
1190
1191     *pnstcisFlags = 0;
1192
1193     tvi.hItem = hitem;
1194     tvi.mask = TVIF_STATE;
1195     tvi.stateMask = TVIS_SELECTED|TVIS_EXPANDED|TVIS_BOLD;
1196
1197     if(nstcisMask & NSTCIS_DISABLED)
1198         tvi.mask |= TVIF_STATEEX;
1199
1200     SendMessageW(This->hwnd_tv, TVM_GETITEMW, 0, (LPARAM)&tvi);
1201     *pnstcisFlags |= (tvi.state & TVIS_SELECTED)?NSTCIS_SELECTED:0;
1202     *pnstcisFlags |= (tvi.state & TVIS_EXPANDED)?NSTCIS_EXPANDED:0;
1203     *pnstcisFlags |= (tvi.state & TVIS_BOLD)?NSTCIS_BOLD:0;
1204     *pnstcisFlags |= (tvi.uStateEx & TVIS_EX_DISABLED)?NSTCIS_DISABLED:0;
1205
1206     *pnstcisFlags &= nstcisMask;
1207
1208     return S_OK;
1209 }
1210
1211 static HRESULT WINAPI NSTC2_fnGetSelectedItems(INameSpaceTreeControl2* iface,
1212                                                IShellItemArray **psiaItems)
1213 {
1214     NSTC2Impl *This = (NSTC2Impl*)iface;
1215     FIXME("stub, %p (%p)\n", This, psiaItems);
1216     return E_NOTIMPL;
1217 }
1218
1219 static HRESULT WINAPI NSTC2_fnGetItemCustomState(INameSpaceTreeControl2* iface,
1220                                                  IShellItem *psi,
1221                                                  int *piStateNumber)
1222 {
1223     NSTC2Impl *This = (NSTC2Impl*)iface;
1224     FIXME("stub, %p (%p, %p)\n", This, psi, piStateNumber);
1225     return E_NOTIMPL;
1226 }
1227
1228 static HRESULT WINAPI NSTC2_fnSetItemCustomState(INameSpaceTreeControl2* iface,
1229                                                  IShellItem *psi,
1230                                                  int iStateNumber)
1231 {
1232     NSTC2Impl *This = (NSTC2Impl*)iface;
1233     FIXME("stub, %p (%p, %d)\n", This, psi, iStateNumber);
1234     return E_NOTIMPL;
1235 }
1236
1237 static HRESULT WINAPI NSTC2_fnEnsureItemVisible(INameSpaceTreeControl2* iface,
1238                                                 IShellItem *psi)
1239 {
1240     NSTC2Impl *This = (NSTC2Impl*)iface;
1241     FIXME("stub, %p (%p)\n", This, psi);
1242     return E_NOTIMPL;
1243 }
1244
1245 static HRESULT WINAPI NSTC2_fnSetTheme(INameSpaceTreeControl2* iface,
1246                                        LPCWSTR pszTheme)
1247 {
1248     NSTC2Impl *This = (NSTC2Impl*)iface;
1249     FIXME("stub, %p (%p)\n", This, pszTheme);
1250     return E_NOTIMPL;
1251 }
1252
1253 static HRESULT WINAPI NSTC2_fnGetNextItem(INameSpaceTreeControl2* iface,
1254                                           IShellItem *psi,
1255                                           NSTCGNI nstcgi,
1256                                           IShellItem **ppsiNext)
1257 {
1258     NSTC2Impl *This = (NSTC2Impl*)iface;
1259     HTREEITEM hitem, hnext;
1260     UINT tvgn;
1261     TRACE("%p (%p, %x, %p)\n", This, psi, nstcgi, ppsiNext);
1262
1263     if(!ppsiNext) return E_POINTER;
1264     if(!psi)      return E_FAIL;
1265
1266     *ppsiNext = NULL;
1267
1268     hitem = treeitem_from_shellitem(This, psi);
1269     if(!hitem)
1270         return E_INVALIDARG;
1271
1272     switch(nstcgi)
1273     {
1274     case NSTCGNI_NEXT:         tvgn = TVGN_NEXT; break;
1275     case NSTCGNI_NEXTVISIBLE:  tvgn = TVGN_NEXTVISIBLE; break;
1276     case NSTCGNI_PREV:         tvgn = TVGN_PREVIOUS; break;
1277     case NSTCGNI_PREVVISIBLE:  tvgn = TVGN_PREVIOUSVISIBLE; break;
1278     case NSTCGNI_PARENT:       tvgn = TVGN_PARENT; break;
1279     case NSTCGNI_CHILD:        tvgn = TVGN_CHILD; break;
1280     case NSTCGNI_FIRSTVISIBLE: tvgn = TVGN_FIRSTVISIBLE; break;
1281     case NSTCGNI_LASTVISIBLE:  tvgn = TVGN_LASTVISIBLE; break;
1282     default:
1283         FIXME("Unknown nstcgi value %d\n", nstcgi);
1284         return E_FAIL;
1285     }
1286
1287     hnext = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_GETNEXTITEM, tvgn, (WPARAM)hitem);
1288     if(hnext)
1289     {
1290         *ppsiNext = shellitem_from_treeitem(This, hnext);
1291         IShellItem_AddRef(*ppsiNext);
1292         return S_OK;
1293     }
1294
1295     return E_FAIL;
1296 }
1297
1298 static HRESULT WINAPI NSTC2_fnHitTest(INameSpaceTreeControl2* iface,
1299                                       POINT *ppt,
1300                                       IShellItem **ppsiOut)
1301 {
1302     NSTC2Impl *This = (NSTC2Impl*)iface;
1303     FIXME("stub, %p (%p, %p)\n", This, ppsiOut, ppt);
1304     return E_NOTIMPL;
1305 }
1306
1307 static HRESULT WINAPI NSTC2_fnGetItemRect(INameSpaceTreeControl2* iface,
1308                                           IShellItem *psi,
1309                                           RECT *prect)
1310 {
1311     NSTC2Impl *This = (NSTC2Impl*)iface;
1312     FIXME("stub, %p (%p, %p)\n", This, psi, prect);
1313     return E_NOTIMPL;
1314 }
1315
1316 static HRESULT WINAPI NSTC2_fnCollapseAll(INameSpaceTreeControl2* iface)
1317 {
1318     NSTC2Impl *This = (NSTC2Impl*)iface;
1319     nstc_root *root;
1320     TRACE("%p\n", This);
1321
1322     LIST_FOR_EACH_ENTRY(root, &This->roots, nstc_root, entry)
1323         collapse_all(This, root->htreeitem);
1324
1325     return S_OK;
1326 }
1327
1328 static HRESULT WINAPI NSTC2_fnSetControlStyle(INameSpaceTreeControl2* iface,
1329                                               NSTCSTYLE nstcsMask,
1330                                               NSTCSTYLE nstcsStyle)
1331 {
1332     NSTC2Impl *This = (NSTC2Impl*)iface;
1333     static const DWORD tv_style_flags =
1334         NSTCS_HASEXPANDOS | NSTCS_HASLINES | NSTCS_FULLROWSELECT |
1335         NSTCS_HORIZONTALSCROLL | NSTCS_ROOTHASEXPANDO |
1336         NSTCS_SHOWSELECTIONALWAYS | NSTCS_NOINFOTIP | NSTCS_EVENHEIGHT |
1337         NSTCS_DISABLEDRAGDROP | NSTCS_NOEDITLABELS | NSTCS_CHECKBOXES;
1338     static const DWORD host_style_flags = NSTCS_TABSTOP | NSTCS_BORDER;
1339     static const DWORD nstc_flags =
1340         NSTCS_SINGLECLICKEXPAND | NSTCS_NOREPLACEOPEN | NSTCS_NOORDERSTREAM |
1341         NSTCS_FAVORITESMODE | NSTCS_EMPTYTEXT | NSTCS_ALLOWJUNCTIONS |
1342         NSTCS_SHOWTABSBUTTON | NSTCS_SHOWDELETEBUTTON | NSTCS_SHOWREFRESHBUTTON;
1343     TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
1344
1345     /* Fail if there is an attempt to set an unknown style. */
1346     if(nstcsMask & ~(tv_style_flags | host_style_flags | nstc_flags))
1347         return E_FAIL;
1348
1349     if(nstcsMask & tv_style_flags)
1350     {
1351         DWORD new_style;
1352         treeview_style_from_nstcs(This, nstcsStyle, nstcsMask, &new_style);
1353         SetWindowLongPtrW(This->hwnd_tv, GWL_STYLE, new_style);
1354     }
1355
1356     /* Flags affecting the host window */
1357     if(nstcsMask & NSTCS_BORDER)
1358     {
1359         DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_STYLE);
1360         new_style &= ~WS_BORDER;
1361         new_style |= nstcsStyle & NSTCS_BORDER ? WS_BORDER : 0;
1362         SetWindowLongPtrW(This->hwnd_main, GWL_STYLE, new_style);
1363     }
1364     if(nstcsMask & NSTCS_TABSTOP)
1365     {
1366         DWORD new_style = GetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE);
1367         new_style &= ~WS_EX_CONTROLPARENT;
1368         new_style |= nstcsStyle & NSTCS_TABSTOP ? WS_EX_CONTROLPARENT : 0;
1369         SetWindowLongPtrW(This->hwnd_main, GWL_EXSTYLE, new_style);
1370     }
1371
1372     if((nstcsStyle & nstcsMask) & unsupported_styles)
1373         FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
1374               (nstcsStyle & nstcsMask),
1375               (nstcsStyle & nstcsMask) & unsupported_styles);
1376
1377     This->style &= ~nstcsMask;
1378     This->style |= (nstcsStyle & nstcsMask);
1379
1380     return S_OK;
1381 }
1382
1383 static HRESULT WINAPI NSTC2_fnGetControlStyle(INameSpaceTreeControl2* iface,
1384                                               NSTCSTYLE nstcsMask,
1385                                               NSTCSTYLE *pnstcsStyle)
1386 {
1387     NSTC2Impl *This = (NSTC2Impl*)iface;
1388     TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
1389
1390     *pnstcsStyle = (This->style & nstcsMask);
1391
1392     return S_OK;
1393 }
1394
1395 static HRESULT WINAPI NSTC2_fnSetControlStyle2(INameSpaceTreeControl2* iface,
1396                                                NSTCSTYLE2 nstcsMask,
1397                                                NSTCSTYLE2 nstcsStyle)
1398 {
1399     NSTC2Impl *This = (NSTC2Impl*)iface;
1400     TRACE("%p (%x, %x)\n", This, nstcsMask, nstcsStyle);
1401
1402     if((nstcsStyle & nstcsMask) & unsupported_styles2)
1403         FIXME("mask & style (0x%08x) contains unsupported style(s): 0x%08x\n",
1404               (nstcsStyle & nstcsMask),
1405               (nstcsStyle & nstcsMask) & unsupported_styles2);
1406
1407     This->style2 &= ~nstcsMask;
1408     This->style2 |= (nstcsStyle & nstcsMask);
1409
1410     return S_OK;
1411 }
1412
1413 static HRESULT WINAPI NSTC2_fnGetControlStyle2(INameSpaceTreeControl2* iface,
1414                                                NSTCSTYLE2 nstcsMask,
1415                                                NSTCSTYLE2 *pnstcsStyle)
1416 {
1417     NSTC2Impl *This = (NSTC2Impl*)iface;
1418     TRACE("%p (%x, %p)\n", This, nstcsMask, pnstcsStyle);
1419
1420     *pnstcsStyle = (This->style2 & nstcsMask);
1421
1422     return S_OK;
1423 }
1424
1425 static const INameSpaceTreeControl2Vtbl vt_INameSpaceTreeControl2 = {
1426     NSTC2_fnQueryInterface,
1427     NSTC2_fnAddRef,
1428     NSTC2_fnRelease,
1429     NSTC2_fnInitialize,
1430     NSTC2_fnTreeAdvise,
1431     NSTC2_fnTreeUnadvise,
1432     NSTC2_fnAppendRoot,
1433     NSTC2_fnInsertRoot,
1434     NSTC2_fnRemoveRoot,
1435     NSTC2_fnRemoveAllRoots,
1436     NSTC2_fnGetRootItems,
1437     NSTC2_fnSetItemState,
1438     NSTC2_fnGetItemState,
1439     NSTC2_fnGetSelectedItems,
1440     NSTC2_fnGetItemCustomState,
1441     NSTC2_fnSetItemCustomState,
1442     NSTC2_fnEnsureItemVisible,
1443     NSTC2_fnSetTheme,
1444     NSTC2_fnGetNextItem,
1445     NSTC2_fnHitTest,
1446     NSTC2_fnGetItemRect,
1447     NSTC2_fnCollapseAll,
1448     NSTC2_fnSetControlStyle,
1449     NSTC2_fnGetControlStyle,
1450     NSTC2_fnSetControlStyle2,
1451     NSTC2_fnGetControlStyle2
1452 };
1453
1454 /**************************************************************************
1455  * IOleWindow Implementation
1456  */
1457
1458 static inline NSTC2Impl *impl_from_IOleWindow(IOleWindow *iface)
1459 {
1460     return (NSTC2Impl *)((char*)iface - FIELD_OFFSET(NSTC2Impl, lpowVtbl));
1461 }
1462
1463 static HRESULT WINAPI IOW_fnQueryInterface(IOleWindow *iface, REFIID riid, void **ppvObject)
1464 {
1465     NSTC2Impl *This = impl_from_IOleWindow(iface);
1466     TRACE("%p\n", This);
1467     return NSTC2_fnQueryInterface((INameSpaceTreeControl2*)This, riid, ppvObject);
1468 }
1469
1470 static ULONG WINAPI IOW_fnAddRef(IOleWindow *iface)
1471 {
1472     NSTC2Impl *This = impl_from_IOleWindow(iface);
1473     TRACE("%p\n", This);
1474     return NSTC2_fnAddRef((INameSpaceTreeControl2*)This);
1475 }
1476
1477 static ULONG WINAPI IOW_fnRelease(IOleWindow *iface)
1478 {
1479     NSTC2Impl *This = impl_from_IOleWindow(iface);
1480     TRACE("%p\n", This);
1481     return NSTC2_fnRelease((INameSpaceTreeControl2*)This);
1482 }
1483
1484 static HRESULT WINAPI IOW_fnGetWindow(IOleWindow *iface, HWND *phwnd)
1485 {
1486     NSTC2Impl *This = impl_from_IOleWindow(iface);
1487     TRACE("%p (%p)\n", This, phwnd);
1488
1489     *phwnd = This->hwnd_main;
1490     return S_OK;
1491 }
1492
1493 static HRESULT WINAPI IOW_fnContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMode)
1494 {
1495     NSTC2Impl *This = impl_from_IOleWindow(iface);
1496     TRACE("%p (%d)\n", This, fEnterMode);
1497
1498     /* Not implemented */
1499     return E_NOTIMPL;
1500 }
1501
1502 static const IOleWindowVtbl vt_IOleWindow = {
1503     IOW_fnQueryInterface,
1504     IOW_fnAddRef,
1505     IOW_fnRelease,
1506     IOW_fnGetWindow,
1507     IOW_fnContextSensitiveHelp
1508 };
1509
1510 HRESULT NamespaceTreeControl_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
1511 {
1512     NSTC2Impl *nstc;
1513     HRESULT ret;
1514
1515     TRACE ("%p %s %p\n", pUnkOuter, debugstr_guid(riid), ppv);
1516
1517     if(!ppv)
1518         return E_POINTER;
1519     if(pUnkOuter)
1520         return CLASS_E_NOAGGREGATION;
1521
1522     EFRAME_LockModule();
1523
1524     nstc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NSTC2Impl));
1525     nstc->ref = 1;
1526     nstc->lpVtbl = &vt_INameSpaceTreeControl2;
1527     nstc->lpowVtbl = &vt_IOleWindow;
1528
1529     list_init(&nstc->roots);
1530
1531     ret = INameSpaceTreeControl_QueryInterface((INameSpaceTreeControl*)nstc, riid, ppv);
1532     INameSpaceTreeControl_Release((INameSpaceTreeControl*)nstc);
1533
1534     TRACE("--(%p)\n", ppv);
1535     return ret;
1536 }