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