[SHELLFIND] Add missing dependency to CMakeLists.txt
[reactos.git] / dll / win32 / browseui / shellfind / CSearchBar.cpp
1 /*
2 * PROJECT: ReactOS Search Shell Extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Search UI
5 * COPYRIGHT: Copyright 2019 Brock Mammen
6 */
7
8 #include "CSearchBar.h"
9 #include <psdk/wingdi.h>
10 #include <commoncontrols.h>
11 #include <../browseui.h>
12 #include <shellapi.h>
13 #include <exdispid.h>
14
15 WINE_DEFAULT_DEBUG_CHANNEL(shellfind);
16
17 #if 1
18 #undef UNIMPLEMENTED
19
20 #define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__)
21 #endif
22
23 CSearchBar::CSearchBar() :
24 m_pSite(NULL),
25 m_bVisible(FALSE)
26 {
27 }
28
29 CSearchBar::~CSearchBar()
30 {
31 }
32
33 LRESULT CSearchBar::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
34 {
35 SetSearchInProgress(FALSE);
36
37 HWND hCombobox = GetDlgItem(IDC_SEARCH_COMBOBOX);
38 CComPtr<IImageList> pImageList;
39 HRESULT hResult = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &pImageList));
40 SendMessage(hCombobox, CBEM_SETIMAGELIST, 0, FAILED_UNEXPECTEDLY(hResult) ? 0 : reinterpret_cast<LPARAM>(pImageList.p));
41
42 SendMessage(hCombobox, CBEM_SETEXTENDEDSTYLE,
43 CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT, CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT);
44 HWND hEditControl = reinterpret_cast<HWND>(SendMessage(hCombobox, CBEM_GETEDITCONTROL, 0, 0));
45 hResult = CAddressEditBox_CreateInstance(IID_PPV_ARG(IAddressEditBox, &m_AddressEditBox));
46 if (FAILED_UNEXPECTEDLY(hResult))
47 return FALSE;
48
49 hResult = m_AddressEditBox->Init(hCombobox, hEditControl, 0, m_pSite);
50 if (FAILED_UNEXPECTEDLY(hResult))
51 return FALSE;
52
53 // Subscribe to navigation events
54 CComPtr<IShellBrowser> pShellBrowser;
55 hResult = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pShellBrowser));
56 DWORD dwAdviseCookie;
57 if (SUCCEEDED(hResult))
58 AtlAdvise(pShellBrowser, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &dwAdviseCookie);
59
60 // Invoke the navigate event in case a search results folder is already open
61 DISPPARAMS params = {0};
62 Invoke(DISPID_NAVIGATECOMPLETE2, GUID_NULL, 0, DISPATCH_METHOD, &params, NULL, NULL, NULL);
63
64 return TRUE;
65 }
66
67
68 // *** ATL event handlers ***
69 LRESULT CSearchBar::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
70 {
71 IUnknown_OnFocusChangeIS(m_pSite, static_cast<IDeskBand *>(this), TRUE);
72 bHandled = FALSE;
73 return TRUE;
74 }
75
76 HRESULT CSearchBar::GetSearchResultsFolder(IShellBrowser **ppShellBrowser, HWND *pHwnd, IShellFolder **ppShellFolder)
77 {
78 HRESULT hr;
79 CComPtr<IShellBrowser> pShellBrowser;
80 if (!ppShellBrowser)
81 ppShellBrowser = &pShellBrowser;
82 if (!*ppShellBrowser)
83 {
84 hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, ppShellBrowser));
85 if (FAILED_UNEXPECTEDLY(hr))
86 return hr;
87 }
88
89 CComPtr<IShellView> pShellView;
90 hr = (*ppShellBrowser)->QueryActiveShellView(&pShellView);
91 if (FAILED(hr) || !pShellView)
92 return hr;
93
94 CComPtr<IFolderView> pFolderView;
95 hr = pShellView->QueryInterface(IID_PPV_ARG(IFolderView, &pFolderView));
96 if (FAILED(hr) || !pFolderView)
97 return hr;
98
99 CComPtr<IShellFolder> pShellFolder;
100 if (!ppShellFolder)
101 ppShellFolder = &pShellFolder;
102 hr = pFolderView->GetFolder(IID_PPV_ARG(IShellFolder, ppShellFolder));
103 if (FAILED(hr) || !pShellFolder)
104 return hr;
105
106 CLSID clsid;
107 hr = IUnknown_GetClassID(*ppShellFolder, &clsid);
108 if (FAILED(hr))
109 return hr;
110 if (clsid != CLSID_FindFolder)
111 return E_FAIL;
112
113 if (pHwnd)
114 {
115 hr = pShellView->GetWindow(pHwnd);
116 if (FAILED_UNEXPECTEDLY(hr))
117 return hr;
118 }
119
120 return S_OK;
121 }
122
123 LRESULT CSearchBar::OnSearchButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
124 {
125 CComHeapPtr<SearchStart> pSearchStart(static_cast<SearchStart *>(CoTaskMemAlloc(sizeof(SearchStart))));
126 GetDlgItemText(IDC_SEARCH_FILENAME, pSearchStart->szFileName, _countof(pSearchStart->szFileName));
127 GetDlgItemText(IDC_SEARCH_QUERY, pSearchStart->szQuery, _countof(pSearchStart->szQuery));
128 if (!GetAddressEditBoxPath(pSearchStart->szPath))
129 {
130 ShellMessageBoxW(_AtlBaseModule.GetResourceInstance(), m_hWnd, MAKEINTRESOURCEW(IDS_SEARCHINVALID), MAKEINTRESOURCEW(IDS_SEARCHLABEL), MB_OK | MB_ICONERROR, pSearchStart->szPath);
131 return 0;
132 }
133
134 CComPtr<IShellBrowser> pShellBrowser;
135 HRESULT hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pShellBrowser));
136 if (FAILED_UNEXPECTEDLY(hr))
137 return 0;
138
139 HWND hwnd;
140 if (FAILED(GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL)))
141 {
142 // Open a new search results folder
143 WCHAR szShellGuid[MAX_PATH];
144 const WCHAR shellGuidPrefix[] = L"shell:::";
145 memcpy(szShellGuid, shellGuidPrefix, sizeof(shellGuidPrefix));
146 hr = StringFromGUID2(CLSID_FindFolder, szShellGuid + _countof(shellGuidPrefix) - 1,
147 _countof(szShellGuid) - _countof(shellGuidPrefix));
148 if (FAILED_UNEXPECTEDLY(hr))
149 return 0;
150
151 CComHeapPtr<ITEMIDLIST> findFolderPidl;
152 hr = SHParseDisplayName(szShellGuid, NULL, &findFolderPidl, 0, NULL);
153 if (FAILED_UNEXPECTEDLY(hr))
154 return 0;
155
156 hr = pShellBrowser->BrowseObject(findFolderPidl, 0);
157 if (FAILED_UNEXPECTEDLY(hr))
158 return 0;
159
160 hr = GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL);
161 if (FAILED_UNEXPECTEDLY(hr))
162 return 0;
163 }
164
165 ::PostMessageW(hwnd, WM_SEARCH_START, 0, (LPARAM) pSearchStart.Detach());
166
167 SetSearchInProgress(TRUE);
168
169 return 0;
170 }
171
172 LRESULT CSearchBar::OnStopButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
173 {
174 HWND hwnd;
175 HRESULT hr = GetSearchResultsFolder(NULL, &hwnd, NULL);
176 if (SUCCEEDED(hr))
177 ::PostMessageW(hwnd, WM_SEARCH_STOP, 0, 0);
178
179 return 0;
180 }
181
182 BOOL CSearchBar::GetAddressEditBoxPath(WCHAR *szPath)
183 {
184 HWND hComboboxEx = GetDlgItem(IDC_SEARCH_COMBOBOX);
185 ::GetWindowTextW(hComboboxEx, szPath, MAX_PATH);
186 INT iSelectedIndex = SendMessageW(hComboboxEx, CB_GETCURSEL, 0, 0);
187 if (iSelectedIndex != CB_ERR)
188 {
189 WCHAR szItemText[MAX_PATH];
190 COMBOBOXEXITEMW item = {0};
191 item.mask = CBEIF_LPARAM | CBEIF_TEXT;
192 item.iItem = iSelectedIndex;
193 item.pszText = szItemText;
194 item.cchTextMax = _countof(szItemText);
195 SendMessageW(hComboboxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
196
197 if (!wcscmp(szItemText, szPath) && SHGetPathFromIDListW((LPCITEMIDLIST)item.lParam, szItemText))
198 {
199 StringCbCopyW(szPath, MAX_PATH * sizeof(WCHAR), szItemText);
200 return TRUE;
201 }
202 }
203
204 DWORD dwAttributes = GetFileAttributesW(szPath);
205 return dwAttributes != INVALID_FILE_ATTRIBUTES
206 && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
207 }
208
209 LRESULT CSearchBar::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
210 {
211 INT iWidth = LOWORD(lParam);
212 INT iPadding = 10;
213
214 ((CWindow)GetDlgItem(IDC_SEARCH_LABEL)).SetWindowPos(NULL, 0, 0, iWidth - iPadding, 40, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
215
216 int inputs[] = { IDC_SEARCH_FILENAME, IDC_SEARCH_QUERY, IDC_SEARCH_COMBOBOX, IDC_SEARCH_BUTTON, IDC_SEARCH_STOP_BUTTON, IDC_PROGRESS_BAR };
217 HDWP hdwp = BeginDeferWindowPos(_countof(inputs));
218 for (SIZE_T i = 0; i < _countof(inputs); i++)
219 {
220 CWindow wnd = (CWindow) GetDlgItem(inputs[i]);
221 RECT rect;
222 wnd.GetWindowRect(&rect);
223 POINT pt = { rect.left, rect.top };
224 ScreenToClient(&pt);
225 hdwp = wnd.DeferWindowPos(hdwp,
226 HWND_TOP,
227 iPadding,
228 pt.y,
229 iWidth - iPadding * 2,
230 rect.bottom - rect.top,
231 SWP_NOZORDER | SWP_NOACTIVATE);
232 }
233 EndDeferWindowPos(hdwp);
234
235 return 0;
236 }
237
238
239 // *** IOleWindow methods ***
240 HRESULT STDMETHODCALLTYPE CSearchBar::GetWindow(HWND *lphwnd)
241 {
242 if (!lphwnd)
243 return E_INVALIDARG;
244 *lphwnd = m_hWnd;
245 return S_OK;
246 }
247
248 HRESULT STDMETHODCALLTYPE CSearchBar::ContextSensitiveHelp(BOOL fEnterMode)
249 {
250 UNIMPLEMENTED;
251 return E_NOTIMPL;
252 }
253
254
255 // *** IDockingWindow methods ***
256 HRESULT STDMETHODCALLTYPE CSearchBar::CloseDW(DWORD dwReserved)
257 {
258 // We do nothing, we don't have anything to save yet
259 TRACE("CloseDW called\n");
260 return S_OK;
261 }
262
263 HRESULT STDMETHODCALLTYPE CSearchBar::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
264 {
265 /* Must return E_NOTIMPL according to MSDN */
266 return E_NOTIMPL;
267 }
268
269 HRESULT STDMETHODCALLTYPE CSearchBar::ShowDW(BOOL fShow)
270 {
271 m_bVisible = fShow;
272 ShowWindow(fShow);
273 return S_OK;
274 }
275
276
277 // *** IDeskBand methods ***
278 HRESULT STDMETHODCALLTYPE CSearchBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
279 {
280 if (!pdbi)
281 {
282 return E_INVALIDARG;
283 }
284
285 if (pdbi->dwMask & DBIM_MINSIZE)
286 {
287 pdbi->ptMinSize.x = 200;
288 pdbi->ptMinSize.y = 30;
289 }
290
291 if (pdbi->dwMask & DBIM_MAXSIZE)
292 {
293 pdbi->ptMaxSize.y = -1;
294 }
295
296 if (pdbi->dwMask & DBIM_INTEGRAL)
297 {
298 pdbi->ptIntegral.y = 1;
299 }
300
301 if (pdbi->dwMask & DBIM_ACTUAL)
302 {
303 pdbi->ptActual.x = 200;
304 pdbi->ptActual.y = 30;
305 }
306
307 if (pdbi->dwMask & DBIM_TITLE)
308 {
309 if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_SEARCHLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
310 return HRESULT_FROM_WIN32(GetLastError());
311 }
312
313 if (pdbi->dwMask & DBIM_MODEFLAGS)
314 {
315 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
316 }
317
318 if (pdbi->dwMask & DBIM_BKCOLOR)
319 {
320 pdbi->dwMask &= ~DBIM_BKCOLOR;
321 }
322 return S_OK;
323 }
324
325
326 // *** IObjectWithSite methods ***
327 HRESULT STDMETHODCALLTYPE CSearchBar::SetSite(IUnknown *pUnkSite)
328 {
329 HRESULT hr;
330 HWND parentWnd;
331
332 if (pUnkSite == m_pSite)
333 return S_OK;
334
335 TRACE("SetSite called \n");
336 if (!pUnkSite)
337 {
338 DestroyWindow();
339 m_hWnd = NULL;
340 return S_OK;
341 }
342
343 hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
344 if (!SUCCEEDED(hr))
345 {
346 ERR("Could not get parent's window ! Status: %08lx\n", hr);
347 return E_INVALIDARG;
348 }
349
350 m_pSite = pUnkSite;
351
352 if (m_hWnd)
353 {
354 // Change its parent
355 SetParent(parentWnd);
356 }
357 else
358 {
359 CDialogImpl::Create(parentWnd);
360
361 }
362 return S_OK;
363 }
364
365 HRESULT STDMETHODCALLTYPE CSearchBar::GetSite(REFIID riid, void **ppvSite)
366 {
367 if (!ppvSite)
368 return E_POINTER;
369 *ppvSite = m_pSite;
370 return S_OK;
371 }
372
373
374 // *** IInputObject methods ***
375 HRESULT STDMETHODCALLTYPE CSearchBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
376 {
377 if (fActivate)
378 {
379 //SetFocus();
380 SetActiveWindow();
381 }
382 // TODO: handle message
383 if(lpMsg)
384 {
385 TranslateMessage(lpMsg);
386 DispatchMessage(lpMsg);
387 }
388 return S_OK;
389 }
390
391 HRESULT STDMETHODCALLTYPE CSearchBar::HasFocusIO()
392 {
393 return S_OK;
394 }
395
396 HRESULT STDMETHODCALLTYPE CSearchBar::TranslateAcceleratorIO(LPMSG lpMsg)
397 {
398 if (IsDialogMessage(lpMsg))
399 return S_OK;
400
401 if ((lpMsg->hwnd == m_hWnd || IsChild(lpMsg->hwnd)))
402 {
403 TranslateMessage(lpMsg);
404 DispatchMessage(lpMsg);
405 return S_OK;
406 }
407
408 return S_FALSE;
409 }
410
411 // *** IPersist methods ***
412 HRESULT STDMETHODCALLTYPE CSearchBar::GetClassID(CLSID *pClassID)
413 {
414 if (!pClassID)
415 return E_POINTER;
416 *pClassID = CLSID_FileSearchBand;
417 return S_OK;
418 }
419
420
421 // *** IPersistStream methods ***
422 HRESULT STDMETHODCALLTYPE CSearchBar::IsDirty()
423 {
424 UNIMPLEMENTED;
425 return E_NOTIMPL;
426 }
427
428 HRESULT STDMETHODCALLTYPE CSearchBar::Load(IStream *pStm)
429 {
430 UNIMPLEMENTED;
431 return E_NOTIMPL;
432 }
433
434 HRESULT STDMETHODCALLTYPE CSearchBar::Save(IStream *pStm, BOOL fClearDirty)
435 {
436 UNIMPLEMENTED;
437 return E_NOTIMPL;
438 }
439
440 HRESULT STDMETHODCALLTYPE CSearchBar::GetSizeMax(ULARGE_INTEGER *pcbSize)
441 {
442 // TODO: calculate max size
443 UNIMPLEMENTED;
444 return E_NOTIMPL;
445 }
446
447
448 // *** IDispatch methods ***
449 HRESULT STDMETHODCALLTYPE CSearchBar::GetTypeInfoCount(UINT *pctinfo)
450 {
451 UNIMPLEMENTED;
452 return E_NOTIMPL;
453 }
454
455 HRESULT STDMETHODCALLTYPE CSearchBar::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
456 {
457 UNIMPLEMENTED;
458 return E_NOTIMPL;
459 }
460
461 HRESULT STDMETHODCALLTYPE CSearchBar::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
462 {
463 UNIMPLEMENTED;
464 return E_NOTIMPL;
465 }
466
467 void CSearchBar::SetSearchInProgress(BOOL bInProgress)
468 {
469 ::ShowWindow(GetDlgItem(IDC_SEARCH_BUTTON), bInProgress ? SW_HIDE : SW_SHOW);
470 ::ShowWindow(GetDlgItem(IDC_SEARCH_STOP_BUTTON), bInProgress ? SW_SHOW : SW_HIDE);
471 HWND hProgressBar = GetDlgItem(IDC_PROGRESS_BAR);
472 ::ShowWindow(hProgressBar, bInProgress ? SW_SHOW : SW_HIDE);
473 ::PostMessage(hProgressBar, PBM_SETMARQUEE, bInProgress, 0);
474 }
475
476 HRESULT CSearchBar::TrySubscribeToSearchEvents()
477 {
478 CComPtr<IShellFolder> pShellFolder;
479 HRESULT hr = GetSearchResultsFolder(NULL, NULL, &pShellFolder);
480 if (FAILED(hr))
481 return hr;
482
483 DWORD fAdviseCookie;
484 hr = AtlAdvise(pShellFolder, static_cast<IDispatch *>(this), DIID_DSearchCommandEvents, &fAdviseCookie);
485 if (FAILED_UNEXPECTEDLY(hr))
486 return hr;
487
488 return S_OK;
489 }
490
491 HRESULT STDMETHODCALLTYPE CSearchBar::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
492 {
493 switch (dispIdMember)
494 {
495 case DISPID_NAVIGATECOMPLETE2:
496 case DISPID_DOCUMENTCOMPLETE:
497 {
498 TrySubscribeToSearchEvents();
499
500 // Remove the search results folder from the address box
501 CComPtr<IDispatch> pDispatch;
502 HRESULT hResult = m_AddressEditBox->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch));
503 if (FAILED_UNEXPECTEDLY(hResult))
504 return hResult;
505 pDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
506 CComPtr<IShellService> pShellService;
507 hResult = m_AddressEditBox->QueryInterface(IID_PPV_ARG(IShellService, &pShellService));
508 if (FAILED_UNEXPECTEDLY(hResult))
509 return hResult;
510 hResult = pShellService->SetOwner(NULL);
511 if (FAILED_UNEXPECTEDLY(hResult))
512 return hResult;
513 HWND hComboboxEx = GetDlgItem(IDC_SEARCH_COMBOBOX);
514 int index = SendMessageW(hComboboxEx, CB_GETCOUNT, 0, 0);
515 if (index <= 0)
516 return S_OK;
517 COMBOBOXEXITEMW item = {0};
518 item.mask = CBEIF_LPARAM;
519 item.iItem = index - 1;
520 SendMessageW(hComboboxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
521 if (!item.lParam)
522 return S_OK;
523 CComPtr<IShellFolder> pDesktopFolder;
524 hResult = SHGetDesktopFolder(&pDesktopFolder);
525 if (FAILED_UNEXPECTEDLY(hResult))
526 return hResult;
527 CComPtr<IShellFolder> pShellFolder;
528 hResult = pDesktopFolder->BindToObject((LPCITEMIDLIST)item.lParam, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
529 if (FAILED(hResult))
530 return S_OK;
531 CLSID clsid;
532 hResult = IUnknown_GetClassID(pShellFolder, &clsid);
533 if (SUCCEEDED(hResult) && clsid == CLSID_FindFolder)
534 {
535 SendMessageW(hComboboxEx, CBEM_DELETEITEM, item.iItem, 0);
536 SendMessageW(hComboboxEx, CB_SETCURSEL, 0, 0);
537 }
538 return S_OK;
539 }
540 case DISPID_SEARCHCOMPLETE:
541 case DISPID_SEARCHABORT:
542 SetSearchInProgress(FALSE);
543 return S_OK;
544 default:
545 return E_INVALIDARG;
546 }
547 }