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
CExplorerBand()21 CExplorerBand::CExplorerBand()
22 {
23 }
24
~CExplorerBand()25 CExplorerBand::~CExplorerBand()
26 {
27 }
28
GetClassID(CLSID * pClassID)29 STDMETHODIMP CExplorerBand::GetClassID(CLSID *pClassID)
30 {
31 if (!pClassID)
32 return E_POINTER;
33 *pClassID = CLSID_ExplorerBand;
34 return S_OK;
35 }
36
_GetRootCsidl()37 INT CExplorerBand::_GetRootCsidl()
38 {
39 return CSIDL_DESKTOP;
40 }
41
_GetTVStyle()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
_GetTVExStyle()49 DWORD CExplorerBand::_GetTVExStyle()
50 {
51 return 0;
52 }
53
_GetEnumFlags()54 DWORD CExplorerBand::_GetEnumFlags()
55 {
56 return SHCONTF_FOLDERS;
57 }
58
_GetTitle(LPWSTR pszTitle,INT cchTitle)59 BOOL CExplorerBand::_GetTitle(LPWSTR pszTitle, INT cchTitle)
60 {
61 return ::LoadStringW(instance, IDS_FOLDERSLABEL, pszTitle, cchTitle);
62 }
63
_WantsRootItem()64 BOOL CExplorerBand::_WantsRootItem()
65 {
66 return TRUE;
67 }
68
69 // Called when the user has selected an item.
OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl)70 STDMETHODIMP CExplorerBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl)
71 {
72 return Invoke(pidl);
73 }
74
75 // Handles a user action on an item.
Invoke(_In_ PCIDLIST_ABSOLUTE pidl)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
_SortItems(HTREEITEM hParent)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
_CompareTreeItems(LPARAM p1,LPARAM p2,LPARAM p3)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
_CreateTreeView(HWND hwndParent)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
_DestroyTreeView()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
GetTypeInfoCount(UINT * pctinfo)154 STDMETHODIMP CExplorerBand::GetTypeInfoCount(UINT *pctinfo)
155 {
156 UNIMPLEMENTED;
157 return E_NOTIMPL;
158 }
159
GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)160 STDMETHODIMP CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
161 {
162 UNIMPLEMENTED;
163 return E_NOTIMPL;
164 }
165
GetIDsOfNames(REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)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
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)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
_NavigateToCurrentFolder()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
_NavigateToPIDL(_In_ LPCITEMIDLIST dest,_Out_ HTREEITEM * phItem,_In_ BOOL bExpand,_In_ BOOL bInsert,_In_ BOOL bSelect)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