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