xref: /reactos/dll/win32/shdocvw/CNSCBand.cpp (revision 29c8dc82)
1 /*
2  * PROJECT:     ReactOS shdocvw
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     NameSpace Control Band
5  * COPYRIGHT:   Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "objects.h"
9 #include <shlobj.h>
10 #include <commoncontrols.h>
11 #include <undocshell.h>
12 
13 #define TIMER_ID_REFRESH 9999
14 
15 #include <wine/debug.h>
16 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
17 
18 HRESULT
SHDOCVW_GetPathOfShortcut(_In_opt_ HWND hWnd,_In_ LPCWSTR pszLnkFile,_Out_ LPWSTR pszPath)19 SHDOCVW_GetPathOfShortcut(
20     _In_opt_ HWND hWnd,
21     _In_ LPCWSTR pszLnkFile,
22     _Out_ LPWSTR pszPath)
23 {
24     *pszPath = UNICODE_NULL;
25     CComPtr<IShellLink> pShellLink;
26     HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL,  CLSCTX_INPROC_SERVER,
27                                   IID_PPV_ARG(IShellLink, &pShellLink));
28     if (FAILED_UNEXPECTEDLY(hr))
29         return hr;
30 
31     CComPtr<IPersistFile> pPersistFile;
32     hr = pShellLink->QueryInterface(IID_PPV_ARG(IPersistFile, &pPersistFile));
33     if (FAILED_UNEXPECTEDLY(hr))
34         return hr;
35 
36     hr = pPersistFile->Load(pszLnkFile, STGM_READ);
37     if (FAILED_UNEXPECTEDLY(hr))
38         return hr;
39 
40     WIN32_FIND_DATA find;
41     hr = pShellLink->GetPath(pszPath, MAX_PATH, &find, 0);
42     if (FAILED_UNEXPECTEDLY(hr))
43         return hr;
44 
45     return S_OK;
46 }
47 
48 HRESULT
SHDOCVW_CreateShortcut(_In_ LPCWSTR pszLnkFileName,_In_ PCIDLIST_ABSOLUTE pidlTarget,_In_opt_ LPCWSTR pszDescription)49 SHDOCVW_CreateShortcut(
50     _In_ LPCWSTR pszLnkFileName,
51     _In_ PCIDLIST_ABSOLUTE pidlTarget,
52     _In_opt_ LPCWSTR pszDescription)
53 {
54     HRESULT hr;
55 
56     CComPtr<IShellLink> psl;
57     hr = CoCreateInstance(CLSID_ShellLink, NULL,  CLSCTX_INPROC_SERVER,
58                           IID_PPV_ARG(IShellLink, &psl));
59     if (FAILED_UNEXPECTEDLY(hr))
60         return hr;
61 
62     psl->SetIDList(pidlTarget);
63 
64     if (pszDescription)
65         psl->SetDescription(pszDescription);
66 
67     CComPtr<IPersistFile> ppf;
68     hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
69     if (FAILED_UNEXPECTEDLY(hr))
70         return hr;
71 
72     return ppf->Save(pszLnkFileName, TRUE);
73 }
74 
CNSCBand()75 CNSCBand::CNSCBand()
76 {
77     SHDOCVW_LockModule();
78 
79     INITCOMMONCONTROLSEX iccx = { sizeof(iccx), ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES };
80     ::InitCommonControlsEx(&iccx);
81 }
82 
~CNSCBand()83 CNSCBand::~CNSCBand()
84 {
85     if (m_hToolbarImageList)
86     {
87         ImageList_Destroy(m_hToolbarImageList);
88         m_hToolbarImageList = NULL;
89     }
90     SHDOCVW_UnlockModule();
91 }
92 
OnFinalMessage(HWND)93 VOID CNSCBand::OnFinalMessage(HWND)
94 {
95     // The message loop is finished, now we can safely destruct!
96     static_cast<IDeskBand *>(this)->Release();
97 }
98 
99 // *** helper methods ***
100 
GetItemData(_In_ HTREEITEM hItem)101 CNSCBand::CItemData* CNSCBand::GetItemData(_In_ HTREEITEM hItem)
102 {
103     if (hItem == TVI_ROOT)
104         return NULL;
105 
106     TVITEMW tvItem = { TVIF_PARAM, hItem };
107     if (!TreeView_GetItem(m_hwndTreeView, &tvItem))
108         return NULL;
109 
110     return reinterpret_cast<CItemData*>(tvItem.lParam);
111 }
112 
113 static HRESULT
SHDOCVW_GetCurrentLocationFromView(_In_ IShellView & View,_In_ PIDLIST_ABSOLUTE * ppidl)114 SHDOCVW_GetCurrentLocationFromView(_In_ IShellView& View, _In_ PIDLIST_ABSOLUTE *ppidl)
115 {
116     CComPtr<IFolderView> pfv;
117     CComPtr<IShellFolder> psf;
118     HRESULT hr = View.QueryInterface(IID_PPV_ARG(IFolderView, &pfv));
119     if (SUCCEEDED(hr) && SUCCEEDED(hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf))))
120         hr = SHELL_GetIDListFromObject(psf, ppidl);
121     return hr;
122 }
123 
_GetCurrentLocation(_Out_ PIDLIST_ABSOLUTE * ppidl)124 HRESULT CNSCBand::_GetCurrentLocation(_Out_ PIDLIST_ABSOLUTE *ppidl)
125 {
126     *ppidl = NULL;
127     CComPtr<IShellBrowser> psb;
128     HRESULT hr = IUnknown_QueryService(m_pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
129     if (FAILED_UNEXPECTEDLY(hr))
130         return hr;
131 
132     CComPtr<IBrowserService> pbs;
133     if (SUCCEEDED(hr = psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs))))
134         if (SUCCEEDED(hr = pbs->GetPidl(ppidl)) && *ppidl)
135             return hr;
136 
137     CComPtr<IShellView> psv;
138     if (!FAILED_UNEXPECTEDLY(hr = psb->QueryActiveShellView(&psv)))
139         if (SUCCEEDED(hr = psv.p ? SHDOCVW_GetCurrentLocationFromView(*psv.p, ppidl) : E_FAIL))
140             return hr;
141     return hr;
142 }
143 
_IsCurrentLocation(_In_ PCIDLIST_ABSOLUTE pidl)144 HRESULT CNSCBand::_IsCurrentLocation(_In_ PCIDLIST_ABSOLUTE pidl)
145 {
146     if (!pidl)
147         return E_INVALIDARG;
148     HRESULT hr = E_FAIL;
149     PIDLIST_ABSOLUTE location = NULL;
150     hr = _GetCurrentLocation(&location);
151     if (SUCCEEDED(hr))
152         hr = SHELL_IsEqualAbsoluteID(location, pidl) ? S_OK : S_FALSE;
153     ILFree(location);
154     return hr;
155 }
156 
_ExecuteCommand(_In_ CComPtr<IContextMenu> & menu,_In_ UINT nCmd)157 HRESULT CNSCBand::_ExecuteCommand(_In_ CComPtr<IContextMenu>& menu, _In_ UINT nCmd)
158 {
159     CComPtr<IOleWindow> pBrowserOleWnd;
160     HRESULT hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser,
161                                        IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
162     if (FAILED_UNEXPECTEDLY(hr))
163         return hr;
164 
165     HWND browserWnd;
166     hr = pBrowserOleWnd->GetWindow(&browserWnd);
167     if (FAILED_UNEXPECTEDLY(hr))
168         return hr;
169 
170     CMINVOKECOMMANDINFO cmi = { sizeof(cmi) };
171     cmi.lpVerb = MAKEINTRESOURCEA(nCmd);
172     cmi.hwnd = browserWnd;
173     cmi.nShow = SW_SHOW;
174     if (::GetKeyState(VK_SHIFT) < 0)
175         cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
176     if (::GetKeyState(VK_CONTROL) < 0)
177         cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
178 
179     return menu->InvokeCommand(&cmi);
180 }
181 
_RegisterChangeNotify()182 void CNSCBand::_RegisterChangeNotify()
183 {
184 #define TARGET_EVENTS ( \
185     SHCNE_DRIVEADD | SHCNE_MKDIR | SHCNE_CREATE | SHCNE_DRIVEREMOVED | SHCNE_RMDIR | \
186     SHCNE_DELETE | SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR | \
187     SHCNE_UPDATEITEM | SHCNE_ASSOCCHANGED \
188 )
189     // Register shell notification
190     SHChangeNotifyEntry shcne = { m_pidlRoot, TRUE };
191     m_shellRegID = SHChangeNotifyRegister(m_hWnd,
192                                           SHCNRF_NewDelivery | SHCNRF_ShellLevel,
193                                           TARGET_EVENTS,
194                                           WM_USER_SHELLEVENT,
195                                           1, &shcne);
196     if (!m_shellRegID)
197     {
198         ERR("Something went wrong, error %08x\n", GetLastError());
199     }
200 }
201 
_UnregisterChangeNotify()202 void CNSCBand::_UnregisterChangeNotify()
203 {
204     SHChangeNotifyDeregister(m_shellRegID);
205     m_shellRegID = 0;
206 }
207 
_DestroyTreeView()208 void CNSCBand::_DestroyTreeView()
209 {
210     TRACE("Cleaning up treeview...\n");
211     /* Remove all items of the treeview */
212     ::RevokeDragDrop(m_hwndTreeView);
213     TreeView_DeleteAllItems(m_hwndTreeView);
214     m_hwndTreeView.DestroyWindow();
215     m_pDesktop = NULL;
216     m_hRoot = NULL;
217     TRACE("Cleanup ok\n");
218 }
219 
_DestroyToolbar()220 void CNSCBand::_DestroyToolbar()
221 {
222     m_hwndToolbar.DestroyWindow();
223 }
224 
_CreateTreeView(HWND hwndParent)225 HRESULT CNSCBand::_CreateTreeView(HWND hwndParent)
226 {
227     RefreshFlags(&m_dwTVStyle, &m_dwTVExStyle, &m_dwEnumFlags);
228     HWND hwndTV = ::CreateWindowExW(m_dwTVExStyle, WC_TREEVIEWW, NULL, m_dwTVStyle, 0, 0, 0, 0,
229                                     hwndParent, (HMENU)UlongToHandle(IDW_TREEVIEW), instance, NULL);
230     ATLASSERT(hwndTV);
231     if (!hwndTV)
232         return E_FAIL;
233 
234     m_hwndTreeView.Attach(hwndTV);
235     ::RegisterDragDrop(m_hwndTreeView, dynamic_cast<IDropTarget*>(this));
236 
237     // Init the treeview here
238     HRESULT hr = SHGetDesktopFolder(&m_pDesktop);
239     if (FAILED_UNEXPECTEDLY(hr))
240         return hr;
241 
242     m_pidlRoot.Free();
243     hr = SHGetFolderLocation(m_hWnd, _GetRootCsidl(), NULL, 0, &m_pidlRoot);
244     if (FAILED_UNEXPECTEDLY(hr))
245         return hr;
246 
247     // Create image list and set
248     IImageList *piml;
249     hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml));
250     if (FAILED_UNEXPECTEDLY(hr))
251         return hr;
252 
253     TreeView_SetImageList(m_hwndTreeView, (HIMAGELIST)piml, TVSIL_NORMAL);
254     return S_OK;
255 }
256 
257 BOOL
_IsTreeItemInEnum(_In_ HTREEITEM hItem,_In_ IEnumIDList * pEnum)258 CNSCBand::_IsTreeItemInEnum(
259     _In_ HTREEITEM hItem,
260     _In_ IEnumIDList *pEnum)
261 {
262     CItemData* pItemData = GetItemData(hItem);
263     if (!pItemData)
264         return FALSE;
265 
266     pEnum->Reset();
267 
268     CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
269     while (pEnum->Next(1, &pidlTemp, NULL) == S_OK)
270     {
271         if (ILIsEqual(pidlTemp, pItemData->relativePidl))
272             return TRUE;
273 
274         pidlTemp.Free();
275     }
276 
277     return FALSE;
278 }
279 
280 BOOL
_TreeItemHasThisChild(_In_ HTREEITEM hItem,_In_ PCITEMID_CHILD pidlChild)281 CNSCBand::_TreeItemHasThisChild(
282     _In_ HTREEITEM hItem,
283     _In_ PCITEMID_CHILD pidlChild)
284 {
285     for (hItem = TreeView_GetChild(m_hwndTreeView, hItem); hItem;
286          hItem = TreeView_GetNextSibling(m_hwndTreeView, hItem))
287     {
288         CItemData* pItemData = GetItemData(hItem);
289         if (ILIsEqual(pItemData->relativePidl, pidlChild))
290             return TRUE;
291     }
292 
293     return FALSE;
294 }
295 
296 HRESULT
_GetItemEnum(_Out_ CComPtr<IEnumIDList> & pEnum,_In_ HTREEITEM hItem,_Out_opt_ IShellFolder ** ppFolder)297 CNSCBand::_GetItemEnum(
298     _Out_ CComPtr<IEnumIDList>& pEnum,
299     _In_ HTREEITEM hItem,
300     _Out_opt_ IShellFolder **ppFolder)
301 {
302     CComPtr<IShellFolder> psfDesktop;
303     HRESULT hr = SHGetDesktopFolder(&psfDesktop);
304     if (FAILED_UNEXPECTEDLY(hr))
305         return hr;
306 
307     CComPtr<IShellFolder> pFolder;
308     if (!ppFolder)
309         ppFolder = &pFolder;
310 
311     if (hItem == m_hRoot && hItem)
312     {
313         *ppFolder = psfDesktop;
314         (*ppFolder)->AddRef();
315     }
316     else
317     {
318         CItemData* pItemData = GetItemData(hItem);
319         if (!pItemData && hItem == TVI_ROOT && !_WantsRootItem())
320             hr = psfDesktop->BindToObject(m_pidlRoot, NULL, IID_PPV_ARG(IShellFolder, ppFolder));
321         else
322             hr = psfDesktop->BindToObject(pItemData->absolutePidl, NULL, IID_PPV_ARG(IShellFolder, ppFolder));
323         if (FAILED_UNEXPECTEDLY(hr))
324             return hr;
325     }
326 
327     return (*ppFolder)->EnumObjects(NULL, _GetEnumFlags(), &pEnum);
328 }
329 
_ItemHasAnyChild(_In_ HTREEITEM hItem)330 BOOL CNSCBand::_ItemHasAnyChild(_In_ HTREEITEM hItem)
331 {
332     CComPtr<IEnumIDList> pEnum;
333     HRESULT hr = _GetItemEnum(pEnum, hItem);
334     if (FAILED(hr))
335         return FALSE;
336 
337     CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
338     hr = pEnum->Next(1, &pidlTemp, NULL);
339     return SUCCEEDED(hr);
340 }
341 
_RefreshRecurse(_In_ HTREEITEM hTarget)342 void CNSCBand::_RefreshRecurse(_In_ HTREEITEM hTarget)
343 {
344     CComPtr<IEnumIDList> pEnum;
345     HRESULT hrEnum = _GetItemEnum(pEnum, hTarget);
346 
347     // Delete zombie items
348     HTREEITEM hItem, hNextItem;
349     for (hItem = TreeView_GetChild(m_hwndTreeView, hTarget); hItem; hItem = hNextItem)
350     {
351         hNextItem = TreeView_GetNextSibling(m_hwndTreeView, hItem);
352 
353         if (SUCCEEDED(hrEnum) && !_IsTreeItemInEnum(hItem, pEnum))
354             TreeView_DeleteItem(m_hwndTreeView, hItem);
355     }
356 
357     pEnum = NULL;
358     hrEnum = _GetItemEnum(pEnum, hTarget);
359 
360     CItemData* pItemData = ((hTarget == TVI_ROOT) ? NULL : GetItemData(hTarget));
361 
362     // Insert new items and update items
363     if (SUCCEEDED(hrEnum))
364     {
365         CComHeapPtr<ITEMIDLIST_RELATIVE> pidlTemp;
366         while (pEnum->Next(1, &pidlTemp, NULL) == S_OK)
367         {
368             if (!_TreeItemHasThisChild(hTarget, pidlTemp))
369             {
370                 if (pItemData)
371                 {
372                     CComHeapPtr<ITEMIDLIST> pidlAbsolute(ILCombine(pItemData->absolutePidl, pidlTemp));
373                     _InsertItem(hTarget, pidlAbsolute, pidlTemp, TRUE);
374                 }
375                 else
376                 {
377                     CComHeapPtr<ITEMIDLIST> pidlAbsolute(ILCombine(m_pidlRoot, pidlTemp));
378                     _InsertItem(hTarget, pidlAbsolute, pidlTemp, TRUE);
379                 }
380             }
381             pidlTemp.Free();
382         }
383     }
384 
385     // Update children and recurse
386     for (hItem = TreeView_GetChild(m_hwndTreeView, hTarget); hItem; hItem = hNextItem)
387     {
388         hNextItem = TreeView_GetNextSibling(m_hwndTreeView, hItem);
389 
390         TV_ITEMW item = { TVIF_HANDLE | TVIF_CHILDREN };
391         item.hItem = hItem;
392         item.cChildren = _ItemHasAnyChild(hItem);
393         TreeView_SetItem(m_hwndTreeView, &item);
394 
395         if (TreeView_GetItemState(m_hwndTreeView, hItem, TVIS_EXPANDEDONCE) & TVIS_EXPANDEDONCE)
396             _RefreshRecurse(hItem);
397     }
398 }
399 
_Refresh()400 void CNSCBand::_Refresh()
401 {
402     m_hwndTreeView.SendMessage(WM_SETREDRAW, FALSE, 0);
403     _RefreshRecurse(_WantsRootItem() ? m_hRoot : TVI_ROOT);
404     m_hwndTreeView.SendMessage(WM_SETREDRAW, TRUE, 0);
405 }
406 
OnTimer(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)407 LRESULT CNSCBand::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
408 {
409     KillTimer(wParam);
410 
411     if (wParam == TIMER_ID_REFRESH)
412         _Refresh();
413 
414     return 0;
415 }
416 
417 void
OnChangeNotify(_In_opt_ LPCITEMIDLIST pidl0,_In_opt_ LPCITEMIDLIST pidl1,_In_ LONG lEvent)418 CNSCBand::OnChangeNotify(
419     _In_opt_ LPCITEMIDLIST pidl0,
420     _In_opt_ LPCITEMIDLIST pidl1,
421     _In_ LONG lEvent)
422 {
423     switch (lEvent)
424     {
425         case SHCNE_DRIVEADD:
426         case SHCNE_MKDIR:
427         case SHCNE_CREATE:
428         case SHCNE_DRIVEREMOVED:
429         case SHCNE_RMDIR:
430         case SHCNE_DELETE:
431         case SHCNE_RENAMEFOLDER:
432         case SHCNE_RENAMEITEM:
433         case SHCNE_UPDATEDIR:
434         case SHCNE_UPDATEITEM:
435         case SHCNE_ASSOCCHANGED:
436         {
437             KillTimer(TIMER_ID_REFRESH);
438             SetTimer(TIMER_ID_REFRESH, 500, NULL);
439             break;
440         }
441         default:
442         {
443             TRACE("lEvent: 0x%08lX\n", lEvent);
444             break;
445         }
446     }
447 }
448 
449 HTREEITEM
_InsertItem(_In_opt_ HTREEITEM hParent,_Inout_ IShellFolder * psfParent,_In_ LPCITEMIDLIST pElt,_In_ LPCITEMIDLIST pEltRelative,_In_ BOOL bSort)450 CNSCBand::_InsertItem(
451     _In_opt_ HTREEITEM hParent,
452     _Inout_ IShellFolder *psfParent,
453     _In_ LPCITEMIDLIST pElt,
454     _In_ LPCITEMIDLIST pEltRelative,
455     _In_ BOOL bSort)
456 {
457     /* Get the attributes of the node */
458     SFGAOF attrs = SFGAO_STREAM | SFGAO_HASSUBFOLDER;
459     HRESULT hr = psfParent->GetAttributesOf(1, &pEltRelative, &attrs);
460     if (FAILED_UNEXPECTEDLY(hr))
461         return NULL;
462 
463     /* Get the name of the node */
464     WCHAR wszDisplayName[MAX_PATH];
465     STRRET strret;
466     hr = psfParent->GetDisplayNameOf(pEltRelative, SHGDN_INFOLDER, &strret);
467     if (FAILED_UNEXPECTEDLY(hr))
468         return NULL;
469 
470     hr = StrRetToBufW(&strret, pEltRelative, wszDisplayName, MAX_PATH);
471     if (FAILED_UNEXPECTEDLY(hr))
472         return NULL;
473 
474     /* Get the icon of the node */
475     INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL);
476 
477     CItemData* pChildInfo = new CItemData;
478     if (!pChildInfo)
479     {
480         ERR("Failed to allocate CItemData\n");
481         return NULL;
482     }
483     pChildInfo->absolutePidl.Attach(ILClone(pElt));
484     pChildInfo->relativePidl.Attach(ILClone(pEltRelative));
485 
486     // Set up our treeview template
487     TV_INSERTSTRUCT tvInsert = { hParent, TVI_LAST };
488     tvInsert.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
489     tvInsert.item.cchTextMax = MAX_PATH;
490     tvInsert.item.pszText = wszDisplayName;
491     tvInsert.item.iImage = tvInsert.item.iSelectedImage = iIcon;
492     tvInsert.item.lParam = (LPARAM)pChildInfo;
493 
494     if (!(attrs & SFGAO_STREAM) && (attrs & SFGAO_HASSUBFOLDER))
495         tvInsert.item.cChildren = 1;
496 
497     HTREEITEM htiCreated = TreeView_InsertItem(m_hwndTreeView, &tvInsert);
498 
499     if (bSort)
500         _SortItems(hParent);
501 
502     return htiCreated;
503 }
504 
505 /* This is the slow version of the above method */
506 HTREEITEM
_InsertItem(_In_opt_ HTREEITEM hParent,_In_ LPCITEMIDLIST pElt,_In_ LPCITEMIDLIST pEltRelative,_In_ BOOL bSort)507 CNSCBand::_InsertItem(
508     _In_opt_ HTREEITEM hParent,
509     _In_ LPCITEMIDLIST pElt,
510     _In_ LPCITEMIDLIST pEltRelative,
511     _In_ BOOL bSort)
512 {
513     CComPtr<IShellFolder> psfFolder;
514     HRESULT hr = SHBindToParent(pElt, IID_PPV_ARG(IShellFolder, &psfFolder), NULL);
515     if (FAILED_UNEXPECTEDLY(hr))
516         return NULL;
517 
518     return _InsertItem(hParent, psfFolder, pElt, pEltRelative, bSort);
519 }
520 
_InsertSubitems(HTREEITEM hItem,LPCITEMIDLIST entry)521 BOOL CNSCBand::_InsertSubitems(HTREEITEM hItem, LPCITEMIDLIST entry)
522 {
523     ULONG fetched = 1, uItemCount = 0;
524 
525     CComPtr<IEnumIDList> pEnum;
526     CComPtr<IShellFolder> pFolder;
527     HRESULT hr = _GetItemEnum(pEnum, hItem, &pFolder);
528     if (FAILED_UNEXPECTEDLY(hr))
529         return FALSE;
530 
531     /* Don't redraw while we add stuff into the tree */
532     m_hwndTreeView.SendMessage(WM_SETREDRAW, FALSE, 0);
533 
534     LPITEMIDLIST pidlSub;
535     while (SUCCEEDED(pEnum->Next(1, &pidlSub, &fetched)) && pidlSub && fetched)
536     {
537         LPITEMIDLIST pidlSubComplete;
538         pidlSubComplete = ILCombine(entry, pidlSub);
539 
540         if (_InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE))
541             ++uItemCount;
542 
543         ILFree(pidlSubComplete);
544         ILFree(pidlSub);
545     }
546 
547     /* Let's do sorting */
548     _SortItems(hItem);
549 
550     /* Now we can redraw */
551     m_hwndTreeView.SendMessage(WM_SETREDRAW, TRUE, 0);
552 
553     return (uItemCount > 0);
554 }
555 
556 // *** message handlers ***
557 
OnCreate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)558 LRESULT CNSCBand::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
559 {
560     if (FAILED_UNEXPECTEDLY(_CreateToolbar(m_hWnd)))
561         return -1;
562     if (FAILED_UNEXPECTEDLY(_CreateTreeView(m_hWnd)))
563         return -1;
564     return 0;
565 }
566 
OnDestroy(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)567 LRESULT CNSCBand::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
568 {
569     _DestroyTreeView();
570     _DestroyToolbar();
571     _UnregisterChangeNotify();
572     return 0;
573 }
574 
OnSize(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)575 LRESULT CNSCBand::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
576 {
577     if (!m_hwndTreeView)
578         return 0;
579 
580     RECT rc;
581     GetClientRect(&rc);
582     LONG cx = rc.right, cy = rc.bottom;
583 
584     RECT rcTB;
585     LONG cyTB = 0;
586     if (m_hwndToolbar)
587     {
588         m_hwndToolbar.SendMessage(TB_AUTOSIZE, 0, 0);
589         m_hwndToolbar.GetWindowRect(&rcTB);
590         cyTB = rcTB.bottom - rcTB.top;
591     }
592 
593     m_hwndTreeView.MoveWindow(0, cyTB, cx, cy - cyTB);
594     return 0;
595 }
596 
OnSetFocus(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)597 LRESULT CNSCBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
598 {
599     m_bFocused = TRUE;
600     IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast<IUnknown*>(this), TRUE);
601     bHandled = FALSE;
602     return 0;
603 }
604 
OnKillFocus(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)605 LRESULT CNSCBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
606 {
607     IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast<IUnknown*>(this), FALSE);
608     m_bFocused = FALSE;
609     return 0;
610 }
611 
_AddFavorite()612 HRESULT CNSCBand::_AddFavorite()
613 {
614     CComHeapPtr<ITEMIDLIST> pidlCurrent;
615     _GetCurrentLocation(&pidlCurrent);
616 
617     WCHAR szCurDir[MAX_PATH];
618     if (!ILGetDisplayName(pidlCurrent, szCurDir))
619     {
620         FIXME("\n");
621         return E_FAIL;
622     }
623 
624     WCHAR szPath[MAX_PATH], szSuffix[32];
625     SHGetSpecialFolderPathW(m_hWnd, szPath, CSIDL_FAVORITES, TRUE);
626     PathAppendW(szPath, PathFindFileNameW(szCurDir));
627 
628     const INT ich = lstrlenW(szPath);
629     for (INT iTry = 2; iTry <= 9999; ++iTry)
630     {
631         PathAddExtensionW(szPath, L".lnk");
632         if (!PathFileExistsW(szPath))
633             break;
634         szPath[ich] = UNICODE_NULL;
635         wsprintfW(szSuffix, L" (%d)", iTry);
636         lstrcatW(szPath, szSuffix);
637     }
638 
639     TRACE("%S, %S\n", szCurDir, szPath);
640 
641     return SHDOCVW_CreateShortcut(szPath, pidlCurrent, NULL);
642 }
643 
OnCommand(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)644 LRESULT CNSCBand::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
645 {
646     switch (LOWORD(wParam))
647     {
648         case ID_ADD:
649         {
650             _AddFavorite();
651             break;
652         }
653         case ID_ORGANIZE:
654         {
655             SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_INVOKEIDLIST };
656             sei.hwnd = m_hWnd;
657             sei.nShow = SW_SHOWNORMAL;
658             sei.lpIDList = m_pidlRoot;
659             ::ShellExecuteExW(&sei);
660             break;
661         }
662     }
663     return 0;
664 }
665 
OnTreeItemExpanding(_In_ LPNMTREEVIEW pnmtv)666 BOOL CNSCBand::OnTreeItemExpanding(_In_ LPNMTREEVIEW pnmtv)
667 {
668     CItemData *pItemData;
669 
670     if (pnmtv->action == TVE_COLLAPSE)
671     {
672         if (pnmtv->itemNew.hItem == m_hRoot)
673         {
674             // Prenvent root from collapsing
675             pnmtv->itemNew.mask |= TVIF_STATE;
676             pnmtv->itemNew.stateMask |= TVIS_EXPANDED;
677             pnmtv->itemNew.state &= ~TVIS_EXPANDED;
678             pnmtv->action = TVE_EXPAND;
679             return TRUE;
680         }
681     }
682 
683     if (pnmtv->action == TVE_EXPAND)
684     {
685         // Grab our directory PIDL
686         pItemData = GetItemData(pnmtv->itemNew.hItem);
687         // We have it, let's try
688         if (pItemData && !pItemData->expanded)
689         {
690             if (_InsertSubitems(pnmtv->itemNew.hItem, pItemData->absolutePidl))
691             {
692                 pItemData->expanded = TRUE;
693             }
694             else
695             {
696                 // remove subitem "+" since we failed to add subitems
697                 TVITEMW tvItem = { TVIF_CHILDREN, pnmtv->itemNew.hItem };
698                 tvItem.cChildren = 0;
699                 TreeView_SetItem(m_hwndTreeView, &tvItem);
700             }
701         }
702     }
703     return FALSE;
704 }
705 
OnTreeItemDeleted(_In_ LPNMTREEVIEW pnmtv)706 BOOL CNSCBand::OnTreeItemDeleted(_In_ LPNMTREEVIEW pnmtv)
707 {
708     // Navigate to parent when deleting selected item
709     HTREEITEM hItem = pnmtv->itemOld.hItem;
710     HTREEITEM hParent = TreeView_GetParent(m_hwndTreeView, hItem);
711     if (hParent && TreeView_GetSelection(m_hwndTreeView) == hItem)
712         TreeView_SelectItem(m_hwndTreeView, hParent);
713 
714     /* Destroy memory associated to our node */
715     CItemData* pItemData = GetItemData(hItem);
716     if (!pItemData)
717         return FALSE;
718 
719     delete pItemData;
720 
721     return TRUE;
722 }
723 
_OnSelectionChanged(_In_ LPNMTREEVIEW pnmtv)724 void CNSCBand::_OnSelectionChanged(_In_ LPNMTREEVIEW pnmtv)
725 {
726     HTREEITEM hItem = pnmtv->itemNew.hItem;
727     if (!hItem)
728         return;
729     CItemData* pItemData = GetItemData(hItem);
730     if (pItemData)
731         OnSelectionChanged(pItemData->absolutePidl);
732 }
733 
OnTreeItemDragging(_In_ LPNMTREEVIEW pnmtv,_In_ BOOL isRightClick)734 void CNSCBand::OnTreeItemDragging(_In_ LPNMTREEVIEW pnmtv, _In_ BOOL isRightClick)
735 {
736     CItemData* pItemData = GetItemData(pnmtv->itemNew.hItem);
737     if (!pItemData)
738         return;
739 
740     HRESULT hr;
741     CComPtr<IShellFolder> pSrcFolder;
742     LPCITEMIDLIST pLast;
743     hr = SHBindToParent(pItemData->absolutePidl, IID_PPV_ARG(IShellFolder, &pSrcFolder), &pLast);
744     if (FAILED_UNEXPECTEDLY(hr))
745         return;
746 
747     SFGAOF attrs = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK;
748     pSrcFolder->GetAttributesOf(1, &pLast, &attrs);
749 
750     DWORD dwEffect = 0;
751     if (attrs & SFGAO_CANCOPY)
752         dwEffect |= DROPEFFECT_COPY;
753     if (attrs & SFGAO_CANMOVE)
754         dwEffect |= DROPEFFECT_MOVE;
755     if (attrs & SFGAO_CANLINK)
756         dwEffect |= DROPEFFECT_LINK;
757 
758     CComPtr<IDataObject> pObj;
759     hr = pSrcFolder->GetUIObjectOf(m_hWnd, 1, &pLast, IID_IDataObject, 0, (LPVOID*)&pObj);
760     if (FAILED_UNEXPECTEDLY(hr))
761         return;
762 
763     DoDragDrop(pObj, this, dwEffect, &dwEffect);
764 }
765 
OnBeginLabelEdit(_In_ LPNMTVDISPINFO dispInfo)766 LRESULT CNSCBand::OnBeginLabelEdit(_In_ LPNMTVDISPINFO dispInfo)
767 {
768     // TODO: put this in a function ? (mostly copypasta from CDefView)
769     DWORD dwAttr = SFGAO_CANRENAME;
770     CComPtr<IShellFolder> pParent;
771     LPCITEMIDLIST pChild;
772     HRESULT hr;
773 
774     CItemData *info = GetItemData(dispInfo->item.hItem);
775     if (!info)
776         return FALSE;
777 
778     hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pChild);
779     if (FAILED_UNEXPECTEDLY(hr))
780         return FALSE;
781 
782     hr = pParent->GetAttributesOf(1, &pChild, &dwAttr);
783     if (SUCCEEDED(hr) && (dwAttr & SFGAO_CANRENAME))
784     {
785         m_isEditing = TRUE;
786         m_oldSelected = NULL;
787         return FALSE;
788     }
789 
790     return TRUE;
791 }
792 
_UpdateBrowser(LPCITEMIDLIST pidlGoto)793 HRESULT CNSCBand::_UpdateBrowser(LPCITEMIDLIST pidlGoto)
794 {
795     CComPtr<IShellBrowser> pBrowserService;
796     HRESULT hr = IUnknown_QueryService(m_pSite, SID_STopLevelBrowser,
797                                        IID_PPV_ARG(IShellBrowser, &pBrowserService));
798     if (FAILED_UNEXPECTEDLY(hr))
799         return hr;
800 
801     hr = pBrowserService->BrowseObject(pidlGoto, SBSP_SAMEBROWSER | SBSP_ABSOLUTE);
802     if (FAILED_UNEXPECTEDLY(hr))
803         return hr;
804 
805     return S_OK;
806 }
807 
OnEndLabelEdit(_In_ LPNMTVDISPINFO dispInfo)808 LRESULT CNSCBand::OnEndLabelEdit(_In_ LPNMTVDISPINFO dispInfo)
809 {
810     CItemData *info = GetItemData(dispInfo->item.hItem);
811     HRESULT hr;
812 
813     m_isEditing = FALSE;
814     if (m_oldSelected)
815     {
816         ++m_mtxBlockNavigate;
817         TreeView_SelectItem(m_hwndTreeView, m_oldSelected);
818         --m_mtxBlockNavigate;
819     }
820 
821     if (!dispInfo->item.pszText)
822         return FALSE;
823 
824     CComPtr<IShellFolder> pParent;
825     LPCITEMIDLIST pidlChild;
826     BOOL RenamedCurrent = _IsCurrentLocation(info->absolutePidl) == S_OK;
827 
828     hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild);
829     if (FAILED_UNEXPECTEDLY(hr))
830         return FALSE;
831 
832     CComHeapPtr<ITEMIDLIST> pidlNew;
833     hr = pParent->SetNameOf(m_hWnd, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew);
834     if (SUCCEEDED(hr) && pidlNew)
835     {
836         CComPtr<IPersistFolder2> pPersist;
837         hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist));
838         if (FAILED_UNEXPECTEDLY(hr))
839             return FALSE;
840 
841         CComHeapPtr<ITEMIDLIST> pidlParent;
842         hr = pPersist->GetCurFolder(&pidlParent);
843         if (FAILED_UNEXPECTEDLY(hr))
844             return FALSE;
845 
846         CComHeapPtr<ITEMIDLIST> pidlNewAbs(ILCombine(pidlParent, pidlNew));
847         if (RenamedCurrent)
848         {
849             _UpdateBrowser(pidlNewAbs);
850         }
851         else
852         {
853             // Tell everyone if SetNameOf forgot, this causes IShellView to update itself when we rename a child
854             SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, info->absolutePidl, pidlNewAbs);
855         }
856 
857         return TRUE;
858     }
859 
860     return FALSE;
861 }
862 
OnNotify(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)863 LRESULT CNSCBand::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
864 {
865     NMHDR *pnmhdr = (NMHDR*)lParam;
866     switch (pnmhdr->code)
867     {
868         case TVN_ITEMEXPANDING:
869             return OnTreeItemExpanding((LPNMTREEVIEW)lParam);
870         //case TVN_SINGLEEXPAND:
871         case TVN_SELCHANGED:
872             if (pnmhdr->hwndFrom == m_hwndTreeView)
873                 _OnSelectionChanged((LPNMTREEVIEW)lParam);
874             break;
875         case TVN_DELETEITEM:
876             OnTreeItemDeleted((LPNMTREEVIEW)lParam);
877             break;
878         case NM_CLICK:
879         case NM_RCLICK:
880             if (pnmhdr->hwndFrom == m_hwndTreeView)
881             {
882                 TVHITTESTINFO HitTest;
883                 ::GetCursorPos(&HitTest.pt);
884                 ::ScreenToClient(m_hwndTreeView, &HitTest.pt);
885                 TreeView_HitTest(m_hwndTreeView, &HitTest);
886 
887                 if (HitTest.flags & (TVHT_ABOVE | TVHT_BELOW | TVHT_NOWHERE))
888                     return TRUE; // Prevents click processing
889 
890                 if (HitTest.flags & TVHT_ONITEMBUTTON) // [+] / [-]
891                     break; // Do default processing
892 
893                 // Generate selection notification even if same item
894                 m_hwndTreeView.SendMessage(WM_SETREDRAW, FALSE, 0);
895                 TreeView_SelectItem(m_hwndTreeView, NULL);
896                 TreeView_SelectItem(m_hwndTreeView, HitTest.hItem);
897                 m_hwndTreeView.SendMessage(WM_SETREDRAW, TRUE, 0);
898 
899                 if (pnmhdr->code == NM_CLICK)
900                     return TRUE; // Prevents click processing
901             }
902             break;
903         case TVN_BEGINDRAG:
904         case TVN_BEGINRDRAG:
905             OnTreeItemDragging((LPNMTREEVIEW)lParam, pnmhdr->code == TVN_BEGINRDRAG);
906             break;
907         case TVN_BEGINLABELEDITW:
908             return OnBeginLabelEdit((LPNMTVDISPINFO)lParam);
909         case TVN_ENDLABELEDITW:
910             return OnEndLabelEdit((LPNMTVDISPINFO)lParam);
911         default:
912             break;
913     }
914 
915     return 0;
916 }
917 
918 // Temporary menu
919 struct CMenuTemp
920 {
921     HMENU m_hMenu = NULL;
CMenuTempCMenuTemp922     CMenuTemp(HMENU hMenu) : m_hMenu(hMenu)
923     {
924     }
~CMenuTempCMenuTemp925     ~CMenuTemp()
926     {
927         if (m_hMenu)
928             ::DestroyMenu(m_hMenu);
929     }
operator HMENUCMenuTemp930     operator HMENU() const
931     {
932         return m_hMenu;
933     }
934 };
935 
936 // *** ATL event handlers ***
OnContextMenu(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)937 LRESULT CNSCBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
938 {
939     HWND hwndTarget = reinterpret_cast<HWND>(wParam);
940     if (hwndTarget && (hwndTarget == m_hwndToolbar || hwndTarget == m_hWnd))
941     {
942         FIXME("Show 'Close Toolbar' menu\n");
943         return 0;
944     }
945 
946     HTREEITEM hItem = TreeView_GetSelection(m_hwndTreeView);
947     if (!hItem)
948         return 0;
949 
950     POINT pt = { (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam) };
951     if ((UINT)lParam == (UINT)-1)
952     {
953         RECT rc;
954         if (TreeView_GetItemRect(m_hwndTreeView, hItem, &rc, TRUE))
955         {
956             // Center of item rectangle
957             pt.x = (rc.left + rc.right) / 2;
958             pt.y = (rc.top + rc.bottom) / 2;
959         }
960         ClientToScreen(&pt);
961     }
962 
963     CItemData *info = GetItemData(hItem);
964     if (!info)
965     {
966         ERR("No node data, something has gone wrong\n");
967         return 0;
968     }
969 
970     CComPtr<IShellFolder> pFolder;
971     LPCITEMIDLIST pidlChild;
972     HRESULT hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pFolder), &pidlChild);
973     if (FAILED_UNEXPECTEDLY(hr))
974         return 0;
975 
976     CComPtr<IContextMenu> contextMenu;
977     hr = pFolder->GetUIObjectOf(m_hWnd, 1, &pidlChild, IID_NULL_PPV_ARG(IContextMenu, &contextMenu));
978     if (FAILED_UNEXPECTEDLY(hr))
979         return 0;
980 
981     IUnknown_SetSite(contextMenu, (IDeskBand *)this);
982 
983     UINT cmf = CMF_EXPLORE;
984     SFGAOF attr = SFGAO_CANRENAME;
985     hr = pFolder->GetAttributesOf(1, &pidlChild, &attr);
986     if (SUCCEEDED(hr) && (attr & SFGAO_CANRENAME))
987         cmf |= CMF_CANRENAME;
988 
989     CMenuTemp menuTemp(::CreatePopupMenu());
990     UINT idCmdFirst = max(FCIDM_SHVIEWFIRST, 1);
991     hr = contextMenu->QueryContextMenu(menuTemp, 0, idCmdFirst, FCIDM_SHVIEWLAST, cmf);
992     if (FAILED_UNEXPECTEDLY(hr))
993         return 0;
994 
995     enum { flags = TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON };
996     UINT uCommand = ::TrackPopupMenu(menuTemp, flags, pt.x, pt.y, 0, m_hWnd, NULL);
997     if (uCommand)
998     {
999         uCommand -= idCmdFirst;
1000 
1001         // Do DFM_CMD_RENAME in the treeview
1002         if ((cmf & CMF_CANRENAME) && SHELL_IsVerb(contextMenu, uCommand, L"rename"))
1003         {
1004             m_hwndTreeView.SetFocus();
1005             if (TreeView_EditLabel(m_hwndTreeView, hItem))
1006                 m_oldSelected = hItem;
1007             return 0;
1008         }
1009 
1010         hr = _ExecuteCommand(contextMenu, uCommand);
1011     }
1012 
1013     return TRUE;
1014 }
1015 
1016 // WM_USER_SHELLEVENT
OnShellEvent(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)1017 LRESULT CNSCBand::OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
1018 {
1019     // We use SHCNRF_NewDelivery method
1020     HANDLE hChange = (HANDLE)wParam;
1021     DWORD dwProcID = (DWORD)lParam;
1022 
1023     PIDLIST_ABSOLUTE *ppidl = NULL;
1024     LONG lEvent;
1025     HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &ppidl, &lEvent);
1026     if (!hLock)
1027     {
1028         ERR("!hLock\n");
1029         return 0;
1030     }
1031 
1032     OnChangeNotify(ppidl[0], ppidl[1], (lEvent & ~SHCNE_INTERRUPT));
1033 
1034     SHChangeNotification_Unlock(hLock);
1035     return 0;
1036 }
1037 
1038 // *** IOleWindow ***
1039 
GetWindow(HWND * lphwnd)1040 STDMETHODIMP CNSCBand::GetWindow(HWND *lphwnd)
1041 {
1042     if (!lphwnd)
1043         return E_INVALIDARG;
1044     *lphwnd = m_hWnd;
1045     return S_OK;
1046 }
1047 
ContextSensitiveHelp(BOOL fEnterMode)1048 STDMETHODIMP CNSCBand::ContextSensitiveHelp(BOOL fEnterMode)
1049 {
1050     UNIMPLEMENTED;
1051     return E_NOTIMPL;
1052 }
1053 
1054 // *** IDockingWindow ***
1055 
CloseDW(DWORD dwReserved)1056 STDMETHODIMP CNSCBand::CloseDW(DWORD dwReserved)
1057 {
1058     // We do nothing, we don't have anything to save yet
1059     TRACE("CloseDW called\n");
1060     return S_OK;
1061 }
1062 
ResizeBorderDW(const RECT * prcBorder,IUnknown * punkToolbarSite,BOOL fReserved)1063 STDMETHODIMP CNSCBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
1064 {
1065     /* Must return E_NOTIMPL according to MSDN */
1066     return E_NOTIMPL;
1067 }
1068 
ShowDW(BOOL fShow)1069 STDMETHODIMP CNSCBand::ShowDW(BOOL fShow)
1070 {
1071     m_fVisible = fShow;
1072     ShowWindow(fShow ? SW_SHOW : SW_HIDE);
1073     return S_OK;
1074 }
1075 
1076 // *** IDeskBand ***
1077 
GetBandInfo(DWORD dwBandID,DWORD dwViewMode,DESKBANDINFO * pdbi)1078 STDMETHODIMP CNSCBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
1079 {
1080     if (!pdbi)
1081         return E_INVALIDARG;
1082 
1083     m_dwBandID = dwBandID;
1084 
1085     if (pdbi->dwMask & DBIM_MINSIZE)
1086     {
1087         pdbi->ptMinSize.x = 200;
1088         pdbi->ptMinSize.y = 30;
1089     }
1090 
1091     if (pdbi->dwMask & DBIM_MAXSIZE)
1092         pdbi->ptMaxSize.y = -1;
1093 
1094     if (pdbi->dwMask & DBIM_INTEGRAL)
1095         pdbi->ptIntegral.y = 1;
1096 
1097     if (pdbi->dwMask & DBIM_ACTUAL)
1098     {
1099         pdbi->ptActual.x = 200;
1100         pdbi->ptActual.y = 30;
1101     }
1102 
1103     if (pdbi->dwMask & DBIM_TITLE)
1104     {
1105         _GetTitle(pdbi->wszTitle, _countof(pdbi->wszTitle));
1106     }
1107 
1108     if (pdbi->dwMask & DBIM_MODEFLAGS)
1109         pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
1110 
1111     if (pdbi->dwMask & DBIM_BKCOLOR)
1112         pdbi->dwMask &= ~DBIM_BKCOLOR;
1113 
1114     return S_OK;
1115 }
1116 
1117 // *** IObjectWithSite ***
1118 
SetSite(IUnknown * pUnkSite)1119 STDMETHODIMP CNSCBand::SetSite(IUnknown *pUnkSite)
1120 {
1121     HRESULT hr;
1122 
1123     if (pUnkSite == m_pSite)
1124         return S_OK;
1125 
1126     TRACE("SetSite called\n");
1127 
1128     if (!pUnkSite)
1129     {
1130         DestroyWindow();
1131         m_hWnd = NULL;
1132     }
1133 
1134     if (pUnkSite != m_pSite)
1135         m_pSite = NULL;
1136 
1137     if (!pUnkSite)
1138         return S_OK;
1139 
1140     HWND hwndParent;
1141     hr = IUnknown_GetWindow(pUnkSite, &hwndParent);
1142     if (FAILED_UNEXPECTEDLY(hr))
1143         return E_INVALIDARG;
1144 
1145     m_pSite = pUnkSite;
1146 
1147     if (m_hWnd)
1148     {
1149         SetParent(hwndParent); // Change its parent
1150     }
1151     else
1152     {
1153         enum { style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN };
1154         this->Create(hwndParent, NULL, NULL, style, 0, 0U, NULL);
1155     }
1156 
1157     _RegisterChangeNotify();
1158 
1159     return S_OK;
1160 }
1161 
GetSite(REFIID riid,void ** ppvSite)1162 STDMETHODIMP CNSCBand::GetSite(REFIID riid, void **ppvSite)
1163 {
1164     if (!ppvSite)
1165         return E_POINTER;
1166     *ppvSite = m_pSite;
1167     return S_OK;
1168 }
1169 
1170 // *** IOleCommandTarget ***
1171 
QueryStatus(const GUID * pguidCmdGroup,ULONG cCmds,OLECMD prgCmds[],OLECMDTEXT * pCmdText)1172 STDMETHODIMP CNSCBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
1173 {
1174     UNIMPLEMENTED;
1175     return E_NOTIMPL;
1176 }
1177 
Exec(const GUID * pguidCmdGroup,DWORD nCmdID,DWORD nCmdexecopt,VARIANT * pvaIn,VARIANT * pvaOut)1178 STDMETHODIMP CNSCBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
1179 {
1180     UNIMPLEMENTED;
1181     return E_NOTIMPL;
1182 }
1183 
1184 // *** IServiceProvider ***
1185 
QueryService(REFGUID guidService,REFIID riid,void ** ppvObject)1186 STDMETHODIMP CNSCBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
1187 {
1188     return IUnknown_QueryService(m_pSite, guidService, riid, ppvObject);
1189 }
1190 
1191 // *** IContextMenu ***
1192 
QueryContextMenu(HMENU hmenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)1193 STDMETHODIMP CNSCBand::QueryContextMenu(
1194     HMENU hmenu,
1195     UINT indexMenu,
1196     UINT idCmdFirst,
1197     UINT idCmdLast,
1198     UINT uFlags)
1199 {
1200     UNIMPLEMENTED;
1201     return E_NOTIMPL;
1202 }
1203 
InvokeCommand(LPCMINVOKECOMMANDINFO lpici)1204 STDMETHODIMP CNSCBand::InvokeCommand(
1205     LPCMINVOKECOMMANDINFO lpici)
1206 {
1207     UNIMPLEMENTED;
1208     return E_NOTIMPL;
1209 }
1210 
GetCommandString(UINT_PTR idCmd,UINT uType,UINT * pwReserved,LPSTR pszName,UINT cchMax)1211 STDMETHODIMP CNSCBand::GetCommandString(
1212     UINT_PTR idCmd,
1213     UINT uType,
1214     UINT *pwReserved,
1215     LPSTR pszName,
1216     UINT cchMax)
1217 {
1218     UNIMPLEMENTED;
1219     return E_NOTIMPL;
1220 }
1221 
1222 // *** IInputObject ***
1223 
UIActivateIO(BOOL fActivate,LPMSG lpMsg)1224 STDMETHODIMP CNSCBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
1225 {
1226     if (fActivate)
1227     {
1228         m_hwndTreeView.SetFocus();
1229     }
1230 
1231     if (lpMsg)
1232     {
1233         TranslateMessage(lpMsg);
1234         DispatchMessage(lpMsg);
1235     }
1236 
1237     return S_OK;
1238 }
1239 
HasFocusIO()1240 STDMETHODIMP CNSCBand::HasFocusIO()
1241 {
1242     return m_bFocused ? S_OK : S_FALSE;
1243 }
1244 
TranslateAcceleratorIO(LPMSG lpMsg)1245 STDMETHODIMP CNSCBand::TranslateAcceleratorIO(LPMSG lpMsg)
1246 {
1247     if (lpMsg->hwnd == m_hWnd ||
1248         (m_isEditing && IsChild(lpMsg->hwnd)))
1249     {
1250         TranslateMessage(lpMsg);
1251         DispatchMessage(lpMsg);
1252         return S_OK;
1253     }
1254 
1255     return S_FALSE;
1256 }
1257 
1258 // *** IPersist ***
1259 
GetClassID(CLSID * pClassID)1260 STDMETHODIMP CNSCBand::GetClassID(CLSID *pClassID)
1261 {
1262     return E_NOTIMPL;
1263 }
1264 
1265 // *** IPersistStream ***
1266 
IsDirty()1267 STDMETHODIMP CNSCBand::IsDirty()
1268 {
1269     UNIMPLEMENTED;
1270     return E_NOTIMPL;
1271 }
1272 
Load(IStream * pStm)1273 STDMETHODIMP CNSCBand::Load(IStream *pStm)
1274 {
1275     UNIMPLEMENTED;
1276     return E_NOTIMPL;
1277 }
1278 
Save(IStream * pStm,BOOL fClearDirty)1279 STDMETHODIMP CNSCBand::Save(IStream *pStm, BOOL fClearDirty)
1280 {
1281     UNIMPLEMENTED;
1282     return E_NOTIMPL;
1283 }
1284 
GetSizeMax(ULARGE_INTEGER * pcbSize)1285 STDMETHODIMP CNSCBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
1286 {
1287     UNIMPLEMENTED;
1288     return E_NOTIMPL;
1289 }
1290 
1291 // *** IWinEventHandler ***
1292 
OnWinEvent(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT * theResult)1293 STDMETHODIMP CNSCBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
1294 {
1295     return S_OK;
1296 }
1297 
IsWindowOwner(HWND hWnd)1298 STDMETHODIMP CNSCBand::IsWindowOwner(HWND hWnd)
1299 {
1300     return SHIsChildOrSelf(m_hWnd, hWnd);
1301 }
1302 
1303 // *** IBandNavigate ***
1304 
Select(LPCITEMIDLIST pidl)1305 STDMETHODIMP CNSCBand::Select(LPCITEMIDLIST pidl)
1306 {
1307     UNIMPLEMENTED;
1308     return E_NOTIMPL;
1309 }
1310 
1311 // *** INamespaceProxy ***
1312 
1313 // Returns the ITEMIDLIST that should be navigated when an item is invoked.
GetNavigateTarget(_In_ PCIDLIST_ABSOLUTE pidl,_Out_ PIDLIST_ABSOLUTE * ppidlTarget,_Out_ ULONG * pulAttrib)1314 STDMETHODIMP CNSCBand::GetNavigateTarget(
1315     _In_ PCIDLIST_ABSOLUTE pidl,
1316     _Out_ PIDLIST_ABSOLUTE *ppidlTarget,
1317     _Out_ ULONG *pulAttrib)
1318 {
1319     *pulAttrib = 0;
1320     WCHAR szPath[MAX_PATH];
1321     if (!SHGetPathFromIDListW(pidl, szPath))
1322         return E_FAIL;
1323 
1324     if (lstrcmpiW(PathFindExtensionW(szPath), L".lnk") == 0) // shortcut file?
1325     {
1326         WCHAR szTarget[MAX_PATH];
1327         HRESULT hr = SHDOCVW_GetPathOfShortcut(m_hWnd, szPath, szTarget);
1328         if (SUCCEEDED(hr))
1329         {
1330             lstrcpynW(szPath, szTarget, _countof(szPath));
1331             *pulAttrib |= SFGAO_LINK;
1332         }
1333     }
1334 
1335     if (PathIsDirectoryW(szPath) || PathIsRootW(szPath))
1336         *pulAttrib |= SFGAO_FOLDER;
1337 
1338     *ppidlTarget = ILCreateFromPathW(szPath);
1339     return S_OK;
1340 }
1341 
1342 // Handles a user action on an item.
Invoke(_In_ PCIDLIST_ABSOLUTE pidl)1343 STDMETHODIMP CNSCBand::Invoke(_In_ PCIDLIST_ABSOLUTE pidl)
1344 {
1345     UNIMPLEMENTED;
1346     return E_NOTIMPL;
1347 }
1348 
1349 // Called when the user has selected an item.
OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl)1350 STDMETHODIMP CNSCBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl)
1351 {
1352     return S_OK;
1353 }
1354 
1355 // Returns flags used to update the tree control.
RefreshFlags(_Out_ DWORD * pdwStyle,_Out_ DWORD * pdwExStyle,_Out_ DWORD * dwEnum)1356 STDMETHODIMP CNSCBand::RefreshFlags(
1357     _Out_ DWORD *pdwStyle,
1358     _Out_ DWORD *pdwExStyle,
1359     _Out_ DWORD *dwEnum)
1360 {
1361     *pdwStyle = _GetTVStyle();
1362     *pdwExStyle = _GetTVExStyle();
1363     *dwEnum = _GetEnumFlags();
1364     return S_OK;
1365 }
1366 
CacheItem(_In_ PCIDLIST_ABSOLUTE pidl)1367 STDMETHODIMP CNSCBand::CacheItem(
1368     _In_ PCIDLIST_ABSOLUTE pidl)
1369 {
1370     UNIMPLEMENTED;
1371     return E_NOTIMPL;
1372 }
1373 
1374 // *** IDropTarget methods ***
DragEnter(IDataObject * pObj,DWORD glfKeyState,POINTL pt,DWORD * pdwEffect)1375 STDMETHODIMP CNSCBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1376 {
1377     ERR("Entering drag\n");
1378     m_pCurObject = pObj;
1379     m_oldSelected = TreeView_GetSelection(m_hwndTreeView);
1380     return DragOver(glfKeyState, pt, pdwEffect);
1381 }
1382 
DragOver(DWORD glfKeyState,POINTL pt,DWORD * pdwEffect)1383 STDMETHODIMP CNSCBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1384 {
1385     TVHITTESTINFO info;
1386     info.pt.x = pt.x;
1387     info.pt.y = pt.y;
1388     info.flags = TVHT_ONITEM;
1389     info.hItem = NULL;
1390     ScreenToClient(&info.pt);
1391 
1392     // Move to the item selected by the treeview (don't change right pane)
1393     TreeView_HitTest(m_hwndTreeView, &info);
1394 
1395     HRESULT hr;
1396     if (!info.hItem)
1397     {
1398         m_childTargetNode = NULL;
1399         m_pDropTarget = NULL;
1400         *pdwEffect = DROPEFFECT_NONE;
1401         return S_OK;
1402     }
1403 
1404     ++m_mtxBlockNavigate;
1405     TreeView_SelectItem(m_hwndTreeView, info.hItem);
1406     --m_mtxBlockNavigate;
1407 
1408     // Delegate to shell folder
1409     if (m_pDropTarget && info.hItem != m_childTargetNode)
1410         m_pDropTarget = NULL;
1411 
1412     if (info.hItem != m_childTargetNode)
1413     {
1414         CItemData *pItemData = GetItemData(info.hItem);
1415         if (!pItemData)
1416             return E_FAIL;
1417 
1418         CComPtr<IShellFolder> pFolder;
1419         if (_ILIsDesktop(pItemData->absolutePidl))
1420         {
1421             pFolder = m_pDesktop;
1422         }
1423         else
1424         {
1425             hr = m_pDesktop->BindToObject(pItemData->absolutePidl, 0, IID_PPV_ARG(IShellFolder, &pFolder));
1426             if (!SUCCEEDED(hr))
1427             {
1428                 /* Don't allow dnd since we couldn't get our folder object */
1429                 ERR("Can't bind to folder object\n");
1430                 *pdwEffect = DROPEFFECT_NONE;
1431                 return E_FAIL;
1432             }
1433         }
1434 
1435         hr = pFolder->CreateViewObject(m_hWnd, IID_PPV_ARG(IDropTarget, &m_pDropTarget));
1436         if (!SUCCEEDED(hr))
1437         {
1438             /* Don't allow dnd since we couldn't get our drop target */
1439             ERR("Can't get drop target for folder object\n");
1440             *pdwEffect = DROPEFFECT_NONE;
1441             return E_FAIL;
1442         }
1443 
1444         hr = m_pDropTarget->DragEnter(m_pCurObject, glfKeyState, pt, pdwEffect);
1445         m_childTargetNode = info.hItem;
1446     }
1447 
1448     if (m_pDropTarget)
1449         hr = m_pDropTarget->DragOver(glfKeyState, pt, pdwEffect);
1450 
1451     return S_OK;
1452 }
1453 
DragLeave()1454 STDMETHODIMP CNSCBand::DragLeave()
1455 {
1456     ++m_mtxBlockNavigate;
1457     TreeView_SelectItem(m_hwndTreeView, m_oldSelected);
1458     --m_mtxBlockNavigate;
1459     m_childTargetNode = NULL;
1460     if (m_pCurObject)
1461         m_pCurObject = NULL;
1462     return S_OK;
1463 }
1464 
Drop(IDataObject * pObj,DWORD glfKeyState,POINTL pt,DWORD * pdwEffect)1465 STDMETHODIMP CNSCBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1466 {
1467     if (!m_pDropTarget)
1468         return E_FAIL;
1469     m_pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect);
1470     DragLeave();
1471     return S_OK;
1472 }
1473 
1474 // *** IDropSource methods ***
1475 
QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)1476 STDMETHODIMP CNSCBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
1477 {
1478     if (fEscapePressed)
1479         return DRAGDROP_S_CANCEL;
1480     if ((grfKeyState & MK_LBUTTON) || (grfKeyState & MK_RBUTTON))
1481         return S_OK;
1482     return DRAGDROP_S_DROP;
1483 }
1484 
GiveFeedback(DWORD dwEffect)1485 STDMETHODIMP CNSCBand::GiveFeedback(DWORD dwEffect)
1486 {
1487     return DRAGDROP_S_USEDEFAULTCURSORS;
1488 }
1489