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