[BROWSEUI] Implement CLSID_ACListISF (#3298)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Sat, 17 Oct 2020 08:49:24 +0000 (17:49 +0900)
committerGitHub <noreply@github.com>
Sat, 17 Oct 2020 08:49:24 +0000 (17:49 +0900)
Implement enumeration of IShellFolder items of auto-completion. CORE-9281

dll/win32/browseui/aclistisf.cpp
dll/win32/browseui/aclistisf.h

index 72045b3..533a7af 100644 (file)
@@ -2,6 +2,7 @@
  *  Shell AutoComplete list
  *
  *  Copyright 2015  Thomas Faber
+ *  Copyright 2020  Katayama Hirofumi MZ
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 #include "precomp.h"
 
-CACListISF::CACListISF() :
-    m_dwOptions(0)
+CACListISF::CACListISF()
+    : m_dwOptions(ACLO_CURRENTDIR | ACLO_MYCOMPUTER)
+    , m_iNextLocation(LT_DIRECTORY)
+    , m_fShowHidden(FALSE)
 {
 }
 
@@ -29,26 +32,290 @@ CACListISF::~CACListISF()
 {
 }
 
+HRESULT CACListISF::NextLocation()
+{
+    TRACE("(%p)\n", this);
+    HRESULT hr;
+    switch (m_iNextLocation)
+    {
+        case LT_DIRECTORY:
+            m_iNextLocation = LT_DESKTOP;
+            if (!ILIsEmpty(m_pidlCurDir) && (m_dwOptions & ACLO_CURRENTDIR))
+            {
+                CComHeapPtr<ITEMIDLIST> pidl(ILClone(m_pidlCurDir));
+                hr = SetLocation(pidl.Detach());
+                if (SUCCEEDED(hr))
+                {
+                    TRACE("LT_DIRECTORY\n");
+                    return hr;
+                }
+            }
+            // FALL THROUGH
+        case LT_DESKTOP:
+            m_iNextLocation = LT_MYCOMPUTER;
+            if (m_dwOptions & ACLO_DESKTOP)
+            {
+                CComHeapPtr<ITEMIDLIST> pidl;
+                hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl);
+                if (FAILED_UNEXPECTEDLY(hr))
+                    return S_FALSE;
+                hr = SetLocation(pidl.Detach());
+                if (SUCCEEDED(hr))
+                {
+                    TRACE("LT_DESKTOP\n");
+                    return hr;
+                }
+            }
+            // FALL THROUGH
+        case LT_MYCOMPUTER:
+            m_iNextLocation = LT_FAVORITES;
+            if (m_dwOptions & ACLO_MYCOMPUTER)
+            {
+                CComHeapPtr<ITEMIDLIST> pidl;
+                hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl);
+                if (FAILED_UNEXPECTEDLY(hr))
+                    return S_FALSE;
+                hr = SetLocation(pidl.Detach());
+                if (SUCCEEDED(hr))
+                {
+                    TRACE("LT_MYCOMPUTER\n");
+                    return hr;
+                }
+            }
+            // FALL THROUGH
+        case LT_FAVORITES:
+            m_iNextLocation = LT_MAX;
+            if (m_dwOptions & ACLO_FAVORITES)
+            {
+                CComHeapPtr<ITEMIDLIST> pidl;
+                hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
+                if (FAILED_UNEXPECTEDLY(hr))
+                    return S_FALSE;
+                hr = SetLocation(pidl.Detach());
+                if (SUCCEEDED(hr))
+                {
+                    TRACE("LT_FAVORITES\n");
+                    return hr;
+                }
+            }
+            // FALL THROUGH
+        case LT_MAX:
+        default:
+            TRACE("LT_MAX\n");
+            return S_FALSE;
+    }
+}
+
+HRESULT CACListISF::SetLocation(LPITEMIDLIST pidl)
+{
+    TRACE("(%p, %p)\n", this, pidl);
+
+    m_pEnumIDList.Release();
+    m_pShellFolder.Release();
+    m_pidlLocation.Free();
+
+    if (!pidl)
+        return E_FAIL;
+
+    m_pidlLocation.Attach(pidl);
+
+    CComPtr<IShellFolder> pFolder;
+    HRESULT hr = SHGetDesktopFolder(&pFolder);
+    if (FAILED_UNEXPECTEDLY(hr))
+        return hr;
+
+    if (!ILIsEmpty(pidl))
+    {
+        hr = pFolder->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &m_pShellFolder));
+        if (FAILED_UNEXPECTEDLY(hr))
+            return hr;
+    }
+    else
+    {
+        m_pShellFolder.Attach(pFolder.Detach());
+    }
+
+    SHCONTF Flags = SHCONTF_FOLDERS | SHCONTF_INIT_ON_FIRST_NEXT;
+    if (m_fShowHidden)
+        Flags |= SHCONTF_INCLUDEHIDDEN;
+    if (!(m_dwOptions & ACLO_FILESYSDIRS))
+        Flags |= SHCONTF_NONFOLDERS;
+
+    hr = m_pShellFolder->EnumObjects(NULL, Flags, &m_pEnumIDList);
+    if (hr != S_OK)
+    {
+        ERR("EnumObjects failed: 0x%lX\n", hr);
+        hr = E_FAIL;
+    }
+    return hr;
+}
+
+HRESULT CACListISF::GetDisplayName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszChild)
+{
+    TRACE("(%p, %p)\n", this, pidlChild);
+    pszChild.Free();
+
+    STRRET StrRet;
+    DWORD dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR;
+    HRESULT hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet);
+    if (FAILED(hr))
+    {
+        dwFlags = SHGDN_INFOLDER | SHGDN_FORPARSING;
+        hr = m_pShellFolder->GetDisplayNameOf(pidlChild, dwFlags, &StrRet);
+        if (FAILED_UNEXPECTEDLY(hr))
+            return hr;
+    }
+
+    hr = StrRetToStrW(&StrRet, NULL, &pszChild);
+    if (FAILED_UNEXPECTEDLY(hr))
+        return hr;
+
+    TRACE("pszChild: '%S'\n", static_cast<LPCWSTR>(pszChild));
+    return hr;
+}
+
+HRESULT CACListISF::GetPathName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszPath)
+{
+    TRACE("(%p, %p)\n", this, pidlChild);
+
+    CComHeapPtr<WCHAR> pszChild;
+    HRESULT hr = GetDisplayName(pidlChild, pszChild);
+    if (FAILED_UNEXPECTEDLY(hr))
+        return hr;
+
+    CStringW szPath;
+    if (m_szExpand.GetLength() && m_iNextLocation == LT_DIRECTORY)
+    {
+        INT cchExpand = m_szExpand.GetLength();
+        if (StrCmpNIW(pszChild, m_szExpand, cchExpand) != 0 ||
+            pszChild[0] != L'\\' || pszChild[1] != L'\\')
+        {
+            szPath = m_szExpand;
+        }
+    }
+    szPath += pszChild;
+
+    INT cchMax = szPath.GetLength() + 1;
+    CComHeapPtr<WCHAR> pszFullPath;
+    if (!pszFullPath.Allocate(cchMax))
+    {
+        ERR("Out of memory\n");
+        return E_OUTOFMEMORY;
+    }
+
+    StringCchCopyW(pszFullPath, cchMax, szPath);
+    pszPath.Attach(pszFullPath.Detach());
+    TRACE("pszPath: '%S'\n", static_cast<LPCWSTR>(pszPath));
+    return S_OK;
+}
+
 // *** IEnumString methods ***
