2 * PROJECT: ReactOS Search Shell Extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
5 * COPYRIGHT: Copyright 2019 Brock Mammen
8 #include "CSearchBar.h"
9 #include <psdk/wingdi.h>
10 #include <commoncontrols.h>
11 #include <../browseui.h>
15 WINE_DEFAULT_DEBUG_CHANNEL(shellfind
);
20 #define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__)
23 CSearchBar::CSearchBar() :
29 CSearchBar::~CSearchBar()
33 LRESULT
CSearchBar::OnInitDialog(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
35 SetSearchInProgress(FALSE
);
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
));
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
))
49 hResult
= m_AddressEditBox
->Init(hCombobox
, hEditControl
, 0, m_pSite
);
50 if (FAILED_UNEXPECTEDLY(hResult
))
53 // Subscribe to navigation events
54 CComPtr
<IShellBrowser
> pShellBrowser
;
55 hResult
= IUnknown_QueryService(m_pSite
, SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &pShellBrowser
));
57 if (SUCCEEDED(hResult
))
58 AtlAdvise(pShellBrowser
, static_cast<IDispatch
*>(this), DIID_DWebBrowserEvents
, &dwAdviseCookie
);
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
, ¶ms
, NULL
, NULL
, NULL
);
67 HRESULT
CSearchBar::ExecuteCommand(CComPtr
<IContextMenu
>& menu
, UINT nCmd
)
69 CComPtr
<IOleWindow
> pBrowserOleWnd
;
70 CMINVOKECOMMANDINFO cmi
;
74 hr
= IUnknown_QueryService(m_pSite
, SID_SShellBrowser
, IID_PPV_ARG(IOleWindow
, &pBrowserOleWnd
));
75 if (FAILED_UNEXPECTEDLY(hr
))
78 hr
= pBrowserOleWnd
->GetWindow(&browserWnd
);
79 if (FAILED_UNEXPECTEDLY(hr
))
82 ZeroMemory(&cmi
, sizeof(cmi
));
83 cmi
.cbSize
= sizeof(cmi
);
84 cmi
.lpVerb
= MAKEINTRESOURCEA(nCmd
);
85 cmi
.hwnd
= browserWnd
;
86 if (GetKeyState(VK_SHIFT
) & 0x8000)
87 cmi
.fMask
|= CMIC_MASK_SHIFT_DOWN
;
88 if (GetKeyState(VK_CONTROL
) & 0x8000)
89 cmi
.fMask
|= CMIC_MASK_CONTROL_DOWN
;
91 return menu
->InvokeCommand(&cmi
);
95 // *** ATL event handlers ***
96 LRESULT
CSearchBar::OnSetFocus(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
98 IUnknown_OnFocusChangeIS(m_pSite
, static_cast<IDeskBand
*>(this), TRUE
);
103 HRESULT
CSearchBar::GetSearchResultsFolder(IShellBrowser
**ppShellBrowser
, HWND
*pHwnd
, IShellFolder
**ppShellFolder
)
106 CComPtr
<IShellBrowser
> pShellBrowser
;
108 ppShellBrowser
= &pShellBrowser
;
109 if (!*ppShellBrowser
)
111 hr
= IUnknown_QueryService(m_pSite
, SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, ppShellBrowser
));
112 if (FAILED_UNEXPECTEDLY(hr
))
116 CComPtr
<IShellView
> pShellView
;
117 hr
= (*ppShellBrowser
)->QueryActiveShellView(&pShellView
);
118 if (FAILED(hr
) || !pShellView
)
121 CComPtr
<IFolderView
> pFolderView
;
122 hr
= pShellView
->QueryInterface(IID_PPV_ARG(IFolderView
, &pFolderView
));
123 if (FAILED(hr
) || !pFolderView
)
126 CComPtr
<IShellFolder
> pShellFolder
;
128 ppShellFolder
= &pShellFolder
;
129 hr
= pFolderView
->GetFolder(IID_PPV_ARG(IShellFolder
, ppShellFolder
));
130 if (FAILED(hr
) || !pShellFolder
)
134 hr
= IUnknown_GetClassID(*ppShellFolder
, &clsid
);
137 if (clsid
!= CLSID_FindFolder
)
142 hr
= pShellView
->GetWindow(pHwnd
);
143 if (FAILED_UNEXPECTEDLY(hr
))
150 LRESULT
CSearchBar::OnSearchButtonClicked(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
& bHandled
)
152 CComHeapPtr
<SearchStart
> pSearchStart(static_cast<SearchStart
*>(CoTaskMemAlloc(sizeof(SearchStart
))));
153 GetDlgItemText(IDC_SEARCH_FILENAME
, pSearchStart
->szFileName
, _countof(pSearchStart
->szFileName
));
154 GetDlgItemText(IDC_SEARCH_QUERY
, pSearchStart
->szQuery
, _countof(pSearchStart
->szQuery
));
155 if (!GetAddressEditBoxPath(pSearchStart
->szPath
))
157 ShellMessageBoxW(_AtlBaseModule
.GetResourceInstance(), m_hWnd
, MAKEINTRESOURCEW(IDS_SEARCHINVALID
), MAKEINTRESOURCEW(IDS_SEARCHLABEL
), MB_OK
| MB_ICONERROR
, pSearchStart
->szPath
);
161 CComPtr
<IShellBrowser
> pShellBrowser
;
162 HRESULT hr
= IUnknown_QueryService(m_pSite
, SID_SShellBrowser
, IID_PPV_ARG(IShellBrowser
, &pShellBrowser
));
163 if (FAILED_UNEXPECTEDLY(hr
))
167 if (FAILED(GetSearchResultsFolder(&pShellBrowser
, &hwnd
, NULL
)))
169 // Open a new search results folder
170 WCHAR szShellGuid
[MAX_PATH
];
171 const WCHAR shellGuidPrefix
[] = L
"shell:::";
172 memcpy(szShellGuid
, shellGuidPrefix
, sizeof(shellGuidPrefix
));
173 hr
= StringFromGUID2(CLSID_FindFolder
, szShellGuid
+ _countof(shellGuidPrefix
) - 1,
174 _countof(szShellGuid
) - _countof(shellGuidPrefix
));
175 if (FAILED_UNEXPECTEDLY(hr
))
178 CComHeapPtr
<ITEMIDLIST
> findFolderPidl
;
179 hr
= SHParseDisplayName(szShellGuid
, NULL
, &findFolderPidl
, 0, NULL
);
180 if (FAILED_UNEXPECTEDLY(hr
))
183 hr
= pShellBrowser
->BrowseObject(findFolderPidl
, 0);
184 if (FAILED_UNEXPECTEDLY(hr
))
187 hr
= GetSearchResultsFolder(&pShellBrowser
, &hwnd
, NULL
);
188 if (FAILED_UNEXPECTEDLY(hr
))
192 ::PostMessageW(hwnd
, WM_SEARCH_START
, 0, (LPARAM
) pSearchStart
.Detach());
194 SetSearchInProgress(TRUE
);
199 LRESULT
CSearchBar::OnStopButtonClicked(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
& bHandled
)
202 HRESULT hr
= GetSearchResultsFolder(NULL
, &hwnd
, NULL
);
204 ::PostMessageW(hwnd
, WM_SEARCH_STOP
, 0, 0);
209 BOOL
CSearchBar::GetAddressEditBoxPath(WCHAR (&szPath
)[MAX_PATH
])
211 HWND hComboboxEx
= GetDlgItem(IDC_SEARCH_COMBOBOX
);
212 ::GetWindowTextW(hComboboxEx
, szPath
, _countof(szPath
));
213 INT iSelectedIndex
= SendMessageW(hComboboxEx
, CB_GETCURSEL
, 0, 0);
214 if (iSelectedIndex
!= CB_ERR
)
216 WCHAR szItemText
[MAX_PATH
];
217 COMBOBOXEXITEMW item
= {0};
218 item
.mask
= CBEIF_LPARAM
| CBEIF_TEXT
;
219 item
.iItem
= iSelectedIndex
;
220 item
.pszText
= szItemText
;
221 item
.cchTextMax
= _countof(szItemText
);
222 SendMessageW(hComboboxEx
, CBEM_GETITEMW
, 0, (LPARAM
)&item
);
224 if (!wcscmp(szItemText
, szPath
) && SHGetPathFromIDListW((LPCITEMIDLIST
)item
.lParam
, szItemText
))
226 StringCbCopyW(szPath
, MAX_PATH
* sizeof(WCHAR
), szItemText
);
231 DWORD dwAttributes
= GetFileAttributesW(szPath
);
232 return dwAttributes
!= INVALID_FILE_ATTRIBUTES
233 && (dwAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
236 LRESULT
CSearchBar::OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
238 INT iWidth
= LOWORD(lParam
);
241 ((CWindow
)GetDlgItem(IDC_SEARCH_LABEL
)).SetWindowPos(NULL
, 0, 0, iWidth
- iPadding
, 40, SWP_NOACTIVATE
| SWP_NOCOPYBITS
| SWP_NOMOVE
| SWP_NOOWNERZORDER
| SWP_NOZORDER
);
243 int inputs
[] = { IDC_SEARCH_FILENAME
, IDC_SEARCH_QUERY
, IDC_SEARCH_COMBOBOX
, IDC_SEARCH_BUTTON
, IDC_SEARCH_STOP_BUTTON
, IDC_PROGRESS_BAR
};
244 HDWP hdwp
= BeginDeferWindowPos(_countof(inputs
));
245 for (SIZE_T i
= 0; i
< _countof(inputs
); i
++)
247 CWindow wnd
= (CWindow
) GetDlgItem(inputs
[i
]);
249 wnd
.GetWindowRect(&rect
);
250 POINT pt
= { rect
.left
, rect
.top
};
252 hdwp
= wnd
.DeferWindowPos(hdwp
,
256 iWidth
- iPadding
* 2,
257 rect
.bottom
- rect
.top
,
258 SWP_NOZORDER
| SWP_NOACTIVATE
);
260 EndDeferWindowPos(hdwp
);
266 // *** IOleWindow methods ***
267 HRESULT STDMETHODCALLTYPE
CSearchBar::GetWindow(HWND
*lphwnd
)
275 HRESULT STDMETHODCALLTYPE
CSearchBar::ContextSensitiveHelp(BOOL fEnterMode
)
282 // *** IDockingWindow methods ***
283 HRESULT STDMETHODCALLTYPE
CSearchBar::CloseDW(DWORD dwReserved
)
285 // We do nothing, we don't have anything to save yet
286 TRACE("CloseDW called\n");
290 HRESULT STDMETHODCALLTYPE
CSearchBar::ResizeBorderDW(const RECT
*prcBorder
, IUnknown
*punkToolbarSite
, BOOL fReserved
)
292 /* Must return E_NOTIMPL according to MSDN */
296 HRESULT STDMETHODCALLTYPE
CSearchBar::ShowDW(BOOL fShow
)
304 // *** IDeskBand methods ***
305 HRESULT STDMETHODCALLTYPE
CSearchBar::GetBandInfo(DWORD dwBandID
, DWORD dwViewMode
, DESKBANDINFO
*pdbi
)
312 if (pdbi
->dwMask
& DBIM_MINSIZE
)
314 pdbi
->ptMinSize
.x
= 200;
315 pdbi
->ptMinSize
.y
= 30;
318 if (pdbi
->dwMask
& DBIM_MAXSIZE
)
320 pdbi
->ptMaxSize
.y
= -1;
323 if (pdbi
->dwMask
& DBIM_INTEGRAL
)
325 pdbi
->ptIntegral
.y
= 1;
328 if (pdbi
->dwMask
& DBIM_ACTUAL
)
330 pdbi
->ptActual
.x
= 200;
331 pdbi
->ptActual
.y
= 30;
334 if (pdbi
->dwMask
& DBIM_TITLE
)
336 if (!LoadStringW(_AtlBaseModule
.GetResourceInstance(), IDS_SEARCHLABEL
, pdbi
->wszTitle
, _countof(pdbi
->wszTitle
)))
337 return HRESULT_FROM_WIN32(GetLastError());
340 if (pdbi
->dwMask
& DBIM_MODEFLAGS
)
342 pdbi
->dwModeFlags
= DBIMF_NORMAL
| DBIMF_VARIABLEHEIGHT
;
345 if (pdbi
->dwMask
& DBIM_BKCOLOR
)
347 pdbi
->dwMask
&= ~DBIM_BKCOLOR
;
353 // *** IObjectWithSite methods ***
354 HRESULT STDMETHODCALLTYPE
CSearchBar::SetSite(IUnknown
*pUnkSite
)
359 if (pUnkSite
== m_pSite
)
362 TRACE("SetSite called \n");
369 if (pUnkSite
!= m_pSite
)
377 hr
= IUnknown_GetWindow(pUnkSite
, &parentWnd
);
380 ERR("Could not get parent's window ! Status: %08lx\n", hr
);
389 SetParent(parentWnd
);
393 CDialogImpl::Create(parentWnd
);
399 HRESULT STDMETHODCALLTYPE
CSearchBar::GetSite(REFIID riid
, void **ppvSite
)
408 // *** IOleCommandTarget methods ***
409 HRESULT STDMETHODCALLTYPE
CSearchBar::QueryStatus(const GUID
*pguidCmdGroup
, ULONG cCmds
, OLECMD prgCmds
[], OLECMDTEXT
*pCmdText
)
415 HRESULT STDMETHODCALLTYPE
CSearchBar::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
, DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
422 // *** IServiceProvider methods ***
423 HRESULT STDMETHODCALLTYPE
CSearchBar::QueryService(REFGUID guidService
, REFIID riid
, void **ppvObject
)
425 /* FIXME: we probably want to handle more services here */
426 return IUnknown_QueryService(m_pSite
, SID_SShellBrowser
, riid
, ppvObject
);
430 // *** IInputObject methods ***
431 HRESULT STDMETHODCALLTYPE
CSearchBar::UIActivateIO(BOOL fActivate
, LPMSG lpMsg
)
438 // TODO: handle message
441 TranslateMessage(lpMsg
);
442 DispatchMessage(lpMsg
);
447 HRESULT STDMETHODCALLTYPE
CSearchBar::HasFocusIO()
452 HRESULT STDMETHODCALLTYPE
CSearchBar::TranslateAcceleratorIO(LPMSG lpMsg
)
454 if (IsDialogMessage(lpMsg
))
457 if ((lpMsg
->hwnd
== m_hWnd
|| IsChild(lpMsg
->hwnd
)))
459 TranslateMessage(lpMsg
);
460 DispatchMessage(lpMsg
);
467 // *** IPersist methods ***
468 HRESULT STDMETHODCALLTYPE
CSearchBar::GetClassID(CLSID
*pClassID
)
472 *pClassID
= CLSID_FileSearchBand
;
477 // *** IPersistStream methods ***
478 HRESULT STDMETHODCALLTYPE
CSearchBar::IsDirty()
484 HRESULT STDMETHODCALLTYPE
CSearchBar::Load(IStream
*pStm
)
490 HRESULT STDMETHODCALLTYPE
CSearchBar::Save(IStream
*pStm
, BOOL fClearDirty
)
496 HRESULT STDMETHODCALLTYPE
CSearchBar::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
498 // TODO: calculate max size
504 // *** IWinEventHandler methods ***
505 HRESULT STDMETHODCALLTYPE
CSearchBar::OnWinEvent(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*theResult
)
510 HRESULT STDMETHODCALLTYPE
CSearchBar::IsWindowOwner(HWND hWnd
)
512 return (hWnd
== m_hWnd
) ? S_OK
: S_FALSE
;
515 // *** IBandNavigate methods ***
516 HRESULT STDMETHODCALLTYPE
CSearchBar::Select(long paramC
)
522 // *** INamespaceProxy ***
523 HRESULT STDMETHODCALLTYPE
CSearchBar::GetNavigateTarget(long paramC
, long param10
, long param14
)
529 HRESULT STDMETHODCALLTYPE
CSearchBar::Invoke(long paramC
)
535 HRESULT STDMETHODCALLTYPE
CSearchBar::OnSelectionChanged(long paramC
)
541 HRESULT STDMETHODCALLTYPE
CSearchBar::RefreshFlags(long paramC
, long param10
, long param14
)
547 HRESULT STDMETHODCALLTYPE
CSearchBar::CacheItem(long paramC
)
553 // *** IDispatch methods ***
554 HRESULT STDMETHODCALLTYPE
CSearchBar::GetTypeInfoCount(UINT
*pctinfo
)
560 HRESULT STDMETHODCALLTYPE
CSearchBar::GetTypeInfo(UINT iTInfo
, LCID lcid
, ITypeInfo
**ppTInfo
)
566 HRESULT STDMETHODCALLTYPE
CSearchBar::GetIDsOfNames(REFIID riid
, LPOLESTR
*rgszNames
, UINT cNames
, LCID lcid
, DISPID
*rgDispId
)
572 void CSearchBar::SetSearchInProgress(BOOL bInProgress
)
574 ::ShowWindow(GetDlgItem(IDC_SEARCH_BUTTON
), bInProgress
? SW_HIDE
: SW_SHOW
);
575 ::ShowWindow(GetDlgItem(IDC_SEARCH_STOP_BUTTON
), bInProgress
? SW_SHOW
: SW_HIDE
);
576 HWND hProgressBar
= GetDlgItem(IDC_PROGRESS_BAR
);
577 ::ShowWindow(hProgressBar
, bInProgress
? SW_SHOW
: SW_HIDE
);
578 ::PostMessage(hProgressBar
, PBM_SETMARQUEE
, bInProgress
, 0);
581 HRESULT
CSearchBar::TrySubscribeToSearchEvents()
583 CComPtr
<IShellFolder
> pShellFolder
;
584 HRESULT hr
= GetSearchResultsFolder(NULL
, NULL
, &pShellFolder
);
589 hr
= AtlAdvise(pShellFolder
, static_cast<IDispatch
*>(this), DIID_DSearchCommandEvents
, &fAdviseCookie
);
590 if (FAILED_UNEXPECTEDLY(hr
))
596 HRESULT STDMETHODCALLTYPE
CSearchBar::Invoke(DISPID dispIdMember
, REFIID riid
, LCID lcid
, WORD wFlags
, DISPPARAMS
*pDispParams
, VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
598 switch (dispIdMember
)
600 case DISPID_NAVIGATECOMPLETE2
:
601 case DISPID_DOCUMENTCOMPLETE
:
603 TrySubscribeToSearchEvents();
605 // Remove the search results folder from the address box
606 CComPtr
<IDispatch
> pDispatch
;
607 HRESULT hResult
= m_AddressEditBox
->QueryInterface(IID_PPV_ARG(IDispatch
, &pDispatch
));
608 if (FAILED_UNEXPECTEDLY(hResult
))
610 pDispatch
->Invoke(dispIdMember
, riid
, lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
611 CComPtr
<IShellService
> pShellService
;
612 hResult
= m_AddressEditBox
->QueryInterface(IID_PPV_ARG(IShellService
, &pShellService
));
613 if (FAILED_UNEXPECTEDLY(hResult
))
615 hResult
= pShellService
->SetOwner(NULL
);
616 if (FAILED_UNEXPECTEDLY(hResult
))
618 HWND hComboboxEx
= GetDlgItem(IDC_SEARCH_COMBOBOX
);
619 int index
= SendMessageW(hComboboxEx
, CB_GETCOUNT
, 0, 0);
622 COMBOBOXEXITEMW item
= {0};
623 item
.mask
= CBEIF_LPARAM
;
624 item
.iItem
= index
- 1;
625 SendMessageW(hComboboxEx
, CBEM_GETITEMW
, 0, (LPARAM
)&item
);
628 CComPtr
<IShellFolder
> pDesktopFolder
;
629 hResult
= SHGetDesktopFolder(&pDesktopFolder
);
630 if (FAILED_UNEXPECTEDLY(hResult
))
632 CComPtr
<IShellFolder
> pShellFolder
;
633 hResult
= pDesktopFolder
->BindToObject((LPCITEMIDLIST
)item
.lParam
, NULL
, IID_PPV_ARG(IShellFolder
, &pShellFolder
));
637 hResult
= IUnknown_GetClassID(pShellFolder
, &clsid
);
638 if (SUCCEEDED(hResult
) && clsid
== CLSID_FindFolder
)
640 SendMessageW(hComboboxEx
, CBEM_DELETEITEM
, item
.iItem
, 0);
641 SendMessageW(hComboboxEx
, CB_SETCURSEL
, 0, 0);
645 case DISPID_SEARCHCOMPLETE
:
646 case DISPID_SEARCHABORT
:
647 SetSearchInProgress(FALSE
);