xref: /reactos/dll/win32/browseui/aclistisf.cpp (revision 9393fc32)
1 /*
2  *  Shell AutoComplete list
3  *
4  *  Copyright 2015  Thomas Faber
5  *  Copyright 2020-2021 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
178 CACListISF::GetPaths(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszRaw,
179                      CComHeapPtr<WCHAR>& pszExpanded)
180 {
181     TRACE("(%p, %p)\n", this, pidlChild);
182 
183     CComHeapPtr<WCHAR> pszChild;
184     HRESULT hr = GetDisplayName(pidlChild, pszChild);
185     if (FAILED_UNEXPECTEDLY(hr))
186         return hr;
187 
188     CStringW szRawPath, szExpanded;
189     if (m_szRawPath.GetLength() && m_iNextLocation == LT_DIRECTORY)
190     {
191         INT cchExpand = m_szRawPath.GetLength();
192         if (StrCmpNIW(pszChild, m_szRawPath, cchExpand) != 0 ||
193             pszChild[0] != L'\\' || pszChild[1] != L'\\')
194         {
195             szRawPath = m_szRawPath;
196             szExpanded = m_szExpanded;
197         }
198     }
199     szRawPath += pszChild;
200     szExpanded += pszChild;
201 
202     SHStrDupW(szRawPath, &pszRaw);
203     SHStrDupW(szExpanded, &pszExpanded);
204     TRACE("pszRaw: '%S'\n", static_cast<LPCWSTR>(pszRaw));
205     TRACE("pszExpanded: '%S'\n", static_cast<LPCWSTR>(pszExpanded));
206     return S_OK;
207 }
208 
209 // *** IEnumString methods ***
210 STDMETHODIMP CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
211 {
212     TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched);
213 
214     if (celt == 0)
215         return S_OK;
216     if (!rgelt)
217         return S_FALSE;
218 
219     *rgelt = NULL;
220     if (pceltFetched)
221         *pceltFetched = 0;
222 
223     if (!m_pEnumIDList)
224     {
225         NextLocation();
226         if (!m_pEnumIDList)
227             return S_FALSE;
228     }
229 
230     HRESULT hr;
231     CComHeapPtr<ITEMIDLIST> pidlChild;
232     CComHeapPtr<WCHAR> pszRawPath, pszExpanded;
233 
234     do
235     {
236         for (;;)
237         {
238             pidlChild.Free();
239             hr = m_pEnumIDList->Next(1, &pidlChild, NULL);
240             if (hr != S_OK)
241                 break;
242 
243             pszRawPath.Free();
244             pszExpanded.Free();
245             GetPaths(pidlChild, pszRawPath, pszExpanded);
246             if (!pszRawPath || !pszExpanded)
247                 continue;
248 
249             if ((m_dwOptions & ACLO_FILESYSDIRS) && !PathIsDirectoryW(pszExpanded))
250                 continue;
251             else if ((m_dwOptions & ACLO_FILESYSONLY) && !PathFileExistsW(pszExpanded))
252                 continue;
253 
254             hr = S_OK;
255             break;
256         }
257     } while (hr == S_FALSE && NextLocation() == S_OK);
258 
259     if (hr == S_OK)
260     {
261         *rgelt = pszRawPath.Detach();
262         if (pceltFetched)
263             *pceltFetched = 1;
264     }
265     else
266     {
267         hr = S_FALSE;
268     }
269 
270     TRACE("*rgelt: %S\n", *rgelt);
271     return hr;
272 }
273 
274 STDMETHODIMP CACListISF::Reset()
275 {
276     TRACE("(%p)\n", this);
277 
278     m_iNextLocation = LT_DIRECTORY;
279     m_szRawPath = L"";
280 
281     SHELLSTATE ss = { 0 };
282     SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
283     m_fShowHidden = ss.fShowAllObjects;
284 
285     if (m_dwOptions & ACLO_CURRENTDIR)
286     {
287         CComHeapPtr<ITEMIDLIST> pidl;
288         if (m_pBrowserService)
289         {
290             m_pBrowserService->GetPidl(&pidl);
291             if (pidl)
292                 Initialize(pidl);
293         }
294         HRESULT hr = SetLocation(pidl.Detach());
295         if (FAILED_UNEXPECTEDLY(hr))
296             return S_FALSE;
297     }
298     return S_OK;
299 }
300 
301 STDMETHODIMP CACListISF::Skip(ULONG celt)
302 {
303     TRACE("(%p, %d)\n", this, celt);
304     return E_NOTIMPL;
305 }
306 
307 STDMETHODIMP CACListISF::Clone(IEnumString **ppOut)
308 {
309     TRACE("(%p, %p)\n", this, ppOut);
310     *ppOut = NULL;
311     return E_NOTIMPL;
312 }
313 
314 // *** IACList methods ***
315 STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand)
316 {
317     TRACE("(%p, %ls)\n", this, pszExpand);
318 
319     m_szRawPath = pszExpand;
320     m_iNextLocation = LT_DIRECTORY;
321 
322     // skip left space
323     while (*pszExpand == L' ')
324         ++pszExpand;
325 
326     // expand environment variables (%WINDIR% etc.)
327     WCHAR szExpanded[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH];
328     ExpandEnvironmentStringsW(pszExpand, szExpanded, _countof(szExpanded));
329     pszExpand = szExpanded;
330 
331     // get full path
332     if (szExpanded[0] && szExpanded[1] == L':' && szExpanded[2] == 0)
333     {
334         // 'C:' --> 'C:\'
335         szExpanded[2] = L'\\';
336         szExpanded[3] = 0;
337     }
338     else
339     {
340         if (PathIsRelativeW(pszExpand) &&
341             SHGetPathFromIDListW(m_pidlCurDir, szPath1) &&
342             PathCombineW(szPath2, szPath1, pszExpand))
343         {
344             pszExpand = szPath2;
345         }
346         GetFullPathNameW(pszExpand, _countof(szPath1), szPath1, NULL);
347         pszExpand = szPath1;
348     }
349 
350     CComHeapPtr<ITEMIDLIST> pidl;
351     m_szExpanded = pszExpand;
352     HRESULT hr = SHParseDisplayName(m_szExpanded, NULL, &pidl, NULL, NULL);
353     if (SUCCEEDED(hr))
354     {
355         hr = SetLocation(pidl.Detach());
356         if (FAILED_UNEXPECTEDLY(hr))
357         {
358             m_szRawPath = L"";
359             m_szExpanded = L"";
360         }
361     }
362     return hr;
363 }
364 
365 // *** IACList2 methods ***
366 STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag)
367 {
368     TRACE("(%p, %lu)\n", this, dwFlag);
369     m_dwOptions = dwFlag;
370     return S_OK;
371 }
372 
373 STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag)
374 {
375     TRACE("(%p, %p)\n", this, pdwFlag);
376     if (pdwFlag)
377     {
378         *pdwFlag = m_dwOptions;
379         return S_OK;
380     }
381     return E_INVALIDARG;
382 }
383 
384 // *** IShellService methods ***
385 STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner)
386 {
387     TRACE("(%p, %p)\n", this, punkOwner);
388     m_pBrowserService.Release();
389     punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService));
390     return S_OK;
391 }
392 
393 // *** IPersist methods ***
394 STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID)
395 {
396     TRACE("(%p, %p)\n", this, pClassID);
397     if (pClassID == NULL)
398         return E_POINTER;
399     *pClassID = CLSID_ACListISF;
400     return S_OK;
401 }
402 
403 // *** IPersistFolder methods ***
404 STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl)
405 {
406     TRACE("(%p, %p)\n", this, pidl);
407     m_pidlCurDir.Free();
408     if (!pidl)
409         return S_OK;
410 
411     LPITEMIDLIST pidlClone = ILClone(pidl);
412     if (!pidlClone)
413     {
414         ERR("Out of memory\n");
415         return E_OUTOFMEMORY;
416     }
417     m_pidlCurDir.Attach(pidlClone);
418     return S_OK;
419 }
420 
421 // *** ICurrentWorkingDirectory methods ***
422 STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize)
423 {
424     TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize);
425     return E_NOTIMPL;
426 }
427 
428 STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath)
429 {
430     TRACE("(%p, %ls, %ld)\n", this, pwzPath);
431     LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath);
432     if (!pidl)
433     {
434         ERR("Out of memory\n");
435         return E_OUTOFMEMORY;
436     }
437     m_pidlCurDir.Attach(pidl);
438     return S_OK;
439 }
440