ad5abffe904f8b51c0d6820dc6f4d857a08e1974
[reactos.git] / base / shell / rshell / CMenuSite.cpp
1 /*
2 * Shell Menu Site
3 *
4 * Copyright 2014 David Quintana
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "precomp.h"
22 #include <atlwin.h>
23 #include <shlwapi_undoc.h>
24
25 WINE_DEFAULT_DEBUG_CHANNEL(menusite);
26
27 bool _assert(bool cond, LPCSTR expr, LPCSTR file, DWORD line, LPCSTR func)
28 {
29 #if DBG
30 if (!cond)
31 {
32 wine_dbg_printf("%s(%d): Assertion failed '%s', at %s", file, line, expr, func);
33 DebugBreak();
34 }
35 #endif
36 return cond;
37 }
38 #define DBGASSERT(x) _assert(!!(x), #x, __FILE__, __LINE__, __FUNCSIG__)
39
40 class CMenuSite :
41 public CComObjectRootEx<CComMultiThreadModelNoCS>,
42 public CWindowImpl<CMenuSite, CWindow, CControlWinTraits>,
43 public IBandSite,
44 public IDeskBarClient,
45 public IOleCommandTarget,
46 public IInputObject,
47 public IInputObjectSite,
48 public IWinEventHandler,
49 public IServiceProvider
50 {
51 CComPtr<IUnknown> m_DeskBarSite;
52 CComPtr<IUnknown> m_BandObject;
53 CComPtr<IDeskBand> m_DeskBand;
54 CComPtr<IWinEventHandler> m_WinEventHandler;
55 HWND m_hWndBand;
56
57 public:
58 CMenuSite();
59 ~CMenuSite() {}
60
61 DECLARE_WND_CLASS_EX(_T("MenuSite"), 0, COLOR_MENU)
62
63 DECLARE_NOT_AGGREGATABLE(CMenuSite)
64 DECLARE_PROTECT_FINAL_CONSTRUCT()
65 BEGIN_COM_MAP(CMenuSite)
66 COM_INTERFACE_ENTRY_IID(IID_IBandSite, IBandSite)
67 COM_INTERFACE_ENTRY_IID(IID_IDeskBarClient, IDeskBarClient)
68 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
69 COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget)
70 COM_INTERFACE_ENTRY_IID(IID_IInputObject, IInputObject)
71 COM_INTERFACE_ENTRY_IID(IID_IInputObjectSite, IInputObjectSite)
72 COM_INTERFACE_ENTRY_IID(IID_IWinEventHandler, IWinEventHandler)
73 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
74 END_COM_MAP()
75
76 // IBandSite
77 virtual HRESULT STDMETHODCALLTYPE AddBand(IUnknown * punk);
78 virtual HRESULT STDMETHODCALLTYPE EnumBands(UINT uBand, DWORD* pdwBandID);
79 virtual HRESULT STDMETHODCALLTYPE QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName);
80 virtual HRESULT STDMETHODCALLTYPE GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv);
81
82 // IDeskBarClient
83 virtual HRESULT STDMETHODCALLTYPE SetDeskBarSite(IUnknown *punkSite);
84 virtual HRESULT STDMETHODCALLTYPE GetSize(DWORD dwWhich, LPRECT prc);
85 virtual HRESULT STDMETHODCALLTYPE UIActivateDBC(DWORD dwState);
86
87 // IOleWindow
88 virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
89
90 // IOleCommandTarget
91 virtual HRESULT STDMETHODCALLTYPE QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText);
92 virtual HRESULT STDMETHODCALLTYPE Exec(const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut);
93
94 // IInputObject
95 virtual HRESULT STDMETHODCALLTYPE UIActivateIO(BOOL fActivate, LPMSG lpMsg);
96 virtual HRESULT STDMETHODCALLTYPE HasFocusIO();
97 virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorIO(LPMSG lpMsg);
98
99 // IInputObjectSite
100 virtual HRESULT STDMETHODCALLTYPE OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus);
101
102 // IWinEventHandler
103 virtual HRESULT STDMETHODCALLTYPE IsWindowOwner(HWND hWnd);
104 virtual HRESULT STDMETHODCALLTYPE OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult);
105
106 // IServiceProvider
107 virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject);
108
109
110 // Using custom message map instead
111 virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD mapId = 0);
112
113 // UNIMPLEMENTED
114 virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
115 virtual HRESULT STDMETHODCALLTYPE GetBandSiteInfo(BANDSITEINFO *pbsinfo);
116 virtual HRESULT STDMETHODCALLTYPE RemoveBand(DWORD dwBandID);
117 virtual HRESULT STDMETHODCALLTYPE SetBandSiteInfo(const BANDSITEINFO *pbsinfo);
118 virtual HRESULT STDMETHODCALLTYPE SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState);
119 virtual HRESULT STDMETHODCALLTYPE SetModeDBC(DWORD dwMode);
120
121 private:
122 IUnknown * ToIUnknown() { return static_cast<IDeskBarClient*>(this); }
123 };
124
125 extern "C"
126 HRESULT CMenuSite_Constructor(REFIID riid, LPVOID *ppv)
127 {
128 *ppv = NULL;
129
130 CMenuSite * site = new CComObject<CMenuSite>();
131
132 if (!site)
133 return E_OUTOFMEMORY;
134
135 HRESULT hr = site->QueryInterface(riid, ppv);
136
137 if (FAILED(hr))
138 site->Release();
139
140 return hr;
141 }
142
143 CMenuSite::CMenuSite() :
144 m_DeskBarSite(NULL),
145 m_BandObject(NULL),
146 m_DeskBand(NULL),
147 m_WinEventHandler(NULL),
148 m_hWndBand(NULL)
149 {
150 }
151
152 HRESULT STDMETHODCALLTYPE CMenuSite::ContextSensitiveHelp(BOOL fEnterMode)
153 {
154 return E_NOTIMPL;
155 }
156
157 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandSiteInfo(BANDSITEINFO *pbsinfo)
158 {
159 return E_NOTIMPL;
160 }
161
162 HRESULT STDMETHODCALLTYPE CMenuSite::RemoveBand(DWORD dwBandID)
163 {
164 return E_NOTIMPL;
165 }
166
167 HRESULT STDMETHODCALLTYPE CMenuSite::SetBandSiteInfo(const BANDSITEINFO *pbsinfo)
168 {
169 return E_NOTIMPL;
170 }
171
172 HRESULT STDMETHODCALLTYPE CMenuSite::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState)
173 {
174 return E_NOTIMPL;
175 }
176
177 HRESULT STDMETHODCALLTYPE CMenuSite::SetModeDBC(DWORD dwMode)
178 {
179 return E_NOTIMPL;
180 }
181
182 HRESULT STDMETHODCALLTYPE CMenuSite::TranslateAcceleratorIO(LPMSG lpMsg)
183 {
184 return S_FALSE;
185 }
186
187 HRESULT STDMETHODCALLTYPE CMenuSite::HasFocusIO()
188 {
189 return S_FALSE;
190 }
191
192 HRESULT STDMETHODCALLTYPE CMenuSite::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
193 {
194 return S_OK;
195 }
196
197 HRESULT STDMETHODCALLTYPE CMenuSite::AddBand(IUnknown * punk)
198 {
199 if (SHIsSameObject(punk, m_BandObject))
200 return S_OK + 0;
201
202 IUnknown_SetSite(m_BandObject, NULL);
203
204 BOOL result = m_hWndBand != NULL;
205
206 m_BandObject = NULL;
207 m_DeskBand = NULL;
208 m_WinEventHandler = NULL;
209 m_hWndBand = NULL;
210
211 if (!punk)
212 return result ? S_OK + 0 : E_FAIL;
213
214 DBGASSERT(SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IDeskBand, &m_DeskBand))));
215 DBGASSERT(SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IWinEventHandler, &m_WinEventHandler))));
216
217 IUnknown_SetSite(punk, this->ToIUnknown());
218 IUnknown_GetWindow(punk, &m_hWndBand);
219
220 m_BandObject = punk;
221
222 punk->AddRef();
223
224 return S_OK + 0;
225 }
226
227 HRESULT STDMETHODCALLTYPE CMenuSite::EnumBands(UINT uBand, DWORD* pdwBandID)
228 {
229 if (uBand != 0)
230 return E_FAIL;
231
232 *pdwBandID = 0;
233
234 return S_OK;
235 }
236
237 HRESULT STDMETHODCALLTYPE CMenuSite::Exec(const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
238 {
239 return IUnknown_Exec(m_DeskBarSite, *pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
240 }
241
242 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)
243 {
244 if (!DBGASSERT(dwBandID == 0) || m_BandObject == NULL)
245 {
246 *ppv = NULL;
247 return E_NOINTERFACE;
248 }
249
250 return m_BandObject->QueryInterface(riid, ppv);
251 }
252
253 HRESULT STDMETHODCALLTYPE CMenuSite::GetSize(DWORD dwWhich, LPRECT prc)
254 {
255 memset(prc, 0, sizeof(*prc));
256
257 if (dwWhich != 0)
258 return S_OK;
259
260 if (m_DeskBand == NULL)
261 return S_OK;
262
263 DESKBANDINFO info = { 0 };
264 info.dwMask = DBIM_MAXSIZE;
265
266 m_DeskBand->GetBandInfo(0, 0, &info);
267
268 prc->right = info.ptMaxSize.x;
269 prc->bottom = info.ptMaxSize.y;
270
271 return S_OK;
272 }
273
274 HRESULT STDMETHODCALLTYPE CMenuSite::GetWindow(HWND *phwnd)
275 {
276 DBGASSERT(IsWindow());
277
278 *phwnd = m_hWnd;
279
280 return S_OK;
281 }
282
283 HRESULT STDMETHODCALLTYPE CMenuSite::IsWindowOwner(HWND hWnd)
284 {
285 if (hWnd == m_hWnd)
286 return S_OK;
287
288 if (!m_WinEventHandler)
289 return S_FALSE;
290
291 return m_WinEventHandler->IsWindowOwner(hWnd);
292 }
293
294 HRESULT STDMETHODCALLTYPE CMenuSite::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
295 {
296 if (!m_WinEventHandler)
297 return S_OK;
298
299 return m_WinEventHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
300 }
301
302 HRESULT STDMETHODCALLTYPE CMenuSite::QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName)
303 {
304 DBGASSERT(dwBandID == 0);
305 DBGASSERT(!IsBadWritePtr(ppstb, sizeof(*ppstb)));
306
307 if (!m_BandObject)
308 {
309 *ppstb = NULL;
310 return E_NOINTERFACE;
311 }
312
313 HRESULT hr = m_BandObject->QueryInterface(IID_PPV_ARG(IDeskBand, ppstb));
314
315 *pdwState = 1;
316
317 if (cchName > 0)
318 pszName[0] = 0;
319
320 return hr;
321 }
322
323 HRESULT STDMETHODCALLTYPE CMenuSite::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
324 {
325 *ppvObject = NULL;
326
327 if (IsEqualGUID(guidService, SID_SMenuBandBottom) ||
328 IsEqualGUID(guidService, SID_SMenuBandBottomSelected) ||
329 IsEqualGUID(guidService, SID_SMenuBandChild))
330 {
331 if (m_BandObject == NULL)
332 return E_FAIL;
333
334 return IUnknown_QueryService(m_BandObject, guidService, riid, ppvObject);
335 }
336
337 DBGASSERT(m_DeskBarSite);
338
339 return IUnknown_QueryService(m_DeskBarSite, guidService, riid, ppvObject);
340 }
341
342 HRESULT STDMETHODCALLTYPE CMenuSite::QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
343 {
344 if (!DBGASSERT(m_DeskBarSite))
345 return E_FAIL;
346 return IUnknown_QueryStatus(m_DeskBarSite, *pguidCmdGroup, cCmds, prgCmds, pCmdText);
347 }
348
349 HRESULT STDMETHODCALLTYPE CMenuSite::SetDeskBarSite(IUnknown *punkSite)
350 {
351 HRESULT hr;
352
353 CComPtr<IUnknown> protectThis(this->ToIUnknown());
354
355 if (punkSite)
356 {
357 HWND hWndSite;
358
359 m_DeskBarSite = NULL;
360
361 hr = IUnknown_GetWindow(punkSite, &hWndSite);
362
363 if (FAILED(hr) || !hWndSite)
364 return E_FAIL;
365
366 if (!m_hWnd)
367 {
368 Create(hWndSite, NULL, L"MenuSite");
369 }
370
371 m_DeskBarSite = punkSite;
372
373 return S_OK;
374 }
375
376 if (m_DeskBand)
377 {
378 m_DeskBand->CloseDW(0);
379 }
380
381 IUnknown_SetSite(m_BandObject, NULL);
382
383 m_BandObject = NULL;
384 m_DeskBand = NULL;
385 m_WinEventHandler = NULL;
386 m_hWndBand = NULL;
387 m_hWnd = NULL;
388 m_DeskBarSite = NULL;
389
390 return S_OK;
391 }
392
393 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateDBC(DWORD dwState)
394 {
395 if (!m_DeskBand)
396 return S_OK;
397
398 return m_DeskBand->ShowDW(dwState != 0);
399 }
400
401 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
402 {
403 if (lpMsg)
404 return E_FAIL;
405
406 return IUnknown_UIActivateIO(m_BandObject, fActivate, lpMsg);
407 }
408
409 BOOL CMenuSite::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD mapId)
410 {
411 HWND hWndTarget = NULL;
412 CComPtr<IUnknown> protectThis(this->ToIUnknown());
413
414 switch (uMsg)
415 {
416 case WM_SIZE:
417 if (m_BandObject)
418 {
419 CComPtr<IMenuPopup> pMenuPopup;
420 if (SUCCEEDED(m_BandObject->QueryInterface(IID_PPV_ARG(IMenuPopup, &pMenuPopup))))
421 {
422 RECT Rect = { 0 };
423 GetClientRect(&Rect);
424 Rect.right = Rect.right;
425 pMenuPopup->OnPosRectChangeDB(&Rect);
426 }
427 }
428 hWndTarget = hWnd;
429 lResult = 1;
430 break;
431 case WM_NOTIFY:
432 hWndTarget = reinterpret_cast<LPNMHDR>(lParam)->hwndFrom;
433 break;
434 case WM_COMMAND:
435 hWndTarget = (HWND) lParam;
436 break;
437 default:
438 return FALSE;
439 }
440
441 if (hWndTarget && m_WinEventHandler &&
442 m_WinEventHandler->IsWindowOwner(hWndTarget) == S_OK)
443 {
444 if (SUCCEEDED(m_WinEventHandler->OnWinEvent(hWndTarget, uMsg, wParam, lParam, &lResult)))
445 return TRUE;
446 }
447
448 return FALSE;
449 }