-HRESULT STDMETHODCALLTYPE CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
+STDMETHODIMP CACListISF::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
 {
     TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched);
-    return E_NOTIMPL;
+
+    if (celt == 0)
+        return S_OK;
+    if (!rgelt)
+        return S_FALSE;
+
+    *rgelt = NULL;
+    if (pceltFetched)
+        *pceltFetched = 0;
+
+    if (!m_pEnumIDList)
+    {
+        NextLocation();
+        if (!m_pEnumIDList)
+            return S_FALSE;
+    }
+
+    HRESULT hr;
+    CComHeapPtr<ITEMIDLIST> pidlChild;
+    CComHeapPtr<WCHAR> pszPathName;
+
+    do
+    {
+        for (;;)
+        {
+            pidlChild.Free();
+            hr = m_pEnumIDList->Next(1, &pidlChild, NULL);
+            if (hr != S_OK)
+                break;
+
+            pszPathName.Free();
+            GetPathName(pidlChild, pszPathName);
+            if (!pszPathName)
+                continue;
+
+            if (m_dwOptions & (ACLO_FILESYSONLY | ACLO_FILESYSDIRS))
+            {
+                DWORD attrs = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
+                hr = m_pShellFolder->GetAttributesOf(1, const_cast<LPCITEMIDLIST *>(&pidlChild), &attrs);
+                if (SUCCEEDED(hr))
+                {
+                   if (!(attrs & (SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR)))
+                        continue;
+                }
+            }
+
+            if ((m_dwOptions & ACLO_FILESYSDIRS) && !PathIsDirectoryW(pszPathName))
+                continue;
+
+            hr = S_OK;
+            break;
+        }
+    } while (hr == S_FALSE && NextLocation() == S_OK);
+
+    if (hr == S_OK)
+    {
+        *rgelt = pszPathName.Detach();
+        if (pceltFetched)
+            *pceltFetched = 1;
+    }
+    else
+    {
+        hr = S_FALSE;
+    }
+
+    TRACE("*rgelt: %S\n", *rgelt);
+    return hr;
 }
 
