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