xref: /reactos/dll/win32/browseui/aclistisf.cpp (revision 2b933529)
1 /*
2  *  Shell AutoComplete list
3  *
4  *  Copyright 2015  Thomas Faber
5  *  Copyright 2020  Katayama Hirofumi MZ
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "precomp.h"
23 
24 CACListISF::CACListISF()
25     : m_dwOptions(ACLO_CURRENTDIR | ACLO_MYCOMPUTER)
26     , m_iNextLocation(LT_DIRECTORY)
27     , m_fShowHidden(FALSE)
28 {
29 }
30 
31 CACListISF::~CACListISF()
32 {
33 }
34 
35 HRESULT CACListISF::NextLocation()
36 {
37     TRACE("(%p)\n", this);
38     HRESULT hr;
39     switch (m_iNextLocation)
40     {
41         case LT_DIRECTORY:
42             m_iNextLocation = LT_DESKTOP;
43             if (!ILIsEmpty(m_pidlCurDir) && (m_dwOptions & ACLO_CURRENTDIR))
44             {
45                 CComHeapPtr<ITEMIDLIST> pidl(ILClone(m_pidlCurDir));
46                 hr = SetLocation(pidl.Detach());
47                 if (SUCCEEDED(hr))
48                 {
49                     TRACE("LT_DIRECTORY\n");
50                     return hr;
51                 }
52             }
53             // FALL THROUGH
54         case LT_DESKTOP:
55             m_iNextLocation = LT_MYCOMPUTER;
56             if (m_dwOptions & ACLO_DESKTOP)
57             {
58                 CComHeapPtr<ITEMIDLIST> pidl;
59                 hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
60                 if (FAILED_UNEXPECTEDLY(hr))
61                     return S_FALSE;
62                 hr = SetLocation(pidl.Detach());
63                 if (SUCCEEDED(hr))
64                 {
65                     TRACE("LT_DESKTOP\n");
66                     return hr;
67                 }
68             }
69             // FALL THROUGH
70         case LT_MYCOMPUTER:
71             m_iNextLocation = LT_FAVORITES;
72             if (m_dwOptions & ACLO_MYCOMPUTER)
73             {
74                 CComHeapPtr<ITEMIDLIST> pidl;
75                 hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl);
76                 if (FAILED_UNEXPECTEDLY(hr))
77                     return S_FALSE;
78                 hr = SetLocation(pidl.Detach());
79                 if (SUCCEEDED(hr))
80                 {
81                     TRACE("LT_MYCOMPUTER\n");
82                     return hr;
83                 }
84             }
85             // FALL THROUGH
86         case LT_FAVORITES:
87             m_iNextLocation = LT_MAX;
88             if (m_dwOptions & ACLO_FAVORITES)
89             {
90                 CComHeapPtr<ITEMIDLIST> pidl;
91                 hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
92                 if (FAILED_UNEXPECTEDLY(hr))
93                     return S_FALSE;
94                 hr = SetLocation(pidl.Detach());
95                 if (SUCCEEDED(hr))
96                 {
97                     TRACE("LT_FAVORITES\n");
98                     return hr;
99                 }
100             }
101             // FALL THROUGH
102         case LT_MAX:
103         default:
104             TRACE("LT_MAX\n");
105             return S_FALSE;
106     }
107 }
108 
109 HRESULT CACListISF::SetLocation(LPITEMIDLIST pidl)
110 {
111     TRACE("(%p, %p)\n", this, pidl);
112 
113     m_pEnumIDList.Release();
114     m_pShellFolder.Release();
115     m_pidlLocation.Free();
116 
117     if (!pidl)
118         return E_FAIL;
119 
120     m_pidlLocation.Attach(pidl);
121 
122     CComPtr<IShellFolder> pFolder;
123     HRESULT hr = SHGetDesktopFolder(&pFolder);
124     if (FAILED_UNEXPECTEDLY(hr))
125         return hr;
126 
127     if (!ILIsEmpty(pidl))
128     {
129         hr = pFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &m_pShellFolder));
130         if (FAILED_UNEXPECTEDLY(hr))
131             return hr;
132     }
133     else
134     {
135         m_pShellFolder.Attach(pFolder.Detach());
136     }
137 
138     SHCONTF Flags = SHCONTF_FOLDERS | SHCONTF_INIT_ON_FIRST_NEXT;
139     if (m_fShowHidden)
140         Flags |= SHCONTF_INCLUDEHIDDEN;
141     if (!(m_dwOptions & ACLO_FILESYSDIRS))
142         Flags |= SHCONTF_NONFOLDERS;
143 
144     hr = m_pShellFolder->EnumObjects(NULL, Flags, &m_pEnumIDList);
145     if (hr != S_OK)
146     {
147         ERR("EnumObjects failed: 0x%lX\n", hr);
148         hr = E_FAIL;
149     }
150     return hr;
151 }
152 
153 HRESULT CACListISF::GetDisplayName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszChild)
154 {
155     TRACE("(%p, %p)\n", this, pidlChild);
156     pszChild.Free();
157 
158     STRRET StrRet;
159     DWORD dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR;
160     HRESULT hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet);
161     if (FAILED(hr))
162     {
163         dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING;
164         hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet);
165         if (FAILED_UNEXPECTEDLY(hr))
166             return hr;
167     }
168 
169     hr = StrRetToStrW(&StrRet, NULL, &pszChild);
170     if (FAILED_UNEXPECTEDLY(hr))
171         return hr;
172 
173     TRACE("pszChild: '%S'\n", static_cast<LPCWSTR>(pszChild));
174     return hr;
175 }
176 
177 HRESULT CACListISF::GetPathName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszPath)
178 {
179     TRACE("(%p, %p)\n", this, pidlChild);
180 
181     CComHeapPtr<WCHAR> pszChild;
182     HRESULT hr = GetDisplayName(pidlChild, pszChild);
183     if (FAILED_UNEXPECTEDLY(hr))
184         return hr;
185 
186     CStringW szPath;
187     if (m_szExpand.GetLength() && m_iNextLocation == LT_DIRECTORY)
188     {
189         INT cchExpand = m_szExpand.GetLength();
190         if (StrCmpNIW(pszChild, m_szExpand, cchExpand) != 0 ||
191             pszChild[0] != L'\\' || pszChild[1] != L'\\')
192         {
193             szPath = m_szExpand;
194         }
195     }
196     szPath += pszChild;
197 
198     INT cchMax = szPath.GetLength() + 1;
199     CComHeapPtr<WCHAR> pszFullPath;
200     if (!pszFullPath.Allocate(cchMax))
201     {
202         ERR("Out of memory\n");
203         return E_OUTOFMEMORY;
204     }
205 
206     StringCchCopyW(pszFullPath, cchMax, szPath);
207     pszPath.Attach(pszFullPath.Detach());
208     TRACE("pszPath: '%S'\n", static_cast<LPCWSTR>(pszPath));
209     return S_OK;
210 }
211 
212 // *** IEnumString methods ***
213 STDMETHODIMP CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
214 {
215     TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched);
216 
217     if (celt == 0)
218         return S_OK;
219     if (!rgelt)
220         return S_FALSE;
221 
222     *rgelt = NULL;
223     if (pceltFetched)
224         *pceltFetched = 0;
225 
226     if (!m_pEnumIDList)
227     {
228         NextLocation();
229         if (!m_pEnumIDList)
230             return S_FALSE;
231     }
232 
233     HRESULT hr;
234     CComHeapPtr<ITEMIDLIST> pidlChild;
235     CComHeapPtr<WCHAR> pszPathName;
236 
237     do
238     {
239         for (;;)
240         {
241             pidlChild.Free();
242             hr = m_pEnumIDList->Next(1, &pidlChild, NULL);
243             if (hr != S_OK)
244                 break;
245 
246             pszPathName.Free();
247             GetPathName(pidlChild, pszPathName);
248             if (!pszPathName)
249                 continue;
250 
251             if (m_dwOptions & (ACLO_FILESYSONLY | ACLO_FILESYSDIRS))
252             {
253                 DWORD attrs = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
254                 hr = m_pShellFolder->GetAttributesOf(1, const_cast<LPCITEMIDLIST *>(&pidlChild), &attrs);
255                 if (SUCCEEDED(hr))
256                 {
257                    if (!(attrs & (SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR)))
258                         continue;
259                 }
260             }
261 
262             if ((m_dwOptions & ACLO_FILESYSDIRS) && !PathIsDirectoryW(pszPathName))
263                 continue;
264 
265             hr = S_OK;
266             break;
267         }
268     } while (hr == S_FALSE && NextLocation() == S_OK);
269 
270     if (hr == S_OK)
271     {
272         *rgelt = pszPathName.Detach();
273         if (pceltFetched)
274             *pceltFetched = 1;
275     }
276     else
277     {
278         hr = S_FALSE;
279     }
280 
281     TRACE("*rgelt: %S\n", *rgelt);
282     return hr;
283 }
284 
285 STDMETHODIMP CACListISF::Reset()
286 {
287     TRACE("(%p)\n", this);
288 
289     m_iNextLocation = LT_DIRECTORY;
290     m_szExpand = L"";
291 
292     SHELLSTATE ss = { 0 };
293     SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
294     m_fShowHidden = ss.fShowAllObjects;
295 
296     if (m_dwOptions & ACLO_CURRENTDIR)
297     {
298         CComHeapPtr<ITEMIDLIST> pidl;
299         if (m_pBrowserService)
300         {
301             m_pBrowserService->GetPidl(&pidl);
302             if (pidl)
303                 Initialize(pidl);
304         }
305         HRESULT hr = SetLocation(pidl.Detach());
306         if (FAILED_UNEXPECTEDLY(hr))
307             return S_FALSE;
308     }
309     return S_OK;
310 }
311 
312 STDMETHODIMP CACListISF::Skip(ULONG celt)
313 {
314     TRACE("(%p, %d)\n", this, celt);
315     return E_NOTIMPL;
316 }
317 
318 STDMETHODIMP CACListISF::Clone(IEnumString **ppOut)
319 {
320     TRACE("(%p, %p)\n", this, ppOut);
321     *ppOut = NULL;
322     return E_NOTIMPL;
323 }
324 
325 // *** IACList methods ***
326 STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand)
327 {
328     TRACE("(%p, %ls)\n", this, pszExpand);
329 
330     m_szExpand = pszExpand;
331 
332     m_iNextLocation = LT_DIRECTORY;
333     CComHeapPtr<ITEMIDLIST> pidl;
334     HRESULT hr = SHParseDisplayName(m_szExpand, NULL, &pidl, NULL, NULL);
335     if (SUCCEEDED(hr))
336     {
337         hr = SetLocation(pidl.Detach());
338         if (FAILED_UNEXPECTEDLY(hr))
339             m_szExpand = L"";
340     }
341     return hr;
342 }
343 
344 // *** IACList2 methods ***
345 STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag)
346 {
347     TRACE("(%p, %lu)\n", this, dwFlag);
348     m_dwOptions = dwFlag;
349     return S_OK;
350 }
351 
352 STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag)
353 {
354     TRACE("(%p, %p)\n", this, pdwFlag);
355     if (pdwFlag)
356     {
357         *pdwFlag = m_dwOptions;
358         return S_OK;
359     }
360     return E_INVALIDARG;
361 }
362 
363 // *** IShellService methods ***
364 STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner)
365 {
366     TRACE("(%p, %p)\n", this, punkOwner);
367     m_pBrowserService.Release();
368     punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService));
369     return S_OK;
370 }
371 
372 // *** IPersist methods ***
373 STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID)
374 {
375     TRACE("(%p, %p)\n", this, pClassID);
376     if (pClassID == NULL)
377         return E_POINTER;
378     *pClassID = CLSID_ACListISF;
379     return S_OK;
380 }
381 
382 // *** IPersistFolder methods ***
383 STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl)
384 {
385     TRACE("(%p, %p)\n", this, pidl);
386     m_pidlCurDir.Free();
387     if (!pidl)
388         return S_OK;
389 
390     LPITEMIDLIST pidlClone = ILClone(pidl);
391     if (!pidlClone)
392     {
393         ERR("Out of memory\n");
394         return E_OUTOFMEMORY;
395     }
396     m_pidlCurDir.Attach(pidlClone);
397     return S_OK;
398 }
399 
400 // *** ICurrentWorkingDirectory methods ***
401 STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize)
402 {
403     TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize);
404     return E_NOTIMPL;
405 }
406 
407 STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath)
408 {
409     TRACE("(%p, %ls, %ld)\n", this, pwzPath);
410     LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath);
411     if (!pidl)
412     {
413         ERR("Out of memory\n");
414         return E_OUTOFMEMORY;
415     }
416     m_pidlCurDir.Attach(pidl);
417     return S_OK;
418 }
419