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 #include "shellmenu.h"
21 #include <atlwin.h>
22 #include <shlwapi_undoc.h>
23 
24 #include "CMenuSite.h"
25 
CMenuSite()26 CMenuSite::CMenuSite() :
27     m_DeskBarSite(NULL),
28     m_BandObject(NULL),
29     m_DeskBand(NULL),
30     m_WinEventHandler(NULL),
31     m_hWndBand(NULL)
32 {
33 }
34 
35 // Child Band management (simplified due to only supporting one single child)
AddBand(IUnknown * punk)36 HRESULT STDMETHODCALLTYPE CMenuSite::AddBand(IUnknown * punk)
37 {
38     HRESULT hr;
39 
40     // Little helper, for readability
41 #define TO_HRESULT(x) ((HRESULT)(S_OK+(x)))
42 
43     CComPtr<IUnknown> pUnknown;
44 
45     punk->QueryInterface(IID_PPV_ARG(IUnknown, &pUnknown));
46 
47     if (pUnknown == m_BandObject)
48         return TO_HRESULT(0);
49 
50     if (m_BandObject)
51     {
52         hr = IUnknown_SetSite(m_BandObject, NULL);
53         if (FAILED_UNEXPECTEDLY(hr))
54             return hr;
55     }
56 
57     m_BandObject = NULL;
58     m_DeskBand = NULL;
59     m_WinEventHandler = NULL;
60     m_hWndBand = NULL;
61 
62     if (!pUnknown)
63         return TO_HRESULT(0);
64 
65     hr = pUnknown->QueryInterface(IID_PPV_ARG(IDeskBand, &m_DeskBand));
66     if (FAILED_UNEXPECTEDLY(hr))
67         return hr;
68 
69     hr = pUnknown->QueryInterface(IID_PPV_ARG(IWinEventHandler, &m_WinEventHandler));
70     if (FAILED_UNEXPECTEDLY(hr))
71         return hr;
72 
73     hr = IUnknown_SetSite(pUnknown, this->ToIUnknown());
74     if (FAILED_UNEXPECTEDLY(hr))
75         return hr;
76 
77     hr = IUnknown_GetWindow(pUnknown, &m_hWndBand);
78     if (FAILED_UNEXPECTEDLY(hr))
79         return hr;
80 
81     m_BandObject = pUnknown;
82 
83     return TO_HRESULT(0);
84 }
85 
EnumBands(UINT uBand,DWORD * pdwBandID)86 HRESULT STDMETHODCALLTYPE CMenuSite::EnumBands(UINT uBand, DWORD* pdwBandID)
87 {
88     if (uBand == -1ul)
89         return GetBandCount();
90 
91     if (uBand != 0)
92         return E_FAIL;
93 
94     *pdwBandID = 0;
95 
96     return S_OK;
97 }
98 
GetBandObject(DWORD dwBandID,REFIID riid,VOID ** ppv)99 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandObject(DWORD dwBandID, REFIID riid, VOID **ppv)
100 {
101     if (dwBandID != 0 || m_BandObject == NULL)
102     {
103         *ppv = NULL;
104         return E_NOINTERFACE;
105     }
106 
107     return m_BandObject->QueryInterface(riid, ppv);
108 }
109 
QueryBand(DWORD dwBandID,IDeskBand ** ppstb,DWORD * pdwState,LPWSTR pszName,int cchName)110 HRESULT STDMETHODCALLTYPE CMenuSite::QueryBand(DWORD dwBandID, IDeskBand **ppstb, DWORD *pdwState, LPWSTR pszName, int cchName)
111 {
112     if (dwBandID != 0)
113         return E_FAIL;
114 
115     if (!m_BandObject)
116     {
117         *ppstb = NULL;
118         return E_NOINTERFACE;
119     }
120 
121     HRESULT hr = m_BandObject->QueryInterface(IID_PPV_ARG(IDeskBand, ppstb));
122 
123     *pdwState = 1;
124 
125     if (cchName > 0)
126         pszName[0] = 0;
127 
128     return hr;
129 }
130 
GetSize(DWORD dwWhich,LPRECT prc)131 HRESULT STDMETHODCALLTYPE CMenuSite::GetSize(DWORD dwWhich, LPRECT prc)
132 {
133     memset(prc, 0, sizeof(*prc));
134 
135     if (dwWhich != 0)
136         return S_OK;
137 
138     if (m_DeskBand == NULL)
139         return S_OK;
140 
141     DESKBANDINFO info = { 0 };
142     info.dwMask = DBIM_MAXSIZE;
143 
144     m_DeskBand->GetBandInfo(0, 0, &info);
145 
146     prc->right = info.ptMaxSize.x;
147     prc->bottom = info.ptMaxSize.y;
148 
149     return S_OK;
150 }
151 
GetWindow(HWND * phwnd)152 HRESULT STDMETHODCALLTYPE CMenuSite::GetWindow(HWND *phwnd)
153 {
154     if (!IsWindow())
155         return E_FAIL;
156 
157     *phwnd = m_hWnd;
158 
159     return S_OK;
160 }
161 
IsWindowOwner(HWND hWnd)162 HRESULT STDMETHODCALLTYPE CMenuSite::IsWindowOwner(HWND hWnd)
163 {
164     if (hWnd == m_hWnd)
165         return S_OK;
166 
167     if (!m_WinEventHandler)
168         return S_FALSE;
169 
170     return m_WinEventHandler->IsWindowOwner(hWnd);
171 }
172 
OnWinEvent(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT * theResult)173 HRESULT STDMETHODCALLTYPE CMenuSite::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
174 {
175     if (!m_WinEventHandler)
176         return S_OK;
177 
178     return m_WinEventHandler->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
179 }
180 
QueryService(REFGUID guidService,REFIID riid,void ** ppvObject)181 HRESULT STDMETHODCALLTYPE CMenuSite::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
182 {
183     *ppvObject = NULL;
184 
185     if (IsEqualGUID(guidService, SID_SMenuBandBottom) ||
186         IsEqualGUID(guidService, SID_SMenuBandBottomSelected) ||
187         IsEqualGUID(guidService, SID_SMenuBandChild))
188     {
189         if (m_BandObject == NULL)
190             return E_NOINTERFACE;
191 
192         return IUnknown_QueryService(m_BandObject, guidService, riid, ppvObject);
193     }
194 
195     if (!m_DeskBarSite)
196         return E_NOINTERFACE;
197 
198     return IUnknown_QueryService(m_DeskBarSite, guidService, riid, ppvObject);
199 }
200 
Exec(const GUID * pguidCmdGroup,DWORD nCmdID,DWORD nCmdexecopt,VARIANT * pvaIn,VARIANT * pvaOut)201 HRESULT STDMETHODCALLTYPE CMenuSite::Exec(const GUID * pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
202 {
203     // Forward Exec calls directly to the parent deskbar
204     return IUnknown_Exec(m_DeskBarSite, *pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
205 }
206 
QueryStatus(const GUID * pguidCmdGroup,ULONG cCmds,OLECMD prgCmds[],OLECMDTEXT * pCmdText)207 HRESULT STDMETHODCALLTYPE CMenuSite::QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
208 {
209     // Forward QueryStatus calls directly to the parent deskbar
210     return IUnknown_QueryStatus(m_DeskBarSite, *pguidCmdGroup, cCmds, prgCmds, pCmdText);
211 }
212 
SetDeskBarSite(IUnknown * punkSite)213 HRESULT STDMETHODCALLTYPE CMenuSite::SetDeskBarSite(IUnknown *punkSite)
214 {
215     HRESULT hr;
216 
217     CComPtr<IUnknown> protectThis(this->ToIUnknown());
218 
219     // Only initialize if a parent site is being assigned
220     if (punkSite)
221     {
222         HWND hWndSite;
223 
224         m_DeskBarSite = NULL;
225 
226         hr = IUnknown_GetWindow(punkSite, &hWndSite);
227 
228         if (FAILED(hr) || !hWndSite)
229             return E_FAIL;
230 
231         if (!m_hWnd)
232         {
233             Create(hWndSite, NULL, L"MenuSite");
234         }
235 
236         m_DeskBarSite = punkSite;
237     }
238     else
239     {
240         // Otherwise, deinitialize.
241         if (m_DeskBand)
242         {
243             m_DeskBand->CloseDW(0);
244         }
245 
246         hr = IUnknown_SetSite(m_BandObject, NULL);
247 
248         m_BandObject = NULL;
249         m_DeskBand = NULL;
250         m_WinEventHandler = NULL;
251         m_hWndBand = NULL;
252         if (m_hWnd)
253             DestroyWindow();
254         m_DeskBarSite = NULL;
255     }
256 
257     return S_OK;
258 }
259 
UIActivateDBC(DWORD dwState)260 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateDBC(DWORD dwState)
261 {
262     if (!m_DeskBand)
263         return S_OK;
264 
265     return m_DeskBand->ShowDW(dwState != 0);
266 }
267 
UIActivateIO(BOOL fActivate,LPMSG lpMsg)268 HRESULT STDMETHODCALLTYPE CMenuSite::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
269 {
270     if (lpMsg)
271         return E_FAIL;
272 
273     return IUnknown_UIActivateIO(m_BandObject, fActivate, lpMsg);
274 }
275 
ProcessWindowMessage(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT & lResult,DWORD mapId)276 BOOL CMenuSite::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD mapId)
277 {
278     HWND hWndTarget = NULL;
279     CComPtr<IUnknown> protectThis(this->ToIUnknown());
280 
281     switch (uMsg)
282     {
283     case WM_SIZE:
284         if (m_BandObject)
285         {
286             CComPtr<IMenuPopup> pMenuPopup;
287             if (SUCCEEDED(m_BandObject->QueryInterface(IID_PPV_ARG(IMenuPopup, &pMenuPopup))))
288             {
289                 RECT Rect = { 0 };
290                 GetClientRect(&Rect);
291                 pMenuPopup->OnPosRectChangeDB(&Rect);
292             }
293         }
294         hWndTarget = hWnd;
295         lResult = 1;
296         break;
297     case WM_NOTIFY:
298         hWndTarget = reinterpret_cast<LPNMHDR>(lParam)->hwndFrom;
299         break;
300     case WM_COMMAND:
301         hWndTarget = (HWND) lParam;
302         break;
303     default:
304         return FALSE;
305     }
306 
307     if (hWndTarget && m_WinEventHandler &&
308         m_WinEventHandler->IsWindowOwner(hWndTarget) == S_OK)
309     {
310         if (SUCCEEDED(m_WinEventHandler->OnWinEvent(hWndTarget, uMsg, wParam, lParam, &lResult)))
311             return TRUE;
312     }
313 
314     return FALSE;
315 }
316 
ContextSensitiveHelp(BOOL fEnterMode)317 HRESULT STDMETHODCALLTYPE CMenuSite::ContextSensitiveHelp(BOOL fEnterMode)
318 {
319     return E_NOTIMPL;
320 }
321 
GetBandSiteInfo(BANDSITEINFO * pbsinfo)322 HRESULT STDMETHODCALLTYPE CMenuSite::GetBandSiteInfo(BANDSITEINFO *pbsinfo)
323 {
324     return E_NOTIMPL;
325 }
326 
RemoveBand(DWORD dwBandID)327 HRESULT STDMETHODCALLTYPE CMenuSite::RemoveBand(DWORD dwBandID)
328 {
329     return E_NOTIMPL;
330 }
331 
SetBandSiteInfo(const BANDSITEINFO * pbsinfo)332 HRESULT STDMETHODCALLTYPE CMenuSite::SetBandSiteInfo(const BANDSITEINFO *pbsinfo)
333 {
334     return E_NOTIMPL;
335 }
336 
SetBandState(DWORD dwBandID,DWORD dwMask,DWORD dwState)337 HRESULT STDMETHODCALLTYPE CMenuSite::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState)
338 {
339     return E_NOTIMPL;
340 }
341 
SetModeDBC(DWORD dwMode)342 HRESULT STDMETHODCALLTYPE CMenuSite::SetModeDBC(DWORD dwMode)
343 {
344     return E_NOTIMPL;
345 }
346 
TranslateAcceleratorIO(LPMSG lpMsg)347 HRESULT STDMETHODCALLTYPE CMenuSite::TranslateAcceleratorIO(LPMSG lpMsg)
348 {
349     return S_FALSE;
350 }
351 
HasFocusIO()352 HRESULT STDMETHODCALLTYPE CMenuSite::HasFocusIO()
353 {
354     return S_FALSE;
355 }
356 
OnFocusChangeIS(IUnknown * punkObj,BOOL fSetFocus)357 HRESULT STDMETHODCALLTYPE CMenuSite::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
358 {
359     return S_OK;
360 }
361 
362 extern "C"
RSHELL_CMenuSite_CreateInstance(REFIID riid,LPVOID * ppv)363 HRESULT WINAPI RSHELL_CMenuSite_CreateInstance(REFIID riid, LPVOID *ppv)
364 {
365     return ShellObjectCreator<CMenuSite>(riid, ppv);
366 }
367