[SHELLFIND] Use assignment instead of memcpy
[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 pSite(NULL),
25 fVisible(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 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));
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 hResult;
48
49 CComPtr<IShellService> pShellService;
50 hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IShellService, &pShellService));
51 if (FAILED_UNEXPECTEDLY(hResult))
52 return hResult;
53 hResult = fAddressEditBox->Init(hCombobox, fEditControl, 0, pSite);
54 if (FAILED_UNEXPECTEDLY(hResult))
55 return hResult;
56
57 CComPtr<IDispatch> pDispatch;
58 hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch));
59 if (FAILED_UNEXPECTEDLY(hResult))
60 return hResult;
61 DISPPARAMS params = {0};
62 hResult = pDispatch->Invoke(DISPID_NAVIGATECOMPLETE2, GUID_NULL, 0, DISPATCH_METHOD, &params, NULL, NULL, NULL);
63
64 hResult = pShellService->SetOwner(NULL);
65 if (FAILED_UNEXPECTEDLY(hResult))
66 return hResult;
67
68 return 0;
69 }
70
71 HRESULT CSearchBar::ExecuteCommand(CComPtr<IContextMenu>& menu, UINT nCmd)
72 {
73 CComPtr<IOleWindow> pBrowserOleWnd;
74 CMINVOKECOMMANDINFO cmi;
75 HWND browserWnd;
76 HRESULT hr;
77
78 hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
79 if (FAILED_UNEXPECTEDLY(hr))
80 return hr;
81
82 hr = pBrowserOleWnd->GetWindow(&browserWnd);
83 if (FAILED_UNEXPECTEDLY(hr))
84 return hr;
85
86 ZeroMemory(&cmi, sizeof(cmi));
87 cmi.cbSize = sizeof(cmi);
88 cmi.lpVerb = MAKEINTRESOURCEA(nCmd);
89 cmi.hwnd = browserWnd;
90 if (GetKeyState(VK_SHIFT) & 0x8000)
91 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
92 if (GetKeyState(VK_CONTROL) & 0x8000)
93 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
94
95 return menu->InvokeCommand(&cmi);
96 }
97
98
99 // *** ATL event handlers ***
100 LRESULT CSearchBar::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
101 {
102 IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), TRUE);
103 bHandled = FALSE;
104 return TRUE;
105 }
106
107 HRESULT CSearchBar::GetSearchResultsFolder(IShellBrowser **ppShellBrowser, HWND *pHwnd, IShellFolder **ppShellFolder)
108 {
109 HRESULT hr;
110 CComPtr<IShellBrowser> pShellBrowser;
111 if (!ppShellBrowser)
112 ppShellBrowser = &pShellBrowser;
113 if (!*ppShellBrowser)
114 {
115 hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, ppShellBrowser));
116 if (FAILED_UNEXPECTEDLY(hr))
117 return hr;
118 }
119
120 CComPtr<IShellView> pShellView;
121 hr = (*ppShellBrowser)->QueryActiveShellView(&pShellView);
122 if (FAILED(hr) || !pShellView)
123 return hr;
124
125 CComPtr<IFolderView> pFolderView;
126 hr = pShellView->QueryInterface(IID_PPV_ARG(IFolderView, &pFolderView));
127 if (FAILED(hr) || !pFolderView)
128 return hr;
129
130 CComPtr<IShellFolder> pShellFolder;
131 if (!ppShellFolder)
132 ppShellFolder = &pShellFolder;
133 hr = pFolderView->GetFolder(IID_PPV_ARG(IShellFolder, ppShellFolder));
134 if (FAILED(hr) || !pShellFolder)
135 return hr;
136
137 CLSID clsid;
138 hr = IUnknown_GetClassID(*ppShellFolder, &clsid);
139 if (FAILED(hr))
140 return hr;
141 if (clsid != CLSID_FindFolder)
142 return E_FAIL;
143
144 if (pHwnd)
145 {
146 hr = pShellView->GetWindow(pHwnd);
147 if (FAILED_UNEXPECTEDLY(hr))
148 return hr;
149 }
150
151 return S_OK;
152 }
153
154 LRESULT CSearchBar::OnSearchButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
155 {
156 CComHeapPtr<SearchStart> pSearchStart((SearchStart *)SHAlloc(sizeof(SearchStart)));
157 GetDlgItemText(IDC_SEARCH_FILENAME, pSearchStart->szFileName, _countof(pSearchStart->szFileName));
158 GetDlgItemText(IDC_SEARCH_QUERY, pSearchStart->szQuery, _countof(pSearchStart->szQuery));
159 if (!GetAddressEditBoxPath(pSearchStart->szPath))
160 {
161 ShellMessageBoxW(_AtlBaseModule.GetResourceInstance(), m_hWnd, MAKEINTRESOURCEW(IDS_SEARCHINVALID), MAKEINTRESOURCEW(IDS_SEARCHLABEL), MB_OK | MB_ICONERROR, pSearchStart->szPath);
162 return TRUE;
163 }
164
165 CComPtr<IShellBrowser> pShellBrowser;
166 HRESULT hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pShellBrowser));
167 if (FAILED_UNEXPECTEDLY(hr))
168 return hr;
169
170 HWND hwnd;
171 if (FAILED(GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL)))
172 {
173 // Open a new search results folder
174 WCHAR szShellGuid[MAX_PATH];
175 const WCHAR shellGuidPrefix[] = L"shell:::";
176 memcpy(szShellGuid, shellGuidPrefix, sizeof(shellGuidPrefix));
177 hr = StringFromGUID2(CLSID_FindFolder, szShellGuid + _countof(shellGuidPrefix) - 1,
178 _countof(szShellGuid) - _countof(shellGuidPrefix));
179 if (FAILED_UNEXPECTEDLY(hr))
180 return hr;
181
182 CComHeapPtr<ITEMIDLIST> findFolderPidl;
183 hr = SHParseDisplayName(szShellGuid, NULL, &findFolderPidl, 0, NULL);
184 if (FAILED_UNEXPECTEDLY(hr))
185 return hr;
186
187 hr = pShellBrowser->BrowseObject(findFolderPidl, 0);
188 if (FAILED_UNEXPECTEDLY(hr))
189 return hr;
190
191 CComPtr<IShellFolder> pShellFolder;
192 hr = GetSearchResultsFolder(*pShellBrowser, &hwnd, &pShellFolder);
193 if (FAILED_UNEXPECTEDLY(hr))
194 return hr;
195
196 // Subscribe to search events
197 DWORD fAdviseCookie;
198 hr = AtlAdvise(pShellFolder, static_cast<IDispatch *>(this), DIID_DSearchCommandEvents, &fAdviseCookie);
199 if (FAILED_UNEXPECTEDLY(hr))
200 return hr;
201 }
202
203 ::PostMessageW(hwnd, WM_SEARCH_START, 0, (LPARAM) pSearchStart.Detach());
204
205 SetSearchInProgress(TRUE);
206
207 return TRUE;
208 }
209
210 LRESULT CSearchBar::OnStopButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
211 {
212 HWND hwnd;
213 HRESULT hr = GetSearchResultsFolder(NULL, &hwnd, NULL);
214 if (SUCCEEDED(hr))
215 ::PostMessageW(hwnd, WM_SEARCH_STOP, 0, 0);
216
217 return TRUE;
218 }
219
220 BOOL CSearchBar::GetAddressEditBoxPath(WCHAR (&szPath)[MAX_PATH])
221 {
222 HWND hComboboxEx = GetDlgItem(IDC_SEARCH_COMBOBOX);
223 ::GetWindowTextW(hComboboxEx, szPath, _countof(szPath));
224 INT iSelectedIndex = SendMessageW(hComboboxEx, CB_GETCURSEL, 0, 0);
225 if (iSelectedIndex != CB_ERR)
226 {
227 WCHAR szItemText[MAX_PATH];
228 COMBOBOXEXITEMW item = {0};
229 item.mask = CBEIF_LPARAM | CBEIF_TEXT;
230 item.iItem = iSelectedIndex;
231 item.pszText = szItemText;
232 item.cchTextMax = _countof(szItemText);
233 SendMessageW(hComboboxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
234
235 if (!wcscmp(szItemText, szPath) && SHGetPathFromIDListW((LPCITEMIDLIST)item.lParam, szPath))
236 {
237 return TRUE;
238 }
239 }
240
241 DWORD dwAttributes = GetFileAttributesW(szPath);
242 return dwAttributes != INVALID_FILE_ATTRIBUTES
243 && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
244 }
245
246 LRESULT CSearchBar::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
247 {
248 INT iWidth = LOWORD(lParam);
249 INT iPadding = 10;
250
251 ((CWindow)GetDlgItem(IDC_SEARCH_LABEL)).SetWindowPos(NULL, 0, 0, iWidth - iPadding, 40, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
252
253 HWND inputs[] = { GetDlgItem(IDC_SEARCH_FILENAME), GetDlgItem(IDC_SEARCH_QUERY), GetDlgItem(IDC_SEARCH_COMBOBOX), GetDlgItem(IDC_SEARCH_BUTTON), GetDlgItem(IDC_SEARCH_STOP_BUTTON), GetDlgItem(IDC_PROGRESS_BAR) };
254 for (SIZE_T i = 0; i < _countof(inputs); i++)
255 {
256 CWindow wFileName = (CWindow) inputs[i];
257 RECT rect;
258 wFileName.GetWindowRect(&rect);
259 POINT pt = { rect.left, rect.top };
260 ScreenToClient(&pt);
261 wFileName.MoveWindow(iPadding, pt.y, iWidth - iPadding * 2, rect.bottom - rect.top);
262 }
263
264 return 0;
265 }
266
267
268 // *** IOleWindow methods ***
269 HRESULT STDMETHODCALLTYPE CSearchBar::GetWindow(HWND *lphwnd)
270 {
271 if (!lphwnd)
272 return E_INVALIDARG;
273 *lphwnd = m_hWnd;
274 return S_OK;
275 }
276
277 HRESULT STDMETHODCALLTYPE CSearchBar::ContextSensitiveHelp(BOOL fEnterMode)
278 {
279 UNIMPLEMENTED;
280 return E_NOTIMPL;
281 }
282
283
284 // *** IDockingWindow methods ***
285 HRESULT STDMETHODCALLTYPE CSearchBar::CloseDW(DWORD dwReserved)
286 {
287 // We do nothing, we don't have anything to save yet
288 TRACE("CloseDW called\n");
289 return S_OK;
290 }
291
292 HRESULT STDMETHODCALLTYPE CSearchBar::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
293 {
294 /* Must return E_NOTIMPL according to MSDN */
295 return E_NOTIMPL;
296 }
297
298 HRESULT STDMETHODCALLTYPE CSearchBar::ShowDW(BOOL fShow)
299 {
300 fVisible = fShow;
301 ShowWindow(fShow);
302 return S_OK;
303 }
304
305
306 // *** IDeskBand methods ***
307 HRESULT STDMETHODCALLTYPE CSearchBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
308 {
309 if (!pdbi)
310 {
311 return E_INVALIDARG;
312 }
313
314 if (pdbi->dwMask & DBIM_MINSIZE)
315 {
316 pdbi->ptMinSize.x = 200;
317 pdbi->ptMinSize.y = 30;
318 }
319
320 if (pdbi->dwMask & DBIM_MAXSIZE)
321 {
322 pdbi->ptMaxSize.y = -1;
323 }
324
325 if (pdbi->dwMask & DBIM_INTEGRAL)
326 {
327 pdbi->ptIntegral.y = 1;
328 }
329
330 if (pdbi->dwMask & DBIM_ACTUAL)
331 {
332 pdbi->ptActual.x = 200;
333 pdbi->ptActual.y = 30;
334 }
335
336 if (pdbi->dwMask & DBIM_TITLE)
337 {
338 if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_SEARCHLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
339 return HRESULT_FROM_WIN32(GetLastError());
340 }
341
342 if (pdbi->dwMask & DBIM_MODEFLAGS)
343 {
344 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
345 }
346
347 if (pdbi->dwMask & DBIM_BKCOLOR)
348 {
349 pdbi->dwMask &= ~DBIM_BKCOLOR;
350 }
351 return S_OK;
352 }
353
354
355 // *** IObjectWithSite methods ***
356 HRESULT STDMETHODCALLTYPE CSearchBar::SetSite(IUnknown *pUnkSite)
357 {
358 HRESULT hr;
359 HWND parentWnd;
360
361 if (pUnkSite == pSite)
362 return S_OK;
363
364 TRACE("SetSite called \n");
365 if (!pUnkSite)
366 {
367 DestroyWindow();
368 m_hWnd = NULL;
369 }
370
371 if (pUnkSite != pSite)
372 {
373 pSite = NULL;
374 }
375
376 if(!pUnkSite)
377 return S_OK;
378
379 hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
380 if (!SUCCEEDED(hr))
381 {
382 ERR("Could not get parent's window ! Status: %08lx\n", hr);
383 return E_INVALIDARG;
384 }
385
386 pSite = pUnkSite;
387
388 if (m_hWnd)
389 {
390 // Change its parent
391 SetParent(parentWnd);
392 }
393 else
394 {
395 CDialogImpl::Create(parentWnd);
396
397 }
398 return S_OK;
399 }
400
401 HRESULT STDMETHODCALLTYPE CSearchBar::GetSite(REFIID riid, void **ppvSite)
402 {
403 if (!ppvSite)
404 return E_POINTER;
405 *ppvSite = pSite;
406 return S_OK;
407 }
408
409
410 // *** IOleCommandTarget methods ***
411 HRESULT STDMETHODCALLTYPE CSearchBar::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
412 {
413 UNIMPLEMENTED;
414 return E_NOTIMPL;
415 }
416
417 HRESULT STDMETHODCALLTYPE CSearchBar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
418 {
419 UNIMPLEMENTED;
420 return E_NOTIMPL;
421 }
422
423
424 // *** IServiceProvider methods ***
425 HRESULT STDMETHODCALLTYPE CSearchBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
426 {
427 /* FIXME: we probably want to handle more services here */
428 return IUnknown_QueryService(pSite, SID_SShellBrowser, riid, ppvObject);
429 }
430
431
432 // *** IInputObject methods ***
433 HRESULT STDMETHODCALLTYPE CSearchBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
434 {
435 if (fActivate)
436 {
437 //SetFocus();
438 SetActiveWindow();
439 }
440 // TODO: handle message
441 if(lpMsg)
442 {
443 TranslateMessage(lpMsg);
444 DispatchMessage(lpMsg);
445 }
446 return S_OK;
447 }
448
449 HRESULT STDMETHODCALLTYPE CSearchBar::HasFocusIO()
450 {
451 return S_OK;
452 }
453
454 HRESULT STDMETHODCALLTYPE CSearchBar::TranslateAcceleratorIO(LPMSG lpMsg)
455 {
456 if (IsDialogMessage(lpMsg))
457 return S_OK;
458
459 if ((lpMsg->hwnd == m_hWnd || IsChild(lpMsg->hwnd)))
460 {
461 TranslateMessage(lpMsg);
462 DispatchMessage(lpMsg);
463 return S_OK;
464 }
465
466 return S_FALSE;
467 }
468
469 // *** IPersist methods ***
470 HRESULT STDMETHODCALLTYPE CSearchBar::GetClassID(CLSID *pClassID)
471 {
472 if (!pClassID)
473 return E_POINTER;
474 *pClassID = CLSID_FileSearchBand;
475 return S_OK;
476 }
477
478
479 // *** IPersistStream methods ***
480 HRESULT STDMETHODCALLTYPE CSearchBar::IsDirty()
481 {
482 UNIMPLEMENTED;
483 return E_NOTIMPL;
484 }
485
486 HRESULT STDMETHODCALLTYPE CSearchBar::Load(IStream *pStm)
487 {
488 UNIMPLEMENTED;
489 return E_NOTIMPL;
490 }
491
492 HRESULT STDMETHODCALLTYPE CSearchBar::Save(IStream *pStm, BOOL fClearDirty)
493 {
494 UNIMPLEMENTED;
495 return E_NOTIMPL;
496 }
497
498 HRESULT STDMETHODCALLTYPE CSearchBar::GetSizeMax(ULARGE_INTEGER *pcbSize)
499 {
500 // TODO: calculate max size
501 UNIMPLEMENTED;
502 return E_NOTIMPL;
503 }
504
505
506 // *** IWinEventHandler methods ***
507 HRESULT STDMETHODCALLTYPE CSearchBar::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
508 {
509 return S_OK;
510 }
511
512 HRESULT STDMETHODCALLTYPE CSearchBar::IsWindowOwner(HWND hWnd)
513 {
514 return (hWnd == m_hWnd) ? S_OK : S_FALSE;
515 }
516
517 // *** IBandNavigate methods ***
518 HRESULT STDMETHODCALLTYPE CSearchBar::Select(long paramC)
519 {
520 UNIMPLEMENTED;
521 return E_NOTIMPL;
522 }
523
524 // *** INamespaceProxy ***
525 HRESULT STDMETHODCALLTYPE CSearchBar::GetNavigateTarget(long paramC, long param10, long param14)
526 {
527 UNIMPLEMENTED;
528 return E_NOTIMPL;
529 }
530
531 HRESULT STDMETHODCALLTYPE CSearchBar::Invoke(long paramC)
532 {
533 UNIMPLEMENTED;
534 return E_NOTIMPL;
535 }
536
537 HRESULT STDMETHODCALLTYPE CSearchBar::OnSelectionChanged(long paramC)
538 {
539 UNIMPLEMENTED;
540 return E_NOTIMPL;
541 }
542
543 HRESULT STDMETHODCALLTYPE CSearchBar::RefreshFlags(long paramC, long param10, long param14)
544 {
545 UNIMPLEMENTED;
546 return E_NOTIMPL;
547 }
548
549 HRESULT STDMETHODCALLTYPE CSearchBar::CacheItem(long paramC)
550 {
551 UNIMPLEMENTED;
552 return E_NOTIMPL;
553 }
554
555 // *** IDispatch methods ***
556 HRESULT STDMETHODCALLTYPE CSearchBar::GetTypeInfoCount(UINT *pctinfo)
557 {
558 UNIMPLEMENTED;
559 return E_NOTIMPL;
560 }
561
562 HRESULT STDMETHODCALLTYPE CSearchBar::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
563 {
564 UNIMPLEMENTED;
565 return E_NOTIMPL;
566 }
567
568 HRESULT STDMETHODCALLTYPE CSearchBar::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
569 {
570 UNIMPLEMENTED;
571 return E_NOTIMPL;
572 }
573
574 void CSearchBar::SetSearchInProgress(BOOL bInProgress)
575 {
576 ::ShowWindow(GetDlgItem(IDC_SEARCH_BUTTON), bInProgress ? SW_HIDE : SW_SHOW);
577 ::ShowWindow(GetDlgItem(IDC_SEARCH_STOP_BUTTON), bInProgress ? SW_SHOW : SW_HIDE);
578 HWND hProgressBar = GetDlgItem(IDC_PROGRESS_BAR);
579 ::ShowWindow(hProgressBar, bInProgress ? SW_SHOW : SW_HIDE);
580 ::PostMessage(hProgressBar, PBM_SETMARQUEE, bInProgress, 0);
581 }
582
583 HRESULT STDMETHODCALLTYPE CSearchBar::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
584 {
585 if (dispIdMember == DISPID_SEARCHCOMPLETE || dispIdMember == DISPID_SEARCHABORT)
586 {
587 SetSearchInProgress(FALSE);
588 return S_OK;
589 }
590 return E_INVALIDARG;
591 }