-HRESULT STDMETHODCALLTYPE CACListISF::Reset()
+STDMETHODIMP CACListISF::Reset()
 {
     TRACE("(%p)\n", this);
-    return E_NOTIMPL;
+
+    m_iNextLocation = LT_DIRECTORY;
+    m_szExpand = L"";
+
+    SHELLSTATE ss = { 0 };
+    SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
+    m_fShowHidden = ss.fShowAllObjects;
+
+    if (m_dwOptions & ACLO_CURRENTDIR)
+    {
+        CComHeapPtr<ITEMIDLIST> pidl;
+        if (m_pBrowserService)
+        {
+            m_pBrowserService->GetPidl(&pidl);
+            if (pidl)
+                Initialize(pidl);
+        }
+        HRESULT hr = SetLocation(pidl.Detach());
+        if (FAILED_UNEXPECTEDLY(hr))
+            return S_FALSE;
+    }
+    return S_OK;
 }
 
-HRESULT STDMETHODCALLTYPE CACListISF::Skip(ULONG celt)
+STDMETHODIMP CACListISF::Skip(ULONG celt)
 {
     TRACE("(%p, %d)\n", this, celt);
     return E_NOTIMPL;
 }
 
-HRESULT STDMETHODCALLTYPE CACListISF::Clone(IEnumString **ppOut)
+STDMETHODIMP CACListISF::Clone(IEnumString **ppOut)
 {
     TRACE("(%p, %p)\n", this, ppOut);
     *ppOut = NULL;
@@ -56,36 +323,54 @@ HRESULT STDMETHODCALLTYPE CACListISF::Clone(IEnumString **ppOut)
 }
 
 // *** IACList methods ***
-HRESULT STDMETHODCALLTYPE CACListISF::Expand(LPCOLESTR pszExpand)
+STDMETHODIMP CACListISF::Expand(LPCOLESTR pszExpand)
 {
     TRACE("(%p, %ls)\n", this, pszExpand);
-    return E_NOTIMPL;
+
+    m_szExpand = pszExpand;
+
+    m_iNextLocation = LT_DIRECTORY;
+    CComHeapPtr<ITEMIDLIST> pidl;
+    HRESULT hr = SHParseDisplayName(m_szExpand, NULL, &pidl, NULL, NULL);
+    if (SUCCEEDED(hr))
+    {
+        hr = SetLocation(pidl.Detach());
+        if (FAILED_UNEXPECTEDLY(hr))
+            m_szExpand = L"";
+    }
+    return hr;
 }
 
 // *** IACList2 methods ***
-HRESULT STDMETHODCALLTYPE CACListISF::SetOptions(DWORD dwFlag)
+STDMETHODIMP CACListISF::SetOptions(DWORD dwFlag)
 {
     TRACE("(%p, %lu)\n", this, dwFlag);
     m_dwOptions = dwFlag;
     return S_OK;
 }
 
-HRESULT STDMETHODCALLTYPE CACListISF::GetOptions(DWORD* pdwFlag)
+STDMETHODIMP CACListISF::GetOptions(DWORD* pdwFlag)
 {
     TRACE("(%p, %p)\n", this, pdwFlag);
-    *pdwFlag = m_dwOptions;
-    return S_OK;
+    if (pdwFlag)
+    {
+        *pdwFlag = m_dwOptions;
+        return S_OK;
+    }
+    return E_INVALIDARG;
 }
 
 // *** IShellService methods ***
-HRESULT STDMETHODCALLTYPE CACListISF::SetOwner(IUnknown *punkOwner)
+STDMETHODIMP CACListISF::SetOwner(IUnknown *punkOwner)
 {
     TRACE("(%p, %p)\n", this, punkOwner);
-    return E_NOTIMPL;
+    m_pBrowserService.Release();
+    punkOwner->QueryInterface(IID_PPV_ARG(IBrowserService, &m_pBrowserService));
+    return S_OK;
 }
 
 // *** IPersist methods ***
-HRESULT STDMETHODCALLTYPE CACListISF::GetClassID(CLSID *pClassID)
+STDMETHODIMP CACListISF::GetClassID(CLSID *pClassID)
 {
     TRACE("(%p, %p)\n", this, pClassID);
     if (pClassID == NULL)
@@ -95,8 +380,39 @@ HRESULT STDMETHODCALLTYPE CACListISF::GetClassID(CLSID *pClassID)
 }
 
 // *** IPersistFolder methods ***
-HRESULT STDMETHODCALLTYPE CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl)
+STDMETHODIMP CACListISF::Initialize(PCIDLIST_ABSOLUTE pidl)
 {
     TRACE("(%p, %p)\n", this, pidl);
+    m_pidlCurDir.Free();
+    if (!pidl)
+        return S_OK;
+
+    LPITEMIDLIST pidlClone = ILClone(pidl);
+    if (!pidlClone)
+    {
+        ERR("Out of memory\n");
+        return E_OUTOFMEMORY;
+    }
+    m_pidlCurDir.Attach(pidlClone);
+    return S_OK;
+}
+
+// *** ICurrentWorkingDirectory methods ***
+STDMETHODIMP CACListISF::GetDirectory(LPWSTR pwzPath, DWORD cchSize)
+{
+    TRACE("(%p, %p, %ld)\n", this, pwzPath, cchSize);
+    return E_NOTIMPL;
+}
+
+STDMETHODIMP CACListISF::SetDirectory(LPCWSTR pwzPath)
+{
+    TRACE("(%p, %ls, %ld)\n", this, pwzPath);
+    LPITEMIDLIST pidl = ILCreateFromPathW(pwzPath);
+    if (!pidl)
+    {
+        ERR("Out of memory\n");
+        return E_OUTOFMEMORY;
+    }
+    m_pidlCurDir.Attach(pidl);
     return S_OK;
 }
index ddb5309..abc63ef 100644 (file)
@@ -2,6 +2,7 @@
  *  Shell AutoComplete list
  *
  *  Copyright 2015  Thomas Faber
+ *  Copyright 2020  Katayama Hirofumi MZ
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -25,40 +26,68 @@ class CACListISF :
     public CComObjectRootEx<CComMultiThreadModelNoCS>,
     public IEnumString,
     public IACList2,
+    public ICurrentWorkingDirectory,
     public IShellService,
     public IPersistFolder
 {
 private:
+    enum LOCATION_TYPE
+    {
+        LT_DIRECTORY,
+        LT_DESKTOP,
+        LT_MYCOMPUTER,
+        LT_FAVORITES,
+        LT_MAX
+    };
+
     DWORD m_dwOptions;
+    LOCATION_TYPE m_iNextLocation;
+    BOOL m_fShowHidden;
+    CStringW m_szExpand;
+    CComHeapPtr<ITEMIDLIST> m_pidlLocation;
+    CComHeapPtr<ITEMIDLIST> m_pidlCurDir;
+    CComPtr<IEnumIDList> m_pEnumIDList;
+    CComPtr<IShellFolder> m_pShellFolder;
+    CComPtr<IBrowserService> m_pBrowserService;
 
 public:
     CACListISF();
     ~CACListISF();
 
+    HRESULT NextLocation();
+    HRESULT SetLocation(LPITEMIDLIST pidl);
+    HRESULT GetDisplayName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszChild);
+    HRESULT GetPathName(LPCITEMIDLIST pidlChild, CComHeapPtr<WCHAR>& pszPath);
+
     // *** IEnumString methods ***
-    virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched);
-    virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt);
-    virtual HRESULT STDMETHODCALLTYPE Reset();
-    virtual HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum);
+    STDMETHODIMP Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) override;
+    STDMETHODIMP Skip(ULONG celt) override;
+    STDMETHODIMP Reset() override;
+    STDMETHODIMP Clone(IEnumString **ppenum) override;
 
     // *** IACList methods ***
-    virtual HRESULT STDMETHODCALLTYPE Expand(LPCOLESTR pszExpand);
+    STDMETHODIMP Expand(LPCOLESTR pszExpand) override;
 
     // *** IACList2 methods ***
-    virtual HRESULT STDMETHODCALLTYPE SetOptions(DWORD dwFlag);
-    virtual HRESULT STDMETHODCALLTYPE GetOptions(DWORD* pdwFlag);
+    STDMETHODIMP SetOptions(DWORD dwFlag) override;
+    STDMETHODIMP GetOptions(DWORD* pdwFlag) override;
+
+    // FIXME: These virtual keywords below should be removed.
 
     // *** IShellService methods ***
-    virtual HRESULT STDMETHODCALLTYPE SetOwner(IUnknown *);
+    virtual STDMETHODIMP SetOwner(IUnknown *punkOwner) override;
 
     // *** IPersist methods ***
-    virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID);
+    virtual STDMETHODIMP GetClassID(CLSID *pClassID) override;
 
     // *** IPersistFolder methods ***
-    virtual HRESULT STDMETHODCALLTYPE Initialize(PCIDLIST_ABSOLUTE pidl);
+    virtual STDMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl) override;
 
-public:
+    // *** ICurrentWorkingDirectory methods ***
+    STDMETHODIMP GetDirectory(LPWSTR pwzPath, DWORD cchSize) override;
+    STDMETHODIMP SetDirectory(LPCWSTR pwzPath) override;
 
+public:
     DECLARE_REGISTRY_RESOURCEID(IDR_ACLISTISF)
     DECLARE_NOT_AGGREGATABLE(CACListISF)
 
@@ -72,5 +101,6 @@ public:
         // Windows doesn't return this
         //COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
+        COM_INTERFACE_ENTRY_IID(IID_ICurrentWorkingDirectory, ICurrentWorkingDirectory)
     END_COM_MAP()
 };