[RSHELL]
[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 virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorIO(LPMSG lpMsg);
77 virtual HRESULT STDMETHODCALLTYPE HasFocusIO();
78 virtual HRESULT STDMETHODCALLTYPE OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus);
79 virtual HRESULT STDMETHODCALLTYPE AddBand(IUnknown * punk);
80 virtual HRESULT STDMETHODCALLTYPE EnumBands(UINT uBand, DWORD* pdwBandID);
81 virtual HRESULT STDMETHODCALLTYPE Exec(const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut);
82 virtual HRESULT STDMETHODCALLTYPE GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv);
83 virtual HRESULT STDMETHODCALLTYPE GetSize(DWORD dwWhich, LPRECT prc);
84 virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
85 virtual HRESULT STDMETHODCALLTYPE IsWindowOwner(HWND hWnd);
86 virtual HRESULT STDMETHODCALLTYPE OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult);
87 virtual HRESULT STDMETHODCALLTYPE QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName);
88 virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject);
89 virtual HRESULT STDMETHODCALLTYPE QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText);
90 virtual HRESULT STDMETHODCALLTYPE SetDeskBarSite(IUnknown *punkSite);
91 virtual HRESULT STDMETHODCALLTYPE UIActivateDBC(DWORD dwState);
92 virtual HRESULT STDMETHODCALLTYPE UIActivateIO(BOOL fActivate, LPMSG lpMsg);
93
94 // Using custom message map instead
95 virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD mapId = 0);
96
97 // UNIMPLEMENTED
98 virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
99 virtual HRESULT STDMETHODCALLTYPE GetBandSiteInfo(BANDSITEINFO *pbsinfo);
100 virtual HRESULT STDMETHODCALLTYPE RemoveBand(DWORD dwBandID);
101 virtual HRESULT STDMETHODCALLTYPE SetBandSiteInfo(const BANDSITEINFO *pbsinfo);
102 virtual HRESULT STDMETHODCALLTYPE SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState);
103 virtual HRESULT STDMETHODCALLTYPE SetModeDBC(DWORD dwMode);
104
105 private:
106 BOOL CreateSiteWindow(HWND hWndParent);
107 };
108
109 extern "C"
110 HRESULT CMenuSite_Constructor(REFIID riid, LPVOID *ppv)
111 {
112 *ppv = NULL;
113
114 CMenuSite * site = new CComObject<CMenuSite>();
115
116 if (!site)
117 return E_OUTOFMEMORY;
118
119 HRESULT hr = site->QueryInterface(riid, ppv);
120
121 if (FAILED(hr))
122 site->Release();
123
124 return hr;
125 }
126
127 CMenuSite::CMenuSite() :
128 m_DeskBarSite(NULL),
129 m_BandObject(NULL),
130 m_DeskBand(NULL),
131 m_WinEventHandler(NULL),
132 m_hWndBand(NULL)
133 {
134 }
135
136 HRESULT STDMETHODCALLTYPE CMenuSite::ContextSensitiveHelp(BOOL fEnterMode)
137 {
138 return E_NOTIMPL;
139 }
140
141 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandSiteInfo(BANDSITEINFO *pbsinfo)
142 {
143 return E_NOTIMPL;
144 }
145
146 HRESULT STDMETHODCALLTYPE CMenuSite::RemoveBand(DWORD dwBandID)
147 {
148 return E_NOTIMPL;
149 }
150
151 HRESULT STDMETHODCALLTYPE CMenuSite::SetBandSiteInfo(const BANDSITEINFO *pbsinfo)
152 {
153 return E_NOTIMPL;
154 }
155
156 HRESULT STDMETHODCALLTYPE CMenuSite::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState)
157 {
158 return E_NOTIMPL;
159 }
160
161 HRESULT STDMETHODCALLTYPE CMenuSite::SetModeDBC(DWORD dwMode)
162 {
163 return E_NOTIMPL;
164 }
165
166 HRESULT STDMETHODCALLTYPE CMenuSite::TranslateAcceleratorIO(LPMSG lpMsg)
167 {
168 return S_FALSE;
169 }
170
171 HRESULT STDMETHODCALLTYPE CMenuSite::HasFocusIO()
172 {
173 return S_FALSE;
174 }
175
176 HRESULT STDMETHODCALLTYPE CMenuSite::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
177 {
178 return S_OK;
179 }
180
181 HRESULT STDMETHODCALLTYPE CMenuSite::AddBand(IUnknown * punk)
182 {
183 if (SHIsSameObject(punk, m_BandObject))
184 return S_OK + 0;
185
186 IUnknown_SetSite(m_BandObject, NULL);
187
188 if (m_BandObject)
189 {
190 m_BandObject->Release();
191 m_BandObject = NULL;
192 }
193
194 if (m_DeskBand)
195 {
196 m_DeskBand->Release();
197 m_DeskBand = NULL;
198 }
199
200 if (m_WinEventHandler)
201 {
202 m_WinEventHandler->Release();
203 m_WinEventHandler = NULL;
204 }
205
206 BOOL result = m_hWndBand != NULL;
207
208 m_hWndBand = NULL;
209
210 if (!punk)
211 return result ? S_OK + 0 : E_FAIL;
212
213 DBGASSERT(SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IDeskBand, &m_DeskBand))));
214 DBGASSERT(SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IWinEventHandler, &m_WinEventHandler))));
215
216 IUnknown_SetSite(punk, (IDeskBarClient*)this);
217 IUnknown_GetWindow(punk, &m_hWndBand);
218
219 m_BandObject = punk;
220
221 punk->AddRef();
222
223 return S_OK + 0;
224 }
225
226 HRESULT STDMETHODCALLTYPE CMenuSite::EnumBands(UINT uBand, DWORD* pdwBandID)
227 {
228 if (uBand != 0)
229 return E_FAIL;
230
231 *pdwBandID = 0;
232
233 return S_OK;
234 }
235
236 HRESULT STDMETHODCALLTYPE CMenuSite::Exec(const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
237 {
238 return IUnknown_Exec(m_DeskBarSite, *pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
239 }
240
241 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)
242 {
243 if (!DBGASSERT(dwBandID == 0) || m_BandObject == NULL)
244 {
245 *ppv = NULL;
246 return E_NOINTERFACE;
247 }
248
249 return m_BandObject->QueryInterface(riid, ppv);
250 }
251
252 HRESULT STDMETHODCALLTYPE CMenuSite::GetSize(DWORD dwWhich, LPRECT prc)
253 {
254 memset(prc, 0, sizeof(*prc));
255
256 if (dwWhich != 0)
257 return S_OK;
258
259 if (m_DeskBand == NULL)
260 return S_OK;
261
262 DESKBANDINFO info = { 0 };
263
264 m_DeskBand->GetBandInfo(0, 0, &info);
265
266 prc->right = info.ptMaxSize.x;
267 prc->bottom = info.ptMaxSize.y;
268
269 return S_OK;
270 }
271
272 HRESULT STDMETHODCALLTYPE CMenuSite::GetWindow(HWND *phwnd)
273 {
274 DBGASSERT(IsWindow());
275
276 *phwnd = m_hWnd;
277
278 return S_OK;
279 }
280
281 HRESULT STDMETHODCALLTYPE CMenuSite::IsWindowOwner(HWND hWnd)
282 {
283 if (hWnd == m_hWnd)
284 return S_OK;
285
286 if (!m_WinEventHandler)
287 return S_FALSE;
288
289 return m_WinEventHandler->IsWindowOwner(hWnd);
290 }
291
292 HRESULT STDMETHODCALLTYPE CMenuSite::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
293 {
294 if (!m_WinEventHandler)
295 return S_OK;
296
297 return m_WinEventHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
298 }
299
300 HRESULT STDMETHODCALLTYPE CMenuSite::QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName)
301 {
302 DBGASSERT(dwBandID == 0);
303 DBGASSERT(!IsBadWritePtr(ppstb, sizeof(*ppstb)));
304
305 if (!m_BandObject)
306 {
307 *ppstb = NULL;
308 return E_NOINTERFACE;
309 }
310
311 HRESULT hr = m_BandObject->QueryInterface(IID_PPV_ARG(IDeskBand, ppstb));
312
313 *pdwState = 1;
314
315 if (cchName > 0)
316 pszName[0] = 0;
317
318 return hr;
319 }
320
321 HRESULT STDMETHODCALLTYPE CMenuSite::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
322 {
323 *ppvObject = NULL;
324
325 if (IsEqualGUID(guidService, SID_SMenuBandBottom) ||
326 IsEqualGUID(guidService, SID_SMenuBandBottomSelected) ||
327 IsEqualGUID(guidService, SID_SMenuBandChild))
328 {
329 if (m_BandObject == NULL)
330 return E_FAIL;
331
332 return IUnknown_QueryService(m_BandObject, guidService, riid, ppvObject);
333 }
334
335 DBGASSERT(m_DeskBarSite);
336
337 return IUnknown_QueryService(m_DeskBarSite, guidService, riid, ppvObject);
338 }
339
340 HRESULT STDMETHODCALLTYPE CMenuSite::QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
341 {
342 if (!DBGASSERT(m_DeskBarSite))
343 return E_FAIL;
344 return IUnknown_QueryStatus(m_DeskBarSite, *pguidCmdGroup, cCmds, prgCmds, pCmdText);
345 }
346
347 HRESULT STDMETHODCALLTYPE CMenuSite::SetDeskBarSite(IUnknown *punkSite)
348 {
349 HWND hWndSite;
350
351 ((IDeskBarClient*)this)->AddRef();
352
353 if (punkSite)
354 {
355 if (m_DeskBarSite)
356 {
357 m_DeskBarSite->Release();
358 m_DeskBarSite = NULL;
359 }
360
361 IUnknown_GetWindow(punkSite, &hWndSite);
362
363 if (hWndSite)
364 {
365 CreateSiteWindow(hWndSite);
366
367 m_DeskBarSite = punkSite;
368
369 punkSite->AddRef();
370 }
371 }
372 else
373 {
374 if (m_DeskBand)
375 {
376 m_DeskBand->CloseDW(0);
377 }
378
379 IUnknown_SetSite(m_BandObject, NULL);
380
381 if (m_BandObject)
382 {
383 m_BandObject->Release();
384 m_BandObject = NULL;
385 }
386
387 if (m_DeskBand)
388 {
389 m_DeskBand->Release();
390 m_DeskBand = NULL;
391 }
392
393 if (m_WinEventHandler)
394 {
395 m_WinEventHandler->Release();
396 m_WinEventHandler = NULL;
397 }
398
399 m_hWndBand = NULL;
400
401 if (m_hWnd)
402 {
403 DestroyWindow();
404 m_hWnd = NULL;
405 }
406
407 if (m_DeskBarSite)
408 m_DeskBarSite->Release();
409
410 m_DeskBarSite = NULL;
411 }
412
413 ((IDeskBarClient*)this)->Release();
414
415 if (!m_hWnd)
416 return E_FAIL;
417
418 return S_OK;
419 }
420
421 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateDBC(DWORD dwState)
422 {
423 if (!DBGASSERT(m_DeskBand))
424 return S_OK;
425
426 return m_DeskBand->ShowDW(dwState != 0);
427 }
428
429 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
430 {
431 if (lpMsg && DBGASSERT(IsBadWritePtr(lpMsg, sizeof(*lpMsg))))
432 return E_FAIL;
433
434 return IUnknown_UIActivateIO(m_BandObject, fActivate, lpMsg);
435 }
436
437 BOOL CMenuSite::CreateSiteWindow(HWND hWndParent)
438 {
439 if (m_hWnd)
440 {
441 return DBGASSERT(IsWindow());
442 }
443
444 Create(hWndParent, NULL, L"MenuSite");
445
446 return m_hWnd != NULL;
447 }
448
449 BOOL CMenuSite::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD mapId)
450 {
451 HWND hWndToCall;
452 IMenuPopup * pMenuPopup;
453
454 ((IDeskBarClient*)this)->AddRef();
455
456 switch (uMsg)
457 {
458 case WM_SIZE:
459 if (m_BandObject)
460 {
461 if (SUCCEEDED(m_BandObject->QueryInterface(IID_PPV_ARG(IMenuPopup, &pMenuPopup))))
462 {
463 RECT Rect = { 0 };
464 GetClientRect(&Rect);
465 pMenuPopup->OnPosRectChangeDB(&Rect);
466 pMenuPopup->Release();
467 }
468 }
469 hWndToCall = hWnd;
470 lResult = 1;
471 break;
472 case WM_NOTIFY:
473 hWndToCall = *(HWND *) lParam;
474 break;
475 case WM_COMMAND:
476 hWndToCall = (HWND) lParam;
477 break;
478 default:
479 ((IDeskBarClient*)this)->Release();
480 return FALSE;
481 }
482
483 if (hWndToCall)
484 {
485 if (m_WinEventHandler)
486 {
487 if (m_WinEventHandler->IsWindowOwner(hWndToCall) == S_OK)
488 {
489 HRESULT hr = m_WinEventHandler->OnWinEvent(hWndToCall, uMsg, wParam, lParam, &lResult);
490 ((IDeskBarClient*)this)->Release();
491 return hr == S_OK;
492 }
493 }
494 }
495
496 ((IDeskBarClient*)this)->Release();
497 return FALSE;
498 }