xref: /reactos/dll/win32/browseui/aclistisf.cpp (revision ea936478)
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 
CACListISF()24 CACListISF::CACListISF()
25     : m_dwOptions(ACLO_CURRENTDIR | ACLO_MYCOMPUTER)
26     , m_iNextLocation(LT_DIRECTORY)
27     , m_fShowHidden(FALSE)
28 {
29 }
30 
~CACListISF()31 CACListISF::~CACListISF()
32 {
33 }
34 
NextLocation()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 
SetLocation(LPITEMIDLIST pidl)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 
GetDisplayName(LPCITEMIDLIST pidlChild,CComHeapPtr<WCHAR> & pszChild)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
GetPaths(LPCITEMIDLIST pidlChild,CComHeapPtr<WCHAR> & pszRaw,CComHeapPtr<WCHAR> & pszExpanded)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 ***
Next(ULONG celt,LPOLESTR * rgelt,ULONG * pceltFetched)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             DWORD attrs = SFGAO_FOLDER | SFGAO_FILESYSTEM;
250             LPCITEMIDLIST pidlRef = pidlChild;
251             hr = m_pShellFolder->GetAttributesOf(1, &pidlRef, &attrs);
252             if (FAILED_UNEXPECTEDLY(hr))
253                 continue;
254 
255             if ((m_dwOptions & ACLO_FILESYSDIRS) && !(attrs & SFGAO_FOLDER))
256                 continue;
257             if ((m_dwOptions & (ACLO_FILESYSONLY | ACLO_FILESYSDIRS)) && !(attrs & SFGAO_FILESYSTEM))
258                 continue;
259 
260             hr = S_OK;
261             break;
262         }
263     } while (hr == S_FALSE && NextLocation() == S_OK);
264 
265     if (hr == S_OK)
266     {
267         *rgelt = pszRawPath.Detach();
268         if (pceltFetched)
269             *pceltFetched = 1;
270     }
271     else
272     {
273         hr = S_FALSE;
274     }
275 
276     TRACE("*rgelt: %S\n", *rgelt);
277     return hr;
278 }
279 
Reset()280 STDMETHODIMP CACListISF::Reset()
281 {
282     TRACE("(%p)\n", this);
283 
284     m_iNextLocation = LT_DIRECTORY;
285     m_szRawPath = L"";
286 
287     SHELLSTATE ss = { 0 };
288     SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
289     m_fShowHidden = ss.fShowAllObjects;
290 
291     if (m_dwOptions & ACLO_CURRENTDIR)
292     {
293         CComHeapPtr<ITEMIDLIST> pidl;
294         if (m_pBrowserService)
295         {
296             m_pBrowserService->GetPidl(&pidl);
297             if (pidl)
298                 Initialize(pidl);
299         }
300         HRESULT hr = SetLocation(pidl.Detach());
301         if (FAILED_UNEXPECTEDLY(hr))
302             return S_FALSE;
303     }
304     return S_OK;
305 }
306 
Skip(ULONG celt)307 STDMETHODIMP CACListISF::Skip(ULONG celt)
308 {
309     TRACE("(%p, %d)\n", this, celt);
310     return E_NOTIMPL;
311 }
312 
Clone(IEnumString ** ppOut)313 STDMETHODIMP CACListISF::Clone(IEnumString **ppOut)
314 {
315     TRACE("(%p, %p)\n", this, ppOut);
316     *ppOut = NULL;
317     return E_NOTIMPL;
318 }
319 
320 // *** IACList methods ***
Expand(LPCOLESTR pszExpand)321 STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand)
322 {
323     TRACE("(%p, %ls)\n", this, pszExpand);
324 
325     m_szRawPath = pszExpand;
326     m_iNextLocation = LT_DIRECTORY;
327 
328     // skip left space
329     while (*pszExpand == L' ')
330         ++pszExpand;
331 
332     // expand environment variables (%WINDIR% etc.)
333     WCHAR szExpanded[MAX_PATH], szPath1[MAX_PATH], szPath2[MAX_PATH];
334     ExpandEnvironmentStringsW(pszExpand, szExpanded, _countof(szExpanded));
335     pszExpand = szExpanded;
336 
337     // get full path
338     if (szExpanded[0] && szExpanded[1] == L':' && szExpanded[2] == 0)
339     {
340         // 'C:' --> 'C:\'
341         szExpanded[2] = L'\\';
342         szExpanded[3] = 0;
343     }
344     else
345     {
346         if (PathIsRelativeW(pszExpand) &&
347             SHGetPathFromIDListW(m_pidlCurDir, szPath1) &&
348             PathCombineW(szPath2, szPath1, pszExpand) &&
349             PathFileExistsW(szPath2))
350         {
351             pszExpand = szPath2;
352         }
353         else if (PathFileExistsW(pszExpand))
354         {
355             GetFullPathNameW(pszExpand, _countof(szPath1), szPath1, NULL);
356             pszExpand = szPath1;
357         }
358     }
359 
360     CComHeapPtr<ITEMIDLIST> pidl;
361     m_szExpanded = pszExpand;
362     HRESULT hr = SHParseDisplayName(m_szExpanded, NULL, &pidl, NULL, NULL);
363     if (SUCCEEDED(hr))
364     {
365         hr = SetLocation(pidl.Detach());
366         if (FAILED_UNEXPECTEDLY(hr))
367         {
368             m_szRawPath = L"";
369             m_szExpanded = L"";
370         }
371     }
372     return hr;
373 }
374 
375 // *** IACList2 methods ***
SetOptions(DWORD dwFlag)376 STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag)
377 {
378     TRACE("(%p, %lu)\n", this, dwFlag);
379     m_dwOptions = dwFlag;
380     return S_OK;
381 }
382 
GetOptions(DWORD * pdwFlag)383 STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag)
384 {
385     TRACE("(%p, %p)\n", this, pdwFlag);
386     if (pdwFlag)
387     {
388         *pdwFlag = m_dwOptions;
389         return S_OK;
390     }
391     return E_INVALIDARG;
392 }
393 
394 // *** IShellService methods ***
SetOwner(IUnknown * punkOwner)395 STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner)
396 {
397     TRACE("(%p, %p)\n", this, punkOwner);
398     m_pBrowserService.Release();
399     punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService));
400     return S_OK;
401 }
402 
403 // *** IPersist methods ***
GetClassID(CLSID * pClassID)404 STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID)
405 {
406     TRACE("(%p, %p)\n", this, pClassID);
407     if (pClassID == NULL)
408         return E_POINTER;
409     *pClassID = CLSID_ACListISF;
410     return S_OK;
411 }
412 
413 // *** IPersistFolder methods ***
Initialize(PCIDLIST_ABSOLUTE pidl)414 STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl)
415 {
416     TRACE("(%p, %p)\n", this, pidl);
417     m_pidlCurDir.Free();
418     if (!pidl)
419         return S_OK;
420 
421     LPITEMIDLIST pidlClone = ILClone(pidl);
422     if (!pidlClone)
423     {
424         ERR("Out of memory\n");
425         return E_OUTOFMEMORY;
426     }
427     m_pidlCurDir.Attach(pidlClone);
428     return S_OK;
429 }
430 
431 // *** ICurrentWorkingDirectory methods ***
GetDirectory(LPWSTR pwzPath,DWORD cchSize)432 STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize)
433 {
434     TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize);
435     return E_NOTIMPL;
436 }
437 
SetDirectory(LPCWSTR pwzPath)438 STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath)
439 {
440     TRACE("(%p, %ls, %ld)\n", this, pwzPath);
441     LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath);
442     if (!pidl)
443     {
444         ERR("Out of memory\n");
445         return E_OUTOFMEMORY;
446     }
447     m_pidlCurDir.Attach(pidl);
448     return S_OK;
449 }
450