[SHELL] IPersistFolder::Initialize takes a PCIDLIST_ABSOLUTE. CORE-16385
[reactos.git] / dll / win32 / browseui / shellfind / CFindFolder.cpp
index 198ccfb..0a2aec8 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * PROJECT:     ReactOS Search Shell Extension
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Search results folder
+ * COPYRIGHT:   Copyright 2019 Brock Mammen
+ */
+
 #include "CFindFolder.h"
 #include <exdispid.h>
 
@@ -11,9 +18,75 @@ SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder,
                            PCUITEMID_CHILD_ARRAY apidl,
                            DWORD dwFlags);
 
+static HRESULT SHELL32_CoCreateInitSF(LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
+                                LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
+{
+    HRESULT hr;
+    CComPtr<IShellFolder> pShellFolder;
+
+    hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
+    if (FAILED(hr))
+        return hr;
+
+    LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
+    CComPtr<IPersistFolder> ppf;
+    CComPtr<IPersistFolder3> ppf3;
+
+    if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
+    {
+        ppf3->InitializeEx(NULL, pidlAbsolute, ppfti);
+    }
+    else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
+    {
+        ppf->Initialize(pidlAbsolute);
+    }
+    ILFree (pidlAbsolute);
+
+    return pShellFolder->QueryInterface(riid, ppvOut);
+}
+
+static void WINAPI _InsertMenuItemW(
+        HMENU hMenu,
+        UINT indexMenu,
+        BOOL fByPosition,
+        UINT wID,
+        UINT fType,
+        LPCWSTR dwTypeData,
+        UINT fState)
+{
+    MENUITEMINFOW mii;
+    WCHAR wszText[100];
+
+    ZeroMemory(&mii, sizeof(mii));
+    mii.cbSize = sizeof(mii);
+    if (fType == MFT_SEPARATOR)
+        mii.fMask = MIIM_ID | MIIM_TYPE;
+    else if (fType == MFT_STRING)
+    {
+        mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
+        if (IS_INTRESOURCE(dwTypeData))
+        {
+            if (LoadStringW(_AtlBaseModule.GetResourceInstance(), LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
+                mii.dwTypeData = wszText;
+            else
+            {
+                ERR("failed to load string %p\n", dwTypeData);
+                return;
+            }
+        }
+        else
+            mii.dwTypeData = (LPWSTR)dwTypeData;
+        mii.fState = fState;
+    }
+
+    mii.wID = wID;
+    mii.fType = fType;
+    InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
+}
+
 struct FolderViewColumns
 {
-    LPCWSTR wzColumnName;
+    int iResource;
     DWORD dwDefaultState;
     int fmt;
     int cxChar;
@@ -21,9 +94,9 @@ struct FolderViewColumns
 
 static FolderViewColumns g_ColumnDefs[] =
 {
-    {L"Name",      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
-    {L"In Folder", SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
-    {L"Relevance", SHCOLSTATE_TYPE_STR,                          LVCFMT_LEFT, 0}
+    {IDS_COL_NAME,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
+    {IDS_COL_LOCATION,  SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
+    {IDS_COL_RELEVANCE, SHCOLSTATE_TYPE_STR,                          LVCFMT_LEFT, 0}
 };
 
 CFindFolder::CFindFolder() :
@@ -34,14 +107,14 @@ CFindFolder::CFindFolder() :
 static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath)
 {
     CComHeapPtr<ITEMIDLIST> lpFSPidl(ILCreateFromPathW(lpszPath));
-    if (!(LPITEMIDLIST)lpFSPidl)
+    if (!lpFSPidl)
     {
         ERR("Failed to create pidl from path\n");
-        return 0;
+        return NULL;
     }
     LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl);
 
-    int pathLen = (wcslen(lpszPath) + 1) * sizeof(WCHAR);
+    int pathLen = (PathFindFileNameW(lpszPath) - lpszPath) * sizeof(WCHAR);
     int cbData = sizeof(WORD) + pathLen + lpLastFSPidl->mkid.cb;
     LPITEMIDLIST pidl = (LPITEMIDLIST) SHAlloc(cbData + sizeof(WORD));
     if (!pidl)
@@ -52,7 +125,9 @@ static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath)
     p += sizeof(WORD);
 
     memcpy(p, lpszPath, pathLen);
-    p += pathLen;
+    p += pathLen - sizeof(WCHAR);
+    *((WCHAR *) p) = '\0';
+    p += sizeof(WCHAR);
 
     memcpy(p, lpLastFSPidl, lpLastFSPidl->mkid.cb);
     p += lpLastFSPidl->mkid.cb;
@@ -117,14 +192,14 @@ static UINT StrStrNCount(const TChar *lpFirst, const TString &lpSrch, UINT cchMa
     return uCount;
 }
 
-static UINT StrStrCountA(const CHAR *lpFirst, const CStringA &lpSrch, UINT cchMax)
+static UINT StrStrCountNIA(const CHAR *lpFirst, const CStringA &lpSrch, UINT cchMax)
 {
-    return StrStrNCount<CHAR, CStringA, strncmp>(lpFirst, lpSrch, cchMax);
+    return StrStrNCount<CHAR, CStringA, _strnicmp>(lpFirst, lpSrch, cchMax);
 }
 
-static UINT StrStrCountW(const WCHAR *lpFirst, const CStringW &lpSrch, UINT cchMax)
+static UINT StrStrCountNIW(const WCHAR *lpFirst, const CStringW &lpSrch, UINT cchMax)
 {
-    return StrStrNCount<WCHAR, CStringW, wcsncmp>(lpFirst, lpSrch, cchMax);
+    return StrStrNCount<WCHAR, CStringW, _wcsnicmp>(lpFirst, lpSrch, cchMax);
 }
 
 static UINT SearchFile(LPCWSTR lpFilePath, _SearchData *pSearchData)
@@ -148,11 +223,11 @@ static UINT SearchFile(LPCWSTR lpFilePath, _SearchData *pSearchData)
     // Check for UTF-16 BOM
     if (size >= 2 && lpFileContent[0] == 0xFF && lpFileContent[1] == 0xFE)
     {
-        uMatches = StrStrCountW((LPCWSTR) lpFileContent, pSearchData->szQueryW, size / sizeof(WCHAR));
+        uMatches = StrStrCountNIW((LPCWSTR) lpFileContent, pSearchData->szQueryW, size / sizeof(WCHAR));
     }
     else
     {
-        uMatches = StrStrCountA((LPCSTR) lpFileContent, pSearchData->szQueryA, size / sizeof(CHAR));
+        uMatches = StrStrCountNIA((LPCSTR) lpFileContent, pSearchData->szQueryA, size / sizeof(CHAR));
     }
 
     UnmapViewOfFile(lpFileContent);
@@ -217,7 +292,9 @@ DWORD WINAPI CFindFolder::SearchThreadProc(LPVOID lpParameter)
     CStringW status;
     status.Format(IDS_SEARCH_FILES_FOUND, uTotalFound);
     ::PostMessageW(data->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) StrDupW(status.GetBuffer()));
+    ::SendMessageW(data->hwnd, WM_SEARCH_STOP, 0, 0);
 
+    CloseHandle(data->hStopEvent);
     delete data;
 
     return 0;
@@ -267,10 +344,10 @@ LRESULT CFindFolder::StartSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &
     if (!SHCreateThread(SearchThreadProc, pSearchData, NULL, NULL))
     {
         SHFree(pSearchData);
-        return HRESULT_FROM_WIN32(GetLastError());
+        return 0;
     }
 
-    return S_OK;
+    return 0;
 }
 
 LRESULT CFindFolder::StopSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
@@ -308,7 +385,7 @@ LRESULT CFindFolder::UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
         m_shellBrowser->SetStatusTextSB(status);
     }
 
-    return S_OK;
+    return 0;
 }
 
 // *** IShellFolder2 methods ***
@@ -357,14 +434,11 @@ STDMETHODIMP CFindFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELL
     pDetails->fmt = g_ColumnDefs[iColumn].fmt;
 
     if (!pidl)
-        return SHSetStrRet(&pDetails->str, g_ColumnDefs[iColumn].wzColumnName);
+        return SHSetStrRet(&pDetails->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource);
 
     if (iColumn == 1)
     {
-        WCHAR path[MAX_PATH];
-        wcscpy(path, _ILGetPath(pidl));
-        PathRemoveFileSpecW(path);
-        return SHSetStrRet(&pDetails->str, path);
+        return SHSetStrRet(&pDetails->str, _ILGetPath(pidl));
     }
 
     return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
@@ -438,28 +512,88 @@ STDMETHODIMP CFindFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl
     return m_pisfInner->GetAttributesOf(cidl, aFSPidl, rgfInOut);
 }
 
-STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid,
-                                        UINT *prgfInOut, LPVOID *ppvOut)
+class CFindFolderContextMenu :
+        public IContextMenu,
+        public CComObjectRootEx<CComMultiThreadModelNoCS>
 {
-    if (riid == IID_IDataObject && cidl == 1)
+    CComPtr<IContextMenu> m_pInner;
+    CComPtr<IShellFolderView> m_shellFolderView;
+    UINT m_firstCmdId;
+    static const UINT ADDITIONAL_MENU_ITEMS = 2;
+
+    //// *** IContextMenu methods ***
+    STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
     {
-        WCHAR path[MAX_PATH];
-        wcscpy(path, (LPCWSTR) apidl[0]->mkid.abID);
-        PathRemoveFileSpecW(path);
-        CComHeapPtr<ITEMIDLIST> rootPidl(ILCreateFromPathW(path));
-        if (!rootPidl)
-            return E_OUTOFMEMORY;
-        PCITEMID_CHILD aFSPidl[1];
-        aFSPidl[0] = _ILGetFSPidl(apidl[0]);
-        return IDataObject_Constructor(hwndOwner, rootPidl, aFSPidl, cidl, (IDataObject **) ppvOut);
+        m_firstCmdId = indexMenu;
+        _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst++, MFT_STRING, MAKEINTRESOURCEW(IDS_SEARCH_OPEN_FOLDER), MFS_ENABLED);
+        _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst++, MFT_SEPARATOR, NULL, 0);
+        return m_pInner->QueryContextMenu(hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
     }
 
