xref: /reactos/dll/win32/shdocvw/CExplorerBand.cpp (revision d6eebaa4)
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