1 /* 2 * PROJECT: ReactOS shdocvw 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Explorer bar 5 * COPYRIGHT: Copyright 2016 Sylvain Deverre <deverre.sylv@gmail.com> 6 * Copyright 2020-2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 7 */ 8 9 #include "objects.h" 10 11 #include <wine/debug.h> 12 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); 13 14 /* 15 * TODO: 16 * - Monitor correctly "external" shell interrupts (seems like we need to register/deregister them for each folder) 17 * - find and fix what cause explorer crashes sometimes (seems to be explorer that does more releases than addref) 18 * - TESTING 19 */ 20 21 CExplorerBand::CExplorerBand() 22 { 23 } 24 25 CExplorerBand::~CExplorerBand() 26 { 27 } 28 29 STDMETHODIMP CExplorerBand::GetClassID(CLSID *pClassID) 30 { 31 if (!pClassID) 32 return E_POINTER; 33 *pClassID = CLSID_ExplorerBand; 34 return S_OK; 35 } 36 37 INT CExplorerBand::_GetRootCsidl() 38 { 39 return CSIDL_DESKTOP; 40 } 41 42 DWORD CExplorerBand::_GetTVStyle() 43 { 44 // Remove TVS_SINGLEEXPAND for now since it has strange behaviour 45 return WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TVS_HASLINES | 46 TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_EDITLABELS /* | TVS_SINGLEEXPAND*/; 47 } 48 49 DWORD CExplorerBand::_GetTVExStyle() 50 { 51 return 0; 52 } 53 54 DWORD CExplorerBand::_GetEnumFlags() 55 { 56 return SHCONTF_FOLDERS; 57 } 58 59 BOOL CExplorerBand::_GetTitle(LPWSTR pszTitle, INT cchTitle) 60 { 61 return ::LoadStringW(instance, IDS_FOLDERSLABEL, pszTitle, cchTitle); 62 } 63 64 BOOL CExplorerBand::_WantsRootItem() 65 { 66 return TRUE; 67 } 68 69 // Called when the user has selected an item. 70 STDMETHODIMP CExplorerBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) 71 { 72 return Invoke(pidl); 73 } 74 75 // Handles a user action on an item. 76 STDMETHODIMP CExplorerBand::Invoke(_In_ PCIDLIST_ABSOLUTE pidl) 77 { 78 /* Prevents navigation if selection is initiated inside the band */ 79 if (m_mtxBlockNavigate) 80 return S_OK; 81 82 _UpdateBrowser(pidl); 83 m_hwndTreeView.SetFocus(); 84 return S_OK; 85 } 86 87 void CExplorerBand::_SortItems(HTREEITEM hParent) 88 { 89 TVSORTCB sortCallback; 90 sortCallback.hParent = hParent; 91 sortCallback.lpfnCompare = _CompareTreeItems; 92 sortCallback.lParam = (LPARAM)(PVOID)m_pDesktop; // m_pDesktop is not a pointer 93 TreeView_SortChildrenCB(m_hwndTreeView, &sortCallback, 0); 94 } 95 96 INT CALLBACK CExplorerBand::_CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3) 97 { 98 CItemData *info1 = (CItemData*)p1; 99 CItemData *info2 = (CItemData*)p2; 100 IShellFolder *pDesktop = (IShellFolder *)p3; 101 HRESULT hr = pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl); 102 if (FAILED(hr)) 103 return 0; 104 return (SHORT)HRESULT_CODE(hr); 105 } 106 107 HRESULT CExplorerBand::_CreateTreeView(HWND hwndParent) 108 { 109 HRESULT hr = CNSCBand::_CreateTreeView(hwndParent); 110 if (FAILED_UNEXPECTEDLY(hr)) 111 return hr; 112 113 // Insert the root node 114 m_hRoot = _InsertItem(NULL, m_pDesktop, m_pidlRoot, m_pidlRoot, FALSE); 115 if (!m_hRoot) 116 { 117 ERR("Failed to create root item\n"); 118 return E_FAIL; 119 } 120 TreeView_Expand(m_hwndTreeView, m_hRoot, TVE_EXPAND); 121 122 // Navigate to current folder position 123 _NavigateToCurrentFolder(); 124 125 // Register browser connection endpoint 126 CComPtr<IWebBrowser2> browserService; 127 hr = IUnknown_QueryService(m_pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); 128 if (FAILED_UNEXPECTEDLY(hr)) 129 return hr; 130 131 // Communicate via IDispatch 132 hr = AtlAdvise(browserService, dynamic_cast<IDispatch*>(this), DIID_DWebBrowserEvents, &m_adviseCookie); 133 if (FAILED_UNEXPECTEDLY(hr)) 134 return hr; 135 136 return hr; 137 } 138 139 void CExplorerBand::_DestroyTreeView() 140 { 141 CComPtr<IWebBrowser2> browserService; 142 HRESULT hr = IUnknown_QueryService(m_pSite, SID_SWebBrowserApp, 143 IID_PPV_ARG(IWebBrowser2, &browserService)); 144 if (FAILED_UNEXPECTEDLY(hr)) 145 return; 146 147 AtlUnadvise(browserService, DIID_DWebBrowserEvents, m_adviseCookie); 148 149 CNSCBand::_DestroyTreeView(); 150 } 151 152 // *** IDispatch methods *** 153 154 STDMETHODIMP CExplorerBand::GetTypeInfoCount(UINT *pctinfo) 155 { 156 UNIMPLEMENTED; 157 return E_NOTIMPL; 158 } 159 160 STDMETHODIMP CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) 161 { 162 UNIMPLEMENTED; 163 return E_NOTIMPL; 164 } 165 166 STDMETHODIMP CExplorerBand::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) 167 { 168 UNIMPLEMENTED; 169 return E_NOTIMPL; 170 } 171 172 STDMETHODIMP 173 CExplorerBand::Invoke( 174 DISPID dispIdMember, 175 REFIID riid, 176 LCID lcid, 177 WORD wFlags, 178 DISPPARAMS *pDispParams, 179 VARIANT *pVarResult, 180 EXCEPINFO *pExcepInfo, 181 UINT *puArgErr) 182 { 183 switch (dispIdMember) 184 { 185 case DISPID_DOWNLOADCOMPLETE: 186 case DISPID_NAVIGATECOMPLETE2: 187 { 188 TRACE("dispId %d received\n", dispIdMember); 189 _NavigateToCurrentFolder(); 190 return S_OK; 191 } 192 } 193 TRACE("Unknown dispid requested: %08x\n", dispIdMember); 194 return E_INVALIDARG; 195 } 196 197 BOOL CExplorerBand::_NavigateToCurrentFolder() 198 { 199 CComHeapPtr<ITEMIDLIST> pidl; 200 HRESULT hr = _GetCurrentLocation(&pidl); 201 if (FAILED_UNEXPECTEDLY(hr)) 202 return FALSE; 203 204 // Find PIDL into our explorer 205 ++m_mtxBlockNavigate; 206 HTREEITEM hItem; 207 BOOL result = _NavigateToPIDL(pidl, &hItem, TRUE, FALSE, TRUE); 208 --m_mtxBlockNavigate; 209 210 return result; 211 } 212 213 /** 214 * Navigate to a given PIDL in the treeview, and return matching tree item handle 215 * - dest: The absolute PIDL we should navigate in the treeview 216 * - item: Handle of the tree item matching the PIDL 217 * - bExpand: expand collapsed nodes in order to find the right element 218 * - bInsert: insert the element at the right place if we don't find it 219 * - bSelect: select the item after we found it 220 */ 221 BOOL 222 CExplorerBand::_NavigateToPIDL( 223 _In_ LPCITEMIDLIST dest, 224 _Out_ HTREEITEM *phItem, 225 _In_ BOOL bExpand, 226 _In_ BOOL bInsert, 227 _In_ BOOL bSelect) 228 { 229 if (!phItem) 230 return FALSE; 231 232 *phItem = NULL; 233 234 HTREEITEM hItem = TreeView_GetFirstVisible(m_hwndTreeView); 235 HTREEITEM hParent = NULL, tmp; 236 while (TRUE) 237 { 238 CItemData *pItemData = GetItemData(hItem); 239 if (!pItemData) 240 { 241 ERR("Something has gone wrong, no data associated to node\n"); 242 return FALSE; 243 } 244 245 // If we found our node, give it back 246 if (!m_pDesktop->CompareIDs(0, pItemData->absolutePidl, dest)) 247 { 248 if (bSelect) 249 TreeView_SelectItem(m_hwndTreeView, hItem); 250 *phItem = hItem; 251 return TRUE; 252 } 253 254 // Check if we are a parent of the requested item 255 TVITEMW tvItem; 256 LPITEMIDLIST relativeChild = ILFindChild(pItemData->absolutePidl, dest); 257 if (relativeChild) 258 { 259 // Notify treeview we have children 260 tvItem.mask = TVIF_CHILDREN; 261 tvItem.hItem = hItem; 262 tvItem.cChildren = 1; 263 TreeView_SetItem(m_hwndTreeView, &tvItem); 264 265 // If we can expand and the node isn't expanded yet, do it 266 if (bExpand) 267 { 268 if (!pItemData->expanded) 269 { 270 _InsertSubitems(hItem, pItemData->absolutePidl); 271 pItemData->expanded = TRUE; 272 } 273 TreeView_Expand(m_hwndTreeView, hItem, TVE_EXPAND); 274 } 275 276 // Try to get a child 277 tmp = TreeView_GetChild(m_hwndTreeView, hItem); 278 if (tmp) 279 { 280 // We have a child, let's continue with it 281 hParent = hItem; 282 hItem = tmp; 283 continue; 284 } 285 286 if (bInsert && pItemData->expanded) 287 { 288 // Happens when we have to create a subchild inside a child 289 hItem = _InsertItem(hItem, dest, relativeChild, TRUE); 290 } 291 292 // We end up here, without any children, so we found nothing 293 // Tell the parent node it has children 294 ZeroMemory(&tvItem, sizeof(tvItem)); 295 return FALSE; 296 } 297 298 // Find sibling 299 tmp = TreeView_GetNextSibling(m_hwndTreeView, hItem); 300 if (tmp) 301 { 302 hItem = tmp; 303 continue; 304 } 305 306 if (bInsert) 307 { 308 *phItem = hItem = _InsertItem(hParent, dest, ILFindLastID(dest), TRUE); 309 return TRUE; 310 } 311 312 return FALSE; 313 } 314 315 UNREACHABLE; 316 } 317