/*
- * ReactOS Explorer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * PROJECT: ReactOS Search Shell Extension
+ * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Search UI
+ * COPYRIGHT: Copyright 2019 Brock Mammen
*/
#include "CSearchBar.h"
#include <psdk/wingdi.h>
+#include <commoncontrols.h>
+#include <../browseui.h>
+#include <shellapi.h>
+#include <exdispid.h>
WINE_DEFAULT_DEBUG_CHANNEL(shellfind);
#endif
CSearchBar::CSearchBar() :
- pSite(NULL),
- fVisible(FALSE),
- bFocused(FALSE)
+ m_pSite(NULL),
+ m_bVisible(FALSE)
{
}
{
}
-void CSearchBar::InitializeSearchBar()
-{
- CreateWindowExW(0, WC_STATIC, L"Search by any or all of the criteria below.",
- WS_CHILD | WS_VISIBLE,
- 10, 10, 200, 40,
- m_hWnd, NULL,
- _AtlBaseModule.GetModuleInstance(), NULL);
-
- CreateWindowExW(0, WC_STATIC, L"A &word or phrase in the file:",
- WS_CHILD | WS_VISIBLE,
- 10, 50, 500, 20,
- m_hWnd, NULL,
- _AtlBaseModule.GetModuleInstance(), NULL);
- CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL,
- WS_BORDER | WS_CHILD | WS_VISIBLE,
- 10, 70, 100, 20,
- m_hWnd, NULL,
- _AtlBaseModule.GetModuleInstance(), NULL);
-
- CreateWindowExW(0, WC_STATIC, L"&Look in:",
- WS_CHILD | WS_VISIBLE,
- 10, 100, 500, 20,
- m_hWnd, NULL,
- _AtlBaseModule.GetModuleInstance(), NULL);
- CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, NULL,
- WS_BORDER | WS_CHILD | WS_VISIBLE,
- 10, 120, 100, 20,
- m_hWnd, NULL,
- _AtlBaseModule.GetModuleInstance(), NULL);
-
- CreateWindowExW(0, WC_BUTTON, L"Sea&rch",
- WS_BORDER | WS_CHILD | WS_VISIBLE,
- 10, 150, 100, 20,
- m_hWnd, NULL,
- _AtlBaseModule.GetModuleInstance(), NULL);
+LRESULT CSearchBar::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ SetSearchInProgress(FALSE);
+
+ HWND hCombobox = GetDlgItem(IDC_SEARCH_COMBOBOX);
+ CComPtr<IImageList> pImageList;
+ HRESULT hResult = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &pImageList));
+ SendMessage(hCombobox, CBEM_SETIMAGELIST, 0, FAILED_UNEXPECTEDLY(hResult) ? 0 : reinterpret_cast<LPARAM>(pImageList.p));
+
+ SendMessage(hCombobox, CBEM_SETEXTENDEDSTYLE,
+ CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT, CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT);
+ HWND hEditControl = reinterpret_cast<HWND>(SendMessage(hCombobox, CBEM_GETEDITCONTROL, 0, 0));
+ hResult = CAddressEditBox_CreateInstance(IID_PPV_ARG(IAddressEditBox, &m_AddressEditBox));
+ if (FAILED_UNEXPECTEDLY(hResult))
+ return hResult;
+
+ hResult = m_AddressEditBox->Init(hCombobox, hEditControl, 0, m_pSite);
+ if (FAILED_UNEXPECTEDLY(hResult))
+ return hResult;
+
+ // Subscribe to navigation events
+ CComPtr<IShellBrowser> pShellBrowser;
+ hResult = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pShellBrowser));
+ DWORD dwAdviseCookie;
+ if (SUCCEEDED(hResult))
+ AtlAdvise(pShellBrowser, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &dwAdviseCookie);
+
+ // Invoke the navigate event in case a search results folder is already open
+ DISPPARAMS params = {0};
+ Invoke(DISPID_NAVIGATECOMPLETE2, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);
+
+ return 0;
}
HRESULT CSearchBar::ExecuteCommand(CComPtr<IContextMenu>& menu, UINT nCmd)
HWND browserWnd;
HRESULT hr;
- hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
+ hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
if (FAILED_UNEXPECTEDLY(hr))
return hr;
// *** ATL event handlers ***
LRESULT CSearchBar::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
- bFocused = TRUE;
- IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), TRUE);
+ IUnknown_OnFocusChangeIS(m_pSite, static_cast<IDeskBand *>(this), TRUE);
bHandled = FALSE;
return TRUE;
}
+HRESULT CSearchBar::GetSearchResultsFolder(IShellBrowser **ppShellBrowser, HWND *pHwnd, IShellFolder **ppShellFolder)
+{
+ HRESULT hr;
+ CComPtr<IShellBrowser> pShellBrowser;
+ if (!ppShellBrowser)
+ ppShellBrowser = &pShellBrowser;
+ if (!*ppShellBrowser)
+ {
+ hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, ppShellBrowser));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ }
+
+ CComPtr<IShellView> pShellView;
+ hr = (*ppShellBrowser)->QueryActiveShellView(&pShellView);
+ if (FAILED(hr) || !pShellView)
+ return hr;
+
+ CComPtr<IFolderView> pFolderView;
+ hr = pShellView->QueryInterface(IID_PPV_ARG(IFolderView, &pFolderView));
+ if (FAILED(hr) || !pFolderView)
+ return hr;
+
+ CComPtr<IShellFolder> pShellFolder;
+ if (!ppShellFolder)
+ ppShellFolder = &pShellFolder;
+ hr = pFolderView->GetFolder(IID_PPV_ARG(IShellFolder, ppShellFolder));
+ if (FAILED(hr) || !pShellFolder)
+ return hr;
+
+ CLSID clsid;
+ hr = IUnknown_GetClassID(*ppShellFolder, &clsid);
+ if (FAILED(hr))
+ return hr;
+ if (clsid != CLSID_FindFolder)
+ return E_FAIL;
+
+ if (pHwnd)
+ {
+ hr = pShellView->GetWindow(pHwnd);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ }
+
+ return S_OK;
+}
+
+LRESULT CSearchBar::OnSearchButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
+{
+ CComHeapPtr<SearchStart> pSearchStart(static_cast<SearchStart *>(CoTaskMemAlloc(sizeof(SearchStart))));
+ GetDlgItemText(IDC_SEARCH_FILENAME, pSearchStart->szFileName, _countof(pSearchStart->szFileName));
+ GetDlgItemText(IDC_SEARCH_QUERY, pSearchStart->szQuery, _countof(pSearchStart->szQuery));
+ if (!GetAddressEditBoxPath(pSearchStart->szPath))
+ {
+ ShellMessageBoxW(_AtlBaseModule.GetResourceInstance(), m_hWnd, MAKEINTRESOURCEW(IDS_SEARCHINVALID), MAKEINTRESOURCEW(IDS_SEARCHLABEL), MB_OK | MB_ICONERROR, pSearchStart->szPath);
+ return TRUE;
+ }
+
+ CComPtr<IShellBrowser> pShellBrowser;
+ HRESULT hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pShellBrowser));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ HWND hwnd;
+ if (FAILED(GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL)))
+ {
+ // Open a new search results folder
+ WCHAR szShellGuid[MAX_PATH];
+ const WCHAR shellGuidPrefix[] = L"shell:::";
+ memcpy(szShellGuid, shellGuidPrefix, sizeof(shellGuidPrefix));
+ hr = StringFromGUID2(CLSID_FindFolder, szShellGuid + _countof(shellGuidPrefix) - 1,
+ _countof(szShellGuid) - _countof(shellGuidPrefix));
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ CComHeapPtr<ITEMIDLIST> findFolderPidl;
+ hr = SHParseDisplayName(szShellGuid, NULL, &findFolderPidl, 0, NULL);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ hr = pShellBrowser->BrowseObject(findFolderPidl, 0);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ hr = GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+ }
+
+ ::PostMessageW(hwnd, WM_SEARCH_START, 0, (LPARAM) pSearchStart.Detach());
+
+ SetSearchInProgress(TRUE);
+
+ return TRUE;
+}
+
+LRESULT CSearchBar::OnStopButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
+{
+ HWND hwnd;
+ HRESULT hr = GetSearchResultsFolder(NULL, &hwnd, NULL);
+ if (SUCCEEDED(hr))
+ ::PostMessageW(hwnd, WM_SEARCH_STOP, 0, 0);
+
+ return TRUE;
+}
+
+BOOL CSearchBar::GetAddressEditBoxPath(WCHAR (&szPath)[MAX_PATH])
+{
+ HWND hComboboxEx = GetDlgItem(IDC_SEARCH_COMBOBOX);
+ ::GetWindowTextW(hComboboxEx, szPath, _countof(szPath));
+ INT iSelectedIndex = SendMessageW(hComboboxEx, CB_GETCURSEL, 0, 0);
+ if (iSelectedIndex != CB_ERR)
+ {
+ WCHAR szItemText[MAX_PATH];
+ COMBOBOXEXITEMW item = {0};
+ item.mask = CBEIF_LPARAM | CBEIF_TEXT;
+ item.iItem = iSelectedIndex;
+ item.pszText = szItemText;
+ item.cchTextMax = _countof(szItemText);
+ SendMessageW(hComboboxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
+
+ if (!wcscmp(szItemText, szPath) && SHGetPathFromIDListW((LPCITEMIDLIST)item.lParam, szItemText))
+ {
+ StringCbCopyW(szPath, MAX_PATH * sizeof(WCHAR), szItemText);
+ return TRUE;
+ }
+ }
+
+ DWORD dwAttributes = GetFileAttributesW(szPath);
+ return dwAttributes != INVALID_FILE_ATTRIBUTES
+ && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+LRESULT CSearchBar::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ INT iWidth = LOWORD(lParam);
+ INT iPadding = 10;
+
+ ((CWindow)GetDlgItem(IDC_SEARCH_LABEL)).SetWindowPos(NULL, 0, 0, iWidth - iPadding, 40, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
+
+ int inputs[] = { IDC_SEARCH_FILENAME, IDC_SEARCH_QUERY, IDC_SEARCH_COMBOBOX, IDC_SEARCH_BUTTON, IDC_SEARCH_STOP_BUTTON, IDC_PROGRESS_BAR };
+ HDWP hdwp = BeginDeferWindowPos(_countof(inputs));
+ for (SIZE_T i = 0; i < _countof(inputs); i++)
+ {
+ CWindow wnd = (CWindow) GetDlgItem(inputs[i]);
+ RECT rect;
+ wnd.GetWindowRect(&rect);
+ POINT pt = { rect.left, rect.top };
+ ScreenToClient(&pt);
+ hdwp = wnd.DeferWindowPos(hdwp,
+ HWND_TOP,
+ iPadding,
+ pt.y,
+ iWidth - iPadding * 2,
+ rect.bottom - rect.top,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ EndDeferWindowPos(hdwp);
+
+ return 0;
+}
+
// *** IOleWindow methods ***
HRESULT STDMETHODCALLTYPE CSearchBar::GetWindow(HWND *lphwnd)
HRESULT STDMETHODCALLTYPE CSearchBar::ShowDW(BOOL fShow)
{
- fVisible = fShow;
+ m_bVisible = fShow;
ShowWindow(fShow);
return S_OK;
}
return S_OK;
}
-LRESULT CALLBACK MyWindowProc(
- _In_ HWND hwnd,
- _In_ UINT uMsg,
- _In_ WPARAM wParam,
- _In_ LPARAM lParam
-)
-{
- return 0;
-}
// *** IObjectWithSite methods ***
HRESULT STDMETHODCALLTYPE CSearchBar::SetSite(IUnknown *pUnkSite)
HRESULT hr;
HWND parentWnd;
- if (pUnkSite == pSite)
+ if (pUnkSite == m_pSite)
return S_OK;
TRACE("SetSite called \n");
{
DestroyWindow();
m_hWnd = NULL;
- }
-
- if (pUnkSite != pSite)
- {
- pSite = NULL;
- }
-
- if(!pUnkSite)
return S_OK;
+ }
hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
if (!SUCCEEDED(hr))
return E_INVALIDARG;
}
- pSite = pUnkSite;
+ m_pSite = pUnkSite;
if (m_hWnd)
{
}
else
{
- CWindowImpl::Create(parentWnd);
+ CDialogImpl::Create(parentWnd);
- InitializeSearchBar();
}
return S_OK;
}
{
if (!ppvSite)
return E_POINTER;
- *ppvSite = pSite;
+ *ppvSite = m_pSite;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CSearchBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
{
/* FIXME: we probably want to handle more services here */
- return IUnknown_QueryService(pSite, SID_SShellBrowser, riid, ppvObject);
+ return IUnknown_QueryService(m_pSite, SID_SShellBrowser, riid, ppvObject);
}
HRESULT STDMETHODCALLTYPE CSearchBar::HasFocusIO()
{
- return bFocused ? S_OK : S_FALSE;
+ return S_OK;
}
HRESULT STDMETHODCALLTYPE CSearchBar::TranslateAcceleratorIO(LPMSG lpMsg)
{
- if (lpMsg->hwnd == m_hWnd)
+ if (IsDialogMessage(lpMsg))
+ return S_OK;
+
+ if ((lpMsg->hwnd == m_hWnd || IsChild(lpMsg->hwnd)))
{
TranslateMessage(lpMsg);
DispatchMessage(lpMsg);
{
if (!pClassID)
return E_POINTER;
- memcpy(pClassID, &CLSID_FileSearchBand, sizeof(CLSID));
+ *pClassID = CLSID_FileSearchBand;
return S_OK;
}
return E_NOTIMPL;
}
+void CSearchBar::SetSearchInProgress(BOOL bInProgress)
+{
+ ::ShowWindow(GetDlgItem(IDC_SEARCH_BUTTON), bInProgress ? SW_HIDE : SW_SHOW);
+ ::ShowWindow(GetDlgItem(IDC_SEARCH_STOP_BUTTON), bInProgress ? SW_SHOW : SW_HIDE);
+ HWND hProgressBar = GetDlgItem(IDC_PROGRESS_BAR);
+ ::ShowWindow(hProgressBar, bInProgress ? SW_SHOW : SW_HIDE);
+ ::PostMessage(hProgressBar, PBM_SETMARQUEE, bInProgress, 0);
+}
+
+HRESULT CSearchBar::TrySubscribeToSearchEvents()
+{
+ CComPtr<IShellFolder> pShellFolder;
+ HRESULT hr = GetSearchResultsFolder(NULL, NULL, &pShellFolder);
+ if (FAILED(hr))
+ return hr;
+
+ DWORD fAdviseCookie;
+ hr = AtlAdvise(pShellFolder, static_cast<IDispatch *>(this), DIID_DSearchCommandEvents, &fAdviseCookie);
+ if (FAILED_UNEXPECTEDLY(hr))
+ return hr;
+
+ return S_OK;
+}
+
HRESULT STDMETHODCALLTYPE CSearchBar::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
- TRACE("Unknown dispid requested: %08x\n", dispIdMember);
- return E_INVALIDARG;
+ switch (dispIdMember)
+ {
+ case DISPID_NAVIGATECOMPLETE2:
+ case DISPID_DOCUMENTCOMPLETE:
+ {
+ TrySubscribeToSearchEvents();
+
+ // Remove the search results folder from the address box
+ CComPtr<IDispatch> pDispatch;
+ HRESULT hResult = m_AddressEditBox->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch));
+ if (FAILED_UNEXPECTEDLY(hResult))
+ return hResult;
+ pDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+ CComPtr<IShellService> pShellService;
+ hResult = m_AddressEditBox->QueryInterface(IID_PPV_ARG(IShellService, &pShellService));
+ if (FAILED_UNEXPECTEDLY(hResult))
+ return hResult;
+ hResult = pShellService->SetOwner(NULL);
+ if (FAILED_UNEXPECTEDLY(hResult))
+ return hResult;
+ HWND hComboboxEx = GetDlgItem(IDC_SEARCH_COMBOBOX);
+ int index = SendMessageW(hComboboxEx, CB_GETCOUNT, 0, 0);
+ if (index <= 0)
+ return S_OK;
+ COMBOBOXEXITEMW item = {0};
+ item.mask = CBEIF_LPARAM;
+ item.iItem = index - 1;
+ SendMessageW(hComboboxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
+ if (!item.lParam)
+ return S_OK;
+ CComPtr<IShellFolder> pDesktopFolder;
+ hResult = SHGetDesktopFolder(&pDesktopFolder);
+ if (FAILED_UNEXPECTEDLY(hResult))
+ return hResult;
+ CComPtr<IShellFolder> pShellFolder;
+ hResult = pDesktopFolder->BindToObject((LPCITEMIDLIST)item.lParam, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
+ if (FAILED(hResult))
+ return S_OK;
+ CLSID clsid;
+ hResult = IUnknown_GetClassID(pShellFolder, &clsid);
+ if (SUCCEEDED(hResult) && clsid == CLSID_FindFolder)
+ {
+ SendMessageW(hComboboxEx, CBEM_DELETEITEM, item.iItem, 0);
+ SendMessageW(hComboboxEx, CB_SETCURSEL, 0, 0);
+ }
+ return S_OK;
+ }
+ case DISPID_SEARCHCOMPLETE:
+ case DISPID_SEARCHABORT:
+ SetSearchInProgress(FALSE);
+ return S_OK;
+ default:
+ return E_INVALIDARG;
+ }
}