82d864c3c34dad2c77d80b1371382a7a0e3a4184
[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 IUnknown * m_DeskBarSite;
52 IUnknown * m_BandObject;
53 IDeskBand * m_DeskBand;
54 IWinEventHandler * m_WinEventHandler;
55 HWND m_hWndBand;
56
57 public:
58 CMenuSite();
59 ~CMenuSite() {}
60
61 DECLARE_WND_CLASS_EX(_T("MenuSite"), 0, COLOR_WINDOW)
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 BOOL CreateSiteWindow(HWND hWndParent);
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 if (m_BandObject)
205 {
206 m_BandObject->Release();
207 m_BandObject = NULL;
208 }
209
210 if (m_DeskBand)
211 {
212 m_DeskBand->Release();
213 m_DeskBand = NULL;
214 }
215
216 if (m_WinEventHandler)
217 {
218 m_WinEventHandler->Release();
219 m_WinEventHandler = NULL;
220 }
221
222 BOOL result = m_hWndBand != NULL;
223
224 m_hWndBand = NULL;
225
226 if (!punk)
227 return result ? S_OK + 0 : E_FAIL;
228
229 DBGASSERT(SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IDeskBand, &m_DeskBand))));
230 DBGASSERT(SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IWinEventHandler, &m_WinEventHandler))));
231
232 IUnknown_SetSite(punk, (IDeskBarClient*)this);
233 IUnknown_GetWindow(punk, &m_hWndBand);
234
235 m_BandObject = punk;
236
237 punk->AddRef();
238
239 return S_OK + 0;
240 }
241
242 HRESULT STDMETHODCALLTYPE CMenuSite::EnumBands(UINT uBand, DWORD* pdwBandID)
243 {
244 if (uBand != 0)
245 return E_FAIL;
246
247 *pdwBandID = 0;
248
249 return S_OK;
250 }
251
252 HRESULT STDMETHODCALLTYPE CMenuSite::Exec(const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
253 {
254 return IUnknown_Exec(m_DeskBarSite, *pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
255 }
256
257 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)
258 {
259 if (!DBGASSERT(dwBandID == 0) || m_BandObject == NULL)
260 {
261 *ppv = NULL;
262 return E_NOINTERFACE;
263 }
264
265 return m_BandObject->QueryInterface(riid, ppv);
266 }
267
268 HRESULT STDMETHODCALLTYPE CMenuSite::GetSize(DWORD dwWhich, LPRECT prc)
269 {
270 memset(prc, 0, sizeof(*prc));
271
272 if (dwWhich != 0)
273 return S_OK;
274
275 if (m_DeskBand == NULL)
276 return S_OK;
277
278 DESKBANDINFO info = { 0 };
279 info.dwMask = DBIM_MAXSIZE;
280
281 m_DeskBand->GetBandInfo(0, 0, &info);
282
283 prc->right = info.ptMaxSize.x;
284 prc->bottom = info.ptMaxSize.y;
285
286 return S_OK;
287 }
288
289 HRESULT STDMETHODCALLTYPE CMenuSite::GetWindow(HWND *phwnd)
290 {
291 DBGASSERT(IsWindow());
292
293 *phwnd = m_hWnd;
294
295 return S_OK;
296 }
297
298 HRESULT STDMETHODCALLTYPE CMenuSite::IsWindowOwner(HWND hWnd)
299 {
300 if (hWnd == m_hWnd)
301 return S_OK;
302
303 if (!m_WinEventHandler)
304 return S_FALSE;
305
306 return m_WinEventHandler->IsWindowOwner(hWnd);
307 }
308
309 HRESULT STDMETHODCALLTYPE CMenuSite::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
310 {
311 if (!m_WinEventHandler)
312 return S_OK;
313
314 return m_WinEventHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
315 }
316
317 HRESULT STDMETHODCALLTYPE CMenuSite::QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName)
318 {
319 DBGASSERT(dwBandID == 0);
320 DBGASSERT(!IsBadWritePtr(ppstb, sizeof(*ppstb)));
321
322 if (!m_BandObject)
323 {
324 *ppstb = NULL;
325 return E_NOINTERFACE;
326 }
327
328 HRESULT hr = m_BandObject->QueryInterface(IID_PPV_ARG(IDeskBand, ppstb));
329
330 *pdwState = 1;
331
332 if (cchName > 0)
333 pszName[0] = 0;
334
335 return hr;
336 }
337
338 HRESULT STDMETHODCALLTYPE CMenuSite::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
339 {
340 *ppvObject = NULL;
341
342 if (IsEqualGUID(guidService, SID_SMenuBandBottom) ||
343 IsEqualGUID(guidService, SID_SMenuBandBottomSelected) ||
344 IsEqualGUID(guidService, SID_SMenuBandChild))
345 {
346 if (m_BandObject == NULL)
347 return E_FAIL;
348
349 return IUnknown_QueryService(m_BandObject, guidService, riid, ppvObject);
350 }
351
352 DBGASSERT(m_DeskBarSite);
353
354 return IUnknown_QueryService(m_DeskBarSite, guidService, riid, ppvObject);
355 }
356
357 HRESULT STDMETHODCALLTYPE CMenuSite::QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
358 {
359 if (!DBGASSERT(m_DeskBarSite))
360 return E_FAIL;
361 return IUnknown_QueryStatus(m_DeskBarSite, *pguidCmdGroup, cCmds, prgCmds, pCmdText);
362 }
363
364 HRESULT STDMETHODCALLTYPE CMenuSite::SetDeskBarSite(IUnknown *punkSite)
365 {
366 HWND hWndSite;
367
368 ((IDeskBarClient*)this)->AddRef();
369
370 if (punkSite)
371 {
372 if (m_DeskBarSite)
373 {
374 m_DeskBarSite->Release();
375 m_DeskBarSite = NULL;
376 }
377
378 IUnknown_GetWindow(punkSite, &hWndSite);
379
380 if (hWndSite)
381 {
382 CreateSiteWindow(hWndSite);
383
384 m_DeskBarSite = punkSite;
385
386 punkSite->AddRef();
387 }
388 }
389 else
390 {
391 if (m_DeskBand)
392 {
393 m_DeskBand->CloseDW(0);
394 }
395
396 IUnknown_SetSite(m_BandObject, NULL);
397
398 if (m_BandObject)
399 {
400 m_BandObject->Release();
401 m_BandObject = NULL;
402 }
403
404 if (m_DeskBand)
405 {
406 m_DeskBand->Release();
407 m_DeskBand = NULL;
408 }
409
410 if (m_WinEventHandler)
411 {
412 m_WinEventHandler->Release();
413 m_WinEventHandler = NULL;
414 }
415
416 m_hWndBand = NULL;
417
418 if (m_hWnd)
419 {
420 DestroyWindow();
421 m_hWnd = NULL;
422 }
423
424 if (m_DeskBarSite)
425 m_DeskBarSite->Release();
426
427 m_DeskBarSite = NULL;
428 }
429
430 ((IDeskBarClient*)this)->Release();
431
432 if (!m_hWnd)
433 return E_FAIL;
434
435 return S_OK;
436 }
437
438 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateDBC(DWORD dwState)
439 {
440 if (!DBGASSERT(m_DeskBand))
441 return S_OK;
442
443 return m_DeskBand->ShowDW(dwState != 0);
444 }
445
446 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
447 {
448 if (lpMsg && DBGASSERT(IsBadWritePtr(lpMsg, sizeof(*lpMsg))))
449 return E_FAIL;
450
451 return IUnknown_UIActivateIO(m_BandObject, fActivate, lpMsg);
452 }
453
454 BOOL CMenuSite::CreateSiteWindow(HWND hWndParent)
455 {
456 if (m_hWnd)
457 {
458 return DBGASSERT(IsWindow());
459 }
460
461 Create(hWndParent, NULL, L"MenuSite");
462
463 return m_hWnd != NULL;
464 }
465
466 BOOL CMenuSite::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD mapId)
467 {
468 HWND hWndToCall;
469 IMenuPopup * pMenuPopup;
470
471 ((IDeskBarClient*)this)->AddRef();
472
473 switch (uMsg)
474 {
475 case WM_SIZE:
476 if (m_BandObject)
477 {
478 if (SUCCEEDED(m_BandObject->QueryInterface(IID_PPV_ARG(IMenuPopup, &pMenuPopup))))
479 {
480 RECT Rect = { 0 };
481 GetClientRect(&Rect);
482 pMenuPopup->OnPosRectChangeDB(&Rect);
483 pMenuPopup->Release();
484 }
485 }
486 hWndToCall = hWnd;
487 lResult = 1;
488 break;
489 case WM_NOTIFY:
490 hWndToCall = *(HWND *) lParam;
491 break;
492 case WM_COMMAND:
493 hWndToCall = (HWND) lParam;
494 break;
495 default:
496 ((IDeskBarClient*)this)->Release();
497 return FALSE;
498 }
499
500 if (hWndToCall)
501 {
502 if (m_WinEventHandler)
503 {
504 if (m_WinEventHandler->IsWindowOwner(hWndToCall) == S_OK)
505 {
506 HRESULT hr = m_WinEventHandler->OnWinEvent(hWndToCall, uMsg, wParam, lParam, &lResult);
507 ((IDeskBarClient*)this)->Release();
508 return hr == S_OK;
509 }
510 }
511 }
512
513 ((IDeskBarClient*)this)->Release();
514 return FALSE;
515 }