[SHELLFIND] Localize column names
[reactos.git] / dll / win32 / browseui / shellfind / CFindFolder.cpp
index 4e986be..0d111c6 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "CFindFolder.h"
+#include <exdispid.h>
 
 WINE_DEFAULT_DEBUG_CHANNEL(shellfind);
 
@@ -19,7 +20,7 @@ SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder,
 
 struct FolderViewColumns
 {
-    LPCWSTR wzColumnName;
+    int iResource;
     DWORD dwDefaultState;
     int fmt;
     int cxChar;
@@ -27,9 +28,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() :
@@ -37,10 +38,18 @@ CFindFolder::CFindFolder() :
 {
 }
 
-static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath, LPCITEMIDLIST lpcFindDataPidl)
+static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath)
 {
+    CComHeapPtr<ITEMIDLIST> lpFSPidl(ILCreateFromPathW(lpszPath));
+    if (!(LPITEMIDLIST)lpFSPidl)
+    {
+        ERR("Failed to create pidl from path\n");
+        return 0;
+    }
+    LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl);
+
     int pathLen = (wcslen(lpszPath) + 1) * sizeof(WCHAR);
-    int cbData = sizeof(WORD) + pathLen + lpcFindDataPidl->mkid.cb;
+    int cbData = sizeof(WORD) + pathLen + lpLastFSPidl->mkid.cb;
     LPITEMIDLIST pidl = (LPITEMIDLIST) SHAlloc(cbData + sizeof(WORD));
     if (!pidl)
         return NULL;
@@ -52,8 +61,8 @@ static LPITEMIDLIST _ILCreate(LPCWSTR lpszPath, LPCITEMIDLIST lpcFindDataPidl)
     memcpy(p, lpszPath, pathLen);
     p += pathLen;
 
-    memcpy(p, lpcFindDataPidl, lpcFindDataPidl->mkid.cb);
-    p += lpcFindDataPidl->mkid.cb;
+    memcpy(p, lpLastFSPidl, lpLastFSPidl->mkid.cb);
+    p += lpLastFSPidl->mkid.cb;
 
     *((WORD *) p) = 0;
 
@@ -75,53 +84,238 @@ static LPCITEMIDLIST _ILGetFSPidl(LPCITEMIDLIST pidl)
                             + ((wcslen((LPCWSTR) pidl->mkid.abID) + 1) * sizeof(WCHAR)));
 }
 
-LRESULT CFindFolder::AddItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+struct _SearchData
 {
-    if (!lParam)
+    HWND hwnd;
+    HANDLE hStopEvent;
+    CStringW szPath;
+    CStringW szFileName;
+    CStringA szQueryA;
+    CStringW szQueryW;
+    CComPtr<CFindFolder> pFindFolder;
+};
+
+template<typename TChar, typename TString, int (&StrNCmp)(const TChar *, const TChar *, size_t)>
+static const TChar* StrStrN(const TChar *lpFirst, const TString &lpSrch, UINT cchMax)
+{
+    if (!lpFirst || lpSrch.IsEmpty() || !cchMax)
+        return NULL;
+
+    for (UINT i = cchMax; i > 0 && *lpFirst; i--, lpFirst++)
+    {
+        if (!StrNCmp(lpFirst, lpSrch, lpSrch.GetLength()))
+            return (const TChar*)lpFirst;
+    }
+
+    return NULL;
+}
+
+template<typename TChar, typename TString, int (&StrNCmp)(const TChar *, const TChar *, size_t)>
+static UINT StrStrNCount(const TChar *lpFirst, const TString &lpSrch, UINT cchMax)
+{
+    const TChar *lpSearchEnd = lpFirst + cchMax;
+    UINT uCount = 0;
+    while (lpFirst < lpSearchEnd && (lpFirst = StrStrN<TChar, TString, StrNCmp>(lpFirst, lpSrch, cchMax)))
+    {
+        uCount++;
+        lpFirst += lpSrch.GetLength();
+        cchMax = lpSearchEnd - lpFirst;
+    }
+    return uCount;
+}
+
+static UINT StrStrCountNIA(const CHAR *lpFirst, const CStringA &lpSrch, UINT cchMax)
+{
+    return StrStrNCount<CHAR, CStringA, _strnicmp>(lpFirst, lpSrch, cchMax);
+}
+
+static UINT StrStrCountNIW(const WCHAR *lpFirst, const CStringW &lpSrch, UINT cchMax)
+{
+    return StrStrNCount<WCHAR, CStringW, _wcsnicmp>(lpFirst, lpSrch, cchMax);
+}
+
+static UINT SearchFile(LPCWSTR lpFilePath, _SearchData *pSearchData)
+{
+    HANDLE hFile = CreateFileW(lpFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return 0;
+
+    DWORD size = GetFileSize(hFile, NULL);
+    HANDLE hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+    CloseHandle(hFile);
+    if (hFileMap == INVALID_HANDLE_VALUE)
         return 0;
 
-    HRESULT hr;
-    LPWSTR path = (LPWSTR) lParam;
+    LPBYTE lpFileContent = (LPBYTE) MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
+    CloseHandle(hFileMap);
+    if (!lpFileContent)
+        return 0;
 
-    CComPtr<IShellFolder> pShellFolder;
-    hr = SHGetDesktopFolder(&pShellFolder);
-    if (FAILED_UNEXPECTEDLY(hr))
+    UINT uMatches = 0;
+    // Check for UTF-16 BOM
+    if (size >= 2 && lpFileContent[0] == 0xFF && lpFileContent[1] == 0xFE)
     {
-        LocalFree(path);
-        return hr;
+        uMatches = StrStrCountNIW((LPCWSTR) lpFileContent, pSearchData->szQueryW, size / sizeof(WCHAR));
+    }
+    else
+    {
+        uMatches = StrStrCountNIA((LPCSTR) lpFileContent, pSearchData->szQueryA, size / sizeof(CHAR));
     }
 
-    CComHeapPtr<ITEMIDLIST> lpFSPidl;
-    DWORD pchEaten;
-    hr = pShellFolder->ParseDisplayName(NULL, NULL, path, &pchEaten, &lpFSPidl, NULL);
-    if (FAILED_UNEXPECTEDLY(hr))
+    UnmapViewOfFile(lpFileContent);
+
+    return uMatches;
+}
+
+static UINT RecursiveFind(LPCWSTR lpPath, _SearchData *pSearchData)
+{
+    if (WaitForSingleObject(pSearchData->hStopEvent, 0) != WAIT_TIMEOUT)
+        return 0;
+
+    WCHAR szPath[MAX_PATH];
+    WIN32_FIND_DATAW FindData;
+    HANDLE hFindFile;
+    BOOL bMoreFiles = TRUE;
+    UINT uTotalFound = 0;
+
+    PathCombineW(szPath, lpPath, L"*.*");
+
+    for (hFindFile = FindFirstFileW(szPath, &FindData);
+        bMoreFiles && hFindFile != INVALID_HANDLE_VALUE;
+        bMoreFiles = FindNextFileW(hFindFile, &FindData))
     {
-        LocalFree(path);
-        return hr;
+        if (!wcscmp(FindData.cFileName, L".") || !wcscmp(FindData.cFileName, L".."))
+            continue;
+
+        PathCombineW(szPath, lpPath, FindData.cFileName);
+
+        if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            CStringW status;
+            status.Format(IDS_SEARCH_FOLDER, FindData.cFileName);
+            PostMessageW(pSearchData->hwnd, WM_SEARCH_UPDATE_STATUS, 0, (LPARAM) StrDupW(status.GetBuffer()));
+
+            uTotalFound += RecursiveFind(szPath, pSearchData);
+        }
+        else if ((pSearchData->szFileName.IsEmpty() || PathMatchSpecW(FindData.cFileName, pSearchData->szFileName))
+                && (pSearchData->szQueryA.IsEmpty() || SearchFile(szPath, pSearchData)))
+        {
+            uTotalFound++;
+            PostMessageW(pSearchData->hwnd, WM_SEARCH_ADD_RESULT, 0, (LPARAM) StrDupW(szPath));
+        }
     }
 
-    LPITEMIDLIST lpLastFSPidl = ILFindLastID(lpFSPidl);
-    CComHeapPtr<ITEMIDLIST> lpSearchPidl(_ILCreate(path, lpLastFSPidl));
-    LocalFree(path);
-    if (!lpSearchPidl)
+    if (hFindFile != INVALID_HANDLE_VALUE)
+        FindClose(hFindFile);
+
+    return uTotalFound;
+}
+
+DWORD WINAPI CFindFolder::SearchThreadProc(LPVOID lpParameter)
+{
+    _SearchData *data = static_cast<_SearchData*>(lpParameter);
+
+    data->pFindFolder->NotifyConnections(DISPID_SEARCHSTART);
+
+    UINT uTotalFound = RecursiveFind(data->szPath, data);
+
+    data->pFindFolder->NotifyConnections(DISPID_SEARCHCOMPLETE);
+
+    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;
+}
+
+void CFindFolder::NotifyConnections(DISPID id)
+{
+    DISPPARAMS dispatchParams = {0};
+    CComDynamicUnkArray &subscribers =
+        IConnectionPointImpl<CFindFolder, &DIID_DSearchCommandEvents>::m_vec;
+    for (IUnknown** pSubscriber = subscribers.begin(); pSubscriber < subscribers.end(); pSubscriber++)
     {
-        return E_OUTOFMEMORY;
+        if (!*pSubscriber)
+            continue;
+
+        CComPtr<IDispatch> pDispatch;
+        HRESULT hResult = (*pSubscriber)->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch));
+        if (!FAILED_UNEXPECTEDLY(hResult))
+            pDispatch->Invoke(id, GUID_NULL, 0, DISPATCH_METHOD, &dispatchParams, NULL, NULL, NULL);
     }
+}
+
+LRESULT CFindFolder::StartSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+    if (!lParam)
+        return 0;
 
+    // Clear all previous search results
     UINT uItemIndex;
-    hr = m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex);
+    m_shellFolderView->RemoveObject(NULL, &uItemIndex);
 
-    return hr;
+    _SearchData* pSearchData = new _SearchData();
+    pSearchData->pFindFolder = this;
+    pSearchData->hwnd = m_hWnd;
+
+    SearchStart *pSearchParams = (SearchStart *) lParam;
+    pSearchData->szPath = pSearchParams->szPath;
+    pSearchData->szFileName = pSearchParams->szFileName;
+    pSearchData->szQueryA = pSearchParams->szQuery;
+    pSearchData->szQueryW = pSearchParams->szQuery;
+    SHFree(pSearchParams);
+
+    if (m_hStopEvent)
+        SetEvent(m_hStopEvent);
+    pSearchData->hStopEvent = m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    if (!SHCreateThread(SearchThreadProc, pSearchData, NULL, NULL))
+    {
+        SHFree(pSearchData);
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    return S_OK;
+}
+
+LRESULT CFindFolder::StopSearch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+    if (m_hStopEvent)
+    {
+        SetEvent(m_hStopEvent);
+        m_hStopEvent = NULL;
+    }
+    return 0;
+}
+
+LRESULT CFindFolder::AddResult(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+    if (!lParam)
+        return 0;
+
+    CComHeapPtr<WCHAR> lpPath((LPWSTR) lParam);
+
+    CComHeapPtr<ITEMIDLIST> lpSearchPidl(_ILCreate(lpPath));
+    if (lpSearchPidl)
+    {
+        UINT uItemIndex;
+        m_shellFolderView->AddObject(lpSearchPidl, &uItemIndex);
+    }
+
+    return 0;
 }
 
 LRESULT CFindFolder::UpdateStatus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
 {
-    LPWSTR status = (LPWSTR) lParam;
+    CComHeapPtr<WCHAR> status((LPWSTR) lParam);
     if (m_shellBrowser)
     {
         m_shellBrowser->SetStatusTextSB(status);
     }
-    LocalFree(status);
 
     return S_OK;
 }
@@ -172,7 +366,7 @@ 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)
     {
@@ -356,7 +550,7 @@ STDMETHODIMP CFindFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObjec
         {
             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_STRING, MAKEINTRESOURCEW(IDS_SEARCH_OPEN_FOLDER), MFS_ENABLED);
             _InsertMenuItemW(pqcminfo->hmenu, pqcminfo->indexMenu++, TRUE, pqcminfo->idCmdFirst++, MFT_SEPARATOR, NULL, 0);
             return S_OK;
         }
@@ -417,6 +611,6 @@ STDMETHODIMP CFindFolder::GetClassID(CLSID *pClassId)
 {
     if (pClassId == NULL)
         return E_INVALIDARG;
-    memcpy(pClassId, &CLSID_FindFolder, sizeof(CLSID));
+    *pClassId = CLSID_FindFolder;
     return S_OK;
 }