*/
#include "CFindFolder.h"
+#include <exdispid.h>
WINE_DEFAULT_DEBUG_CHANNEL(shellfind);
struct FolderViewColumns
{
- LPCWSTR wzColumnName;
+ int iResource;
DWORD dwDefaultState;
int fmt;
int cxChar;
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() :
{
}
-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;
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;
+ ((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;
}
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)
{
{
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;
}
{
if (pClassId == NULL)
return E_INVALIDARG;
- memcpy(pClassId, &CLSID_FindFolder, sizeof(CLSID));
+ *pClassId = CLSID_FindFolder;
return S_OK;
}