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