+    STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
+    {
+        if (!IS_INTRESOURCE(lpcmi->lpVerb))
+        {
+            return m_pInner->InvokeCommand(lpcmi);
+        }
+
+        if (LOWORD(lpcmi->lpVerb) < m_firstCmdId + ADDITIONAL_MENU_ITEMS)
+        {
+            PCUITEMID_CHILD *apidl;
+            UINT cidl;
+            HRESULT hResult = m_shellFolderView->GetSelectedObjects(&apidl, &cidl);
+            if (FAILED_UNEXPECTEDLY(hResult))
+                return hResult;
+
+            for (UINT i = 0; i < cidl; i++)
+            {
+                CComHeapPtr<ITEMIDLIST> folderPidl(ILCreateFromPathW(_ILGetPath(apidl[i])));
+                if (!folderPidl)
+                    return E_OUTOFMEMORY;
+                LPCITEMIDLIST pidl = _ILGetFSPidl(apidl[i]);
+                SHOpenFolderAndSelectItems(folderPidl, 1, &pidl, 0);
+            }
+            return S_OK;
+        }
+
+        CMINVOKECOMMANDINFOEX actualCmdInfo;
+        memcpy(&actualCmdInfo, lpcmi, lpcmi->cbSize);
+        actualCmdInfo.lpVerb -= ADDITIONAL_MENU_ITEMS;
+        return m_pInner->InvokeCommand((CMINVOKECOMMANDINFO *)&actualCmdInfo);
+    }
+
+    STDMETHODIMP GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
+    {
+        return m_pInner->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
+    }
+
+public:
+    static HRESULT Create(IShellFolderView *pShellFolderView, IContextMenu *pInnerContextMenu, IContextMenu **pContextMenu)
+    {
+        CComObject<CFindFolderContextMenu> *pObj;
+        HRESULT hResult = CComObject<CFindFolderContextMenu>::CreateInstance(&pObj);
+        if (FAILED_UNEXPECTEDLY(hResult))
+            return hResult;
+        pObj->m_shellFolderView = pShellFolderView;
+        pObj->m_pInner = pInnerContextMenu;
+        return pObj->QueryInterface(IID_PPV_ARG(IContextMenu, pContextMenu));
+    }
+
+    BEGIN_COM_MAP(CFindFolderContextMenu)
+        COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
+    END_COM_MAP()
+};
+
+STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid,
+                                        UINT *prgfInOut, LPVOID *ppvOut)
+{
     if (cidl <= 0)
     {
         return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
     }
 
-    PCITEMID_CHILD *aFSPidl = new PCITEMID_CHILD[cidl];
+    CComHeapPtr<PCITEMID_CHILD> aFSPidl;
+    aFSPidl.Allocate(cidl);
     for (UINT i = 0; i < cidl; i++)
     {
         aFSPidl[i] = _ILGetFSPidl(apidl[i]);
@@ -467,30 +601,25 @@ STDMETHODIMP CFindFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHI
 
     if (riid == IID_IContextMenu)
     {
-        HKEY hKeys[16];
-        UINT cKeys = 0;
-        AddFSClassKeysToArray(aFSPidl[0], hKeys, &cKeys);
-
-        DEFCONTEXTMENU dcm;
-        dcm.hwnd = hwndOwner;
-        dcm.pcmcb = this;
-        dcm.pidlFolder = m_pidl;
-        dcm.psf = this;
-        dcm.cidl = cidl;
-        dcm.apidl = apidl;
-        dcm.cKeys = cKeys;
-        dcm.aKeys = hKeys;
-        dcm.punkAssociationInfo = NULL;
-        HRESULT hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
-        delete[] aFSPidl;
-
-        return hr;
+        CComHeapPtr<ITEMIDLIST> folderPidl(ILCreateFromPathW(_ILGetPath(apidl[0])));
+        if (!folderPidl)
+            return E_OUTOFMEMORY;
+        CComPtr<IShellFolder> pDesktopFolder;
+        HRESULT hResult = SHGetDesktopFolder(&pDesktopFolder);
+        if (FAILED_UNEXPECTEDLY(hResult))
+            return hResult;
+        CComPtr<IShellFolder> pShellFolder;
+        hResult = pDesktopFolder->BindToObject(folderPidl, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
+        if (FAILED_UNEXPECTEDLY(hResult))
+            return hResult;
+        CComPtr<IContextMenu> pContextMenu;
+        hResult = pShellFolder->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, (LPVOID *)&pContextMenu);
+        if (FAILED_UNEXPECTEDLY(hResult))
+            return hResult;
+        return CFindFolderContextMenu::Create(m_shellFolderView, pContextMenu, (IContextMenu **)ppvOut);
     }
 
-    HRESULT hr = m_pisfInner->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, ppvOut);
-    delete[] aFSPidl;
-
-    return hr;
+    return m_pisfInner->GetUIObjectOf(hwndOwner, cidl, aFSPidl, riid, prgfInOut, ppvOut);
 }
 
 STDMETHODIMP CFindFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET pName)
@@ -518,73 +647,41 @@ STDMETHODIMP CFindFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
         }
         case SFVM_WINDOWCREATED:
         {
+            // Subclass window to receive window messages
             SubclassWindow((HWND) wParam);
 
+            // Get shell browser for updating status bar text
             CComPtr<IServiceProvider> pServiceProvider;
             HRESULT hr = m_shellFolderView->QueryInterface(IID_PPV_ARG(IServiceProvider, &pServiceProvider));
             if (FAILED_UNEXPECTEDLY(hr))
-            {
                 return hr;
-            }
-            return pServiceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &m_shellBrowser));
-        }
-    }
-    return E_NOTIMPL;
-}
-
-//// *** IContextMenuCB method ***
-STDMETHODIMP CFindFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
-    switch (uMsg)
-    {
-        case DFM_MERGECONTEXTMENU:
-        {
-            QCMINFO *pqcminfo = (QCMINFO *) lParam;
-            _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_SEPARATOR, NULL, 0);
-            _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_STRING, L"Open Containing Folder", MFS_ENABLED);
-            _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_SEPARATOR, NULL, 0);
-            return S_OK;
-        }
-        case DFM_INVOKECOMMAND:
-        case DFM_INVOKECOMMANDEX:
-        {
-            if (wParam != 1)
-                break;
-
-            PCUITEMID_CHILD *apidl;
-            UINT cidl;
-            HRESULT hr = m_shellFolderView->GetSelectedObjects(&apidl, &cidl);
+            hr = pServiceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &m_shellBrowser));
             if (FAILED_UNEXPECTEDLY(hr))
                 return hr;
 
-            for (UINT i = 0; i < cidl; i++)
-            {
-                CComHeapPtr<ITEMIDLIST> pidl;
-                DWORD attrs = 0;
-                hr = SHILCreateFromPathW((LPCWSTR) apidl[i]->mkid.abID, &pidl, &attrs);
-                if (SUCCEEDED(hr))
-                {
-                    SHOpenFolderAndSelectItems(NULL, 1, &pidl, 0);
-                }
-            }
-
-            return S_OK;
+            // Open search bar
+            CComPtr<IWebBrowser2> pWebBrowser2;
+            hr = m_shellBrowser->QueryInterface(IID_PPV_ARG(IWebBrowser2, &pWebBrowser2));
+            if (FAILED_UNEXPECTEDLY(hr))
+                return hr;
+            WCHAR pwszGuid[MAX_PATH];
+            StringFromGUID2(CLSID_FileSearchBand, pwszGuid, _countof(pwszGuid));
+            CComVariant searchBar(pwszGuid);
+            return pWebBrowser2->ShowBrowserBar(&searchBar, NULL, NULL);
         }
-        case DFM_GETDEFSTATICID:
-            return S_FALSE;
     }
-    return Shell_DefaultContextMenuCallBack(m_pisfInner, pdtobj);
+    return E_NOTIMPL;
 }
 
 //// *** IPersistFolder2 methods ***
-STDMETHODIMP CFindFolder::GetCurFolder(LPITEMIDLIST *pidl)
+STDMETHODIMP CFindFolder::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
 {
     *pidl = ILClone(m_pidl);
     return S_OK;
 }
 
 // *** IPersistFolder methods ***
-STDMETHODIMP CFindFolder::Initialize(LPCITEMIDLIST pidl)
+STDMETHODIMP CFindFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
 {
     m_pidl = ILClone(pidl);
     if (!m_pidl)
@@ -602,6 +699,6 @@ STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassId)
 {
     if (pClassId == NULL)
         return E_INVALIDARG;
-    memcpy(pClassId, &CLSID_FindFolder, sizeof(CLSID));
+    *pClassId = CLSID_FindFolder;
     return S_OK;
 }