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 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 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 75 CNSCBand::CNSCBand() 76 { 77 SHDOCVW_LockModule(); 78 79 INITCOMMONCONTROLSEX iccx = { sizeof(iccx), ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES }; 80 ::InitCommonControlsEx(&iccx); 81 } 82 83 CNSCBand::~CNSCBand() 84 { 85 if (m_hToolbarImageList) 86 { 87 ImageList_Destroy(m_hToolbarImageList); 88 m_hToolbarImageList = NULL; 89 } 90 SHDOCVW_UnlockModule(); 91 } 92 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 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 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 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 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 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 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 202 void CNSCBand::_UnregisterChangeNotify() 203 { 204 SHChangeNotifyDeregister(m_shellRegID); 205 m_shellRegID = 0; 206 } 207 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 220 void CNSCBand::_DestroyToolbar() 221 { 222 m_hwndToolbar.DestroyWindow(); 223 } 224 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 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 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 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 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 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 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 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 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 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 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 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 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 567 LRESULT CNSCBand::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 568 { 569 _DestroyTreeView(); 570 _DestroyToolbar(); 571 _UnregisterChangeNotify(); 572 return 0; 573 } 574 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 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 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 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 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 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 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 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 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 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 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 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 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; 922 CMenuTemp(HMENU hMenu) : m_hMenu(hMenu) 923 { 924 } 925 ~CMenuTemp() 926 { 927 if (m_hMenu) 928 ::DestroyMenu(m_hMenu); 929 } 930 operator HMENU() const 931 { 932 return m_hMenu; 933 } 934 }; 935 936 // *** ATL event handlers *** 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 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 1040 STDMETHODIMP CNSCBand::GetWindow(HWND *lphwnd) 1041 { 1042 if (!lphwnd) 1043 return E_INVALIDARG; 1044 *lphwnd = m_hWnd; 1045 return S_OK; 1046 } 1047 1048 STDMETHODIMP CNSCBand::ContextSensitiveHelp(BOOL fEnterMode) 1049 { 1050 UNIMPLEMENTED; 1051 return E_NOTIMPL; 1052 } 1053 1054 // *** IDockingWindow *** 1055 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 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 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 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 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 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 1172 STDMETHODIMP CNSCBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) 1173 { 1174 UNIMPLEMENTED; 1175 return E_NOTIMPL; 1176 } 1177 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 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 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 1204 STDMETHODIMP CNSCBand::InvokeCommand( 1205 LPCMINVOKECOMMANDINFO lpici) 1206 { 1207 UNIMPLEMENTED; 1208 return E_NOTIMPL; 1209 } 1210 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 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 1240 STDMETHODIMP CNSCBand::HasFocusIO() 1241 { 1242 return m_bFocused ? S_OK : S_FALSE; 1243 } 1244 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 1260 STDMETHODIMP CNSCBand::GetClassID(CLSID *pClassID) 1261 { 1262 return E_NOTIMPL; 1263 } 1264 1265 // *** IPersistStream *** 1266 1267 STDMETHODIMP CNSCBand::IsDirty() 1268 { 1269 UNIMPLEMENTED; 1270 return E_NOTIMPL; 1271 } 1272 1273 STDMETHODIMP CNSCBand::Load(IStream *pStm) 1274 { 1275 UNIMPLEMENTED; 1276 return E_NOTIMPL; 1277 } 1278 1279 STDMETHODIMP CNSCBand::Save(IStream *pStm, BOOL fClearDirty) 1280 { 1281 UNIMPLEMENTED; 1282 return E_NOTIMPL; 1283 } 1284 1285 STDMETHODIMP CNSCBand::GetSizeMax(ULARGE_INTEGER *pcbSize) 1286 { 1287 UNIMPLEMENTED; 1288 return E_NOTIMPL; 1289 } 1290 1291 // *** IWinEventHandler *** 1292 1293 STDMETHODIMP CNSCBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) 1294 { 1295 return S_OK; 1296 } 1297 1298 STDMETHODIMP CNSCBand::IsWindowOwner(HWND hWnd) 1299 { 1300 return SHIsChildOrSelf(m_hWnd, hWnd); 1301 } 1302 1303 // *** IBandNavigate *** 1304 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. 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. 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. 1350 STDMETHODIMP CNSCBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) 1351 { 1352 return S_OK; 1353 } 1354 1355 // Returns flags used to update the tree control. 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 1367 STDMETHODIMP CNSCBand::CacheItem( 1368 _In_ PCIDLIST_ABSOLUTE pidl) 1369 { 1370 UNIMPLEMENTED; 1371 return E_NOTIMPL; 1372 } 1373 1374 // *** IDropTarget methods *** 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 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 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 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 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 1485 STDMETHODIMP CNSCBand::GiveFeedback(DWORD dwEffect) 1486 { 1487 return DRAGDROP_S_USEDEFAULTCURSORS; 1488 } 1489