1 /*
2  * Shell Menu Desk Bar
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 "CMenuDeskBar.h"
25 
26 /* As far as I can tell, the submenu hierarchy looks like this:
27 *
28 * The DeskBar's Child is the Band it contains.
29 * The DeskBar's Parent is the SID_SMenuPopup of the Site.
30 *
31 * The Band's Child is the IMenuPopup of the child submenu.
32 * The Band's Parent is the SID_SMenuPopup of the Site (the DeskBar).
33 *
34 * When the DeskBar receives a selection event:
35 * If it requires closing the window, it will notify the Child (Band) using CancelLevel.
36 * If it has to spread upwards (everything but CancelLevel), it will notify the Parent.
37 *
38 * When the Band receives a selection event, this is where it gets fuzzy:
39 * In which cases does it call the Parent? Probably not CancelLevel.
40 * In which cases does it call the Child?
41 * How does it react to calls?
42 *
43 */
44 
45 
46 WINE_DEFAULT_DEBUG_CHANNEL(CMenuDeskBar);
47 
48 CMenuDeskBar::CMenuDeskBar() :
49     m_Client(NULL),
50     m_ClientWindow(NULL),
51     m_IconSize(0),
52     m_Banner(NULL),
53     m_Shown(FALSE),
54     m_ShowFlags(0),
55     m_didAddRef(FALSE)
56 {
57 }
58 
59 CMenuDeskBar::~CMenuDeskBar()
60 {
61 }
62 
63 LRESULT CMenuDeskBar::_OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
64 {
65     if (!m_didAddRef)
66     {
67         this->AddRef();
68         m_didAddRef = TRUE;
69     }
70 
71     bHandled = FALSE;
72     return 0;
73 }
74 
75 void CMenuDeskBar::OnFinalMessage(HWND /* hWnd */)
76 {
77     if (m_didAddRef)
78     {
79         this->Release();
80         m_didAddRef = FALSE;
81     }
82 }
83 
84 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Initialize(THIS)
85 {
86     return S_OK;
87 }
88 
89 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetWindow(HWND *lphwnd)
90 {
91     if (lphwnd == NULL)
92         return E_POINTER;
93     *lphwnd = m_hWnd;
94     return S_OK;
95 }
96 
97 HRESULT STDMETHODCALLTYPE CMenuDeskBar::ContextSensitiveHelp(BOOL fEnterMode)
98 {
99     return E_NOTIMPL;
100 }
101 
102 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
103 {
104     return IUnknown_OnFocusChangeIS(m_Client, punkObj, fSetFocus);
105 }
106 
107 HRESULT STDMETHODCALLTYPE CMenuDeskBar::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
108     OLECMD prgCmds [], OLECMDTEXT *pCmdText)
109 {
110     return E_NOTIMPL;
111 }
112 
113 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
114     DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
115 {
116     if (IsEqualIID(*pguidCmdGroup, CGID_MenuDeskBar))
117     {
118         switch (nCmdID)
119         {
120         case 2: // refresh
121             return S_OK;
122         case 3: // load complete
123             return S_OK;
124         case 4: // set font metrics
125             return _AdjustForTheme(nCmdexecopt);
126         }
127     }
128     if (IsEqualIID(*pguidCmdGroup, CGID_Explorer))
129     {
130     }
131     else if (IsEqualIID(*pguidCmdGroup, IID_IDeskBarClient))
132     {
133         switch (nCmdID)
134         {
135         case 0:
136             // hide current band
137             break;
138         case 2:
139             break;
140         case 3:
141             break;
142         }
143     }
144     return E_NOTIMPL;
145 }
146 
147 HRESULT STDMETHODCALLTYPE CMenuDeskBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
148 {
149     HRESULT hr;
150 
151     if (IsEqualGUID(guidService, SID_SMenuPopup) ||
152         IsEqualGUID(guidService, SID_SMenuBandParent) ||
153         IsEqualGUID(guidService, SID_STopLevelBrowser))
154     {
155         hr = this->QueryInterface(riid, ppvObject);
156         if (SUCCEEDED(hr))
157             return hr;
158     }
159 
160     if (IsEqualGUID(guidService, SID_SMenuBandBottom) ||
161         IsEqualGUID(guidService, SID_SMenuBandBottomSelected) ||
162         IsEqualGUID(guidService, SID_SMenuBandChild))
163     {
164         if (m_Client == NULL)
165             return E_NOINTERFACE;
166 
167         hr = IUnknown_QueryService(m_Client, guidService, riid, ppvObject);
168         if (SUCCEEDED(hr))
169             return hr;
170     }
171 
172 
173     if (m_Site == NULL)
174         return E_NOINTERFACE;
175 
176     return IUnknown_QueryService(m_Site, guidService, riid, ppvObject);
177 }
178 
179 HRESULT STDMETHODCALLTYPE CMenuDeskBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
180 {
181     return IUnknown_UIActivateIO(m_Client, fActivate, lpMsg);
182 }
183 
184 HRESULT STDMETHODCALLTYPE CMenuDeskBar::HasFocusIO()
185 {
186     return IUnknown_HasFocusIO(m_Client);
187 }
188 
189 HRESULT STDMETHODCALLTYPE CMenuDeskBar::TranslateAcceleratorIO(LPMSG lpMsg)
190 {
191     return IUnknown_TranslateAcceleratorIO(m_Client, lpMsg);
192 }
193 
194 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetClient(IUnknown *punkClient)
195 {
196     CComPtr<IDeskBarClient> pDeskBandClient;
197     HRESULT hr;
198 
199     if (m_Client)
200     {
201         hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pDeskBandClient));
202         if (FAILED_UNEXPECTEDLY(hr))
203             return hr;
204 
205         pDeskBandClient->SetDeskBarSite(NULL);
206 
207         pDeskBandClient = NULL;
208         m_Client = NULL;
209     }
210 
211     if (punkClient == NULL)
212         return S_OK;
213 
214     if (m_hWnd == NULL)
215     {
216         Create(NULL);
217     }
218 
219     hr = punkClient->QueryInterface(IID_PPV_ARG(IUnknown, &m_Client));
220     if (FAILED_UNEXPECTEDLY(hr))
221         return hr;
222 
223     hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pDeskBandClient));
224     if (FAILED_UNEXPECTEDLY(hr))
225         return hr;
226 
227     hr = pDeskBandClient->SetDeskBarSite(static_cast<IDeskBar*>(this));
228     if (FAILED_UNEXPECTEDLY(hr))
229         return hr;
230 
231     return IUnknown_GetWindow(m_Client, &m_ClientWindow);
232 }
233 
234 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetClient(IUnknown **ppunkClient)
235 {
236     if (ppunkClient == NULL)
237         return E_POINTER;
238 
239     if (!m_Client)
240         return E_FAIL;
241 
242     return m_Client->QueryInterface(IID_PPV_ARG(IUnknown, ppunkClient));
243 }
244 
245 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnPosRectChangeDB(LPRECT prc)
246 {
247     if (prc == NULL)
248         return E_POINTER;
249 
250     return S_OK;
251 }
252 
253 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSite(IUnknown *pUnkSite)
254 {
255     // Windows closes the bar if this is called when the bar is shown
256 
257     if (m_Shown)
258         _CloseBar();
259 
260     m_SubMenuParent = NULL;
261 
262     m_Site = pUnkSite;
263 
264     if (m_Site)
265     {
266         IUnknown_QueryService(m_Site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &m_SubMenuParent));
267     }
268     else
269     {
270         SetClient(NULL);
271         DestroyWindow();
272     }
273 
274     return S_OK;
275 }
276 
277 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetSite(REFIID riid, void **ppvSite)
278 {
279     if (m_Site == NULL)
280         return E_FAIL;
281 
282     return m_Site->QueryInterface(riid, ppvSite);
283 }
284 
285 static void AdjustForExcludeArea(BOOL alignLeft, BOOL alignTop, BOOL preferVertical, PINT px, PINT py, INT cx, INT cy, RECTL rcExclude) {
286     RECT rcWindow = { *px, *py, *px + cx, *py + cy };
287 
288     if (rcWindow.right > rcExclude.left && rcWindow.left < rcExclude.right &&
289         rcWindow.bottom > rcExclude.top && rcWindow.top < rcExclude.bottom)
290     {
291         if (preferVertical)
292         {
293             if (alignTop && rcWindow.bottom > rcExclude.top)
294                 *py = rcExclude.top - cy;
295             else if (!alignTop && rcWindow.top < rcExclude.bottom)
296                 *py = rcExclude.bottom;
297             else if (alignLeft && rcWindow.right > rcExclude.left)
298                 *px = rcExclude.left - cx;
299             else if (!alignLeft && rcWindow.left < rcExclude.right)
300                 *px = rcExclude.right;
301         }
302         else
303         {
304             if (alignLeft && rcWindow.right > rcExclude.left)
305                 *px = rcExclude.left - cx;
306             else if (!alignLeft && rcWindow.left < rcExclude.right)
307                 *px = rcExclude.right;
308             else if (alignTop && rcWindow.bottom > rcExclude.top)
309                 *py = rcExclude.top - cy;
310             else if (!alignTop && rcWindow.top < rcExclude.bottom)
311                 *py = rcExclude.bottom;
312         }
313     }
314 }
315 
316 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags)
317 {
318     HRESULT hr;
319     CComPtr<IOleCommandTarget> oct;
320     CComPtr<IInputObject> io;
321     CComPtr<IDeskBand> band;
322     CComPtr<IDeskBarClient> dbc;
323 
324     if (m_hWnd == NULL)
325         return E_FAIL;
326 
327     hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IOleCommandTarget, &oct));
328     if (FAILED_UNEXPECTEDLY(hr))
329         return hr;
330 
331     hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
332     if (FAILED_UNEXPECTEDLY(hr))
333         return hr;
334 
335     // Windows calls this, but it appears to be unimplemented?
336     hr = dbc->SetModeDBC(1);
337     // Allow it to fail with E_NOTIMPL.
338 
339     // No clue about the arg, using anything != 0
340     hr = dbc->UIActivateDBC(TRUE);
341     if (FAILED_UNEXPECTEDLY(hr))
342         return hr;
343 
344     RECT rc = { 0 };
345     hr = dbc->GetSize(0, &rc);
346     if (FAILED_UNEXPECTEDLY(hr))
347         return hr;
348 
349     // Unknown meaning
350     const int CMD = 19;
351     const int CMD_EXEC_OPT = 0;
352 
353     hr = IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD, CMD_EXEC_OPT, NULL, NULL);
354     if (FAILED_UNEXPECTEDLY(hr))
355         return hr;
356 
357     ::AdjustWindowRect(&rc, ::GetWindowLong(m_hWnd, GWL_STYLE), FALSE);
358     ::OffsetRect(&rc, -rc.left, -rc.top);
359 
360     if (m_Banner != NULL)
361     {
362         BITMAP bm;
363         ::GetObject(m_Banner, sizeof(bm), &bm);
364         rc.right += bm.bmWidth;
365     }
366 
367     RECT rcWorkArea;
368     ::GetWindowRect(GetDesktopWindow(), &rcWorkArea);
369     int cxWorkArea = rcWorkArea.right - rcWorkArea.left;
370     int cyWorkArea = rcWorkArea.bottom - rcWorkArea.top;
371 
372     int x = ppt->x;
373     int y = ppt->y;
374     int cx = rc.right - rc.left;
375     int cy = rc.bottom - rc.top;
376 
377     // TODO: Make alignLeft default to TRUE in LTR systems or whenever necessary.
378     BOOL alignLeft = FALSE;
379     BOOL alignTop = FALSE;
380     BOOL preferVertical = FALSE;
381     switch (dwFlags & MPPF_POS_MASK)
382     {
383     case MPPF_TOP:
384         alignTop = TRUE;
385         preferVertical = TRUE;
386         break;
387     case MPPF_LEFT:
388         alignLeft = TRUE;
389         break;
390     case MPPF_BOTTOM:
391         alignTop = FALSE;
392         preferVertical = TRUE;
393         break;
394     case MPPF_RIGHT:
395         alignLeft = FALSE;
396         break;
397     }
398 
399     // Try the selected alignment and verify that it doesn't escape the work area.
400     if (alignLeft)
401     {
402         x = ppt->x - cx;
403     }
404     else
405     {
406         x = ppt->x;
407     }
408 
409     if (alignTop)
410     {
411         y = ppt->y - cy;
412     }
413     else
414     {
415         y = ppt->y;
416     }
417 
418     if (prcExclude)
419         AdjustForExcludeArea(alignLeft, alignTop, preferVertical, &x, &y, cx, cy, *prcExclude);
420 
421     // Verify that it doesn't escape the work area, and flip.
422     if (alignLeft)
423     {
424         if (x < rcWorkArea.left && (ppt->x+cx) <= rcWorkArea.right)
425         {
426             alignLeft = FALSE;
427             if (prcExclude)
428                 x = prcExclude->right - ((x + cx) - prcExclude->left);
429             else
430                 x = ppt->x;
431         }
432     }
433     else
434     {
435         if ((ppt->x + cx) > rcWorkArea.right && x >= rcWorkArea.left)
436         {
437             alignLeft = TRUE;
438             if (prcExclude)
439                 x = prcExclude->left - cx + (prcExclude->right - x);
440             else
441                 x = ppt->x - cx;
442         }
443     }
444 
445     BOOL flipV = FALSE;
446     if (alignTop)
447     {
448         if (y < rcWorkArea.top && (ppt->y + cy) <= rcWorkArea.bottom)
449         {
450             alignTop = FALSE;
451             if (prcExclude)
452                 y = prcExclude->bottom - ((y + cy) - prcExclude->top);
453             else
454                 y = ppt->y;
455 
456             flipV = true;
457         }
458     }
459     else
460     {
461         if ((ppt->y + cy) > rcWorkArea.bottom && y >= rcWorkArea.top)
462         {
463             alignTop = TRUE;
464             if (prcExclude)
465                 y = prcExclude->top - cy + (prcExclude->bottom - y);
466             else
467                 y = ppt->y - cy;
468 
469             flipV = true;
470         }
471     }
472 
473     if (prcExclude)
474         AdjustForExcludeArea(alignLeft, alignTop, preferVertical, &x, &y, cx, cy, *prcExclude);
475 
476     if (x < rcWorkArea.left)
477         x = rcWorkArea.left;
478 
479     if (cx > cxWorkArea)
480         cx = cxWorkArea;
481 
482     if (x + cx > rcWorkArea.right)
483         x = rcWorkArea.right - cx;
484 
485     if (y < rcWorkArea.top)
486         y = rcWorkArea.top;
487 
488     if (cy > cyWorkArea)
489         cy = cyWorkArea;
490 
491     if (y + cy > rcWorkArea.bottom)
492         y = rcWorkArea.bottom - cy;
493 
494     int flags = SWP_SHOWWINDOW | SWP_NOACTIVATE;
495 
496     this->SetWindowPos(HWND_TOPMOST, x, y, cx, cy, flags);
497 
498     if (flipV)
499     {
500         if (dwFlags & MPPF_INITIALSELECT)
501             dwFlags = (dwFlags ^ MPPF_INITIALSELECT) | MPPF_FINALSELECT;
502         else if (dwFlags & MPPF_FINALSELECT)
503             dwFlags = (dwFlags ^ MPPF_FINALSELECT) | MPPF_INITIALSELECT;
504     }
505 
506     m_ShowFlags = dwFlags;
507     m_Shown = true;
508 
509     // HACK: The bar needs to be notified of the size AFTER it is shown.
510     // Quick & dirty way of getting it done.
511     BOOL bHandled;
512     _OnSize(WM_SIZE, 0, 0, bHandled);
513 
514     UIActivateIO(TRUE, NULL);
515 
516     if (dwFlags & (MPPF_INITIALSELECT | MPPF_FINALSELECT))
517     {
518         const int CMD_SELECT = 5;
519         int CMD_SELECT_OPTS = dwFlags & MPPF_INITIALSELECT ? 0 : -2;
520         IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD_SELECT, CMD_SELECT_OPTS, NULL, NULL);
521     }
522 
523     return S_OK;
524 }
525 
526 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon)
527 {
528     HRESULT hr;
529     m_IconSize = iIcon;
530 
531     // Unknown meaning (set flags? set icon size?)
532     const int CMD = 16;
533     const int CMD_EXEC_OPT = iIcon ? 0 : 2; // seems to work
534 
535     hr = IUnknown_QueryServiceExec(m_Client, SID_SMenuBandChild, &CLSID_MenuBand, CMD, CMD_EXEC_OPT, NULL, NULL);
536     if (FAILED_UNEXPECTEDLY(hr))
537         return hr;
538 
539     BOOL bHandled;
540     _OnSize(WM_SIZE, 0, 0, bHandled);
541 
542     return hr;
543 }
544 
545 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetIconSize(THIS_ DWORD* piIcon)
546 {
547     if (piIcon)
548         *piIcon = m_IconSize;
549     return S_OK;
550 }
551 
552 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap)
553 {
554     if (m_Banner && m_Banner != hBitmap)
555         ::DeleteObject(m_Banner);
556 
557     m_Banner = hBitmap;
558 
559     BOOL bHandled;
560     _OnSize(WM_SIZE, 0, 0, bHandled);
561 
562     return S_OK;
563 }
564 
565 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetBitmap(THIS_ HBITMAP* phBitmap)
566 {
567     if (phBitmap)
568         *phBitmap = m_Banner;
569     return S_OK;
570 }
571 
572 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSubMenu(IMenuPopup *pmp, BOOL fSet)
573 {
574     // Called by the MenuBand to assign itself as the logical child of the DeskBar
575 
576     if (fSet)
577     {
578         m_SubMenuChild = pmp;
579     }
580     else
581     {
582         if (m_SubMenuChild)
583         {
584             if (pmp == m_SubMenuChild)
585             {
586                 m_SubMenuChild = NULL;
587             }
588         }
589     }
590     return S_OK;
591 }
592 
593 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnSelect(DWORD dwSelectType)
594 {
595     CComPtr<IDeskBar> safeThis = this;
596     CComPtr<IMenuPopup> oldParent = m_SubMenuParent;
597 
598     TRACE("OnSelect dwSelectType=%d\n", this, dwSelectType);
599     switch (dwSelectType)
600     {
601     case MPOS_EXECUTE:
602     case MPOS_FULLCANCEL:
603     case MPOS_CANCELLEVEL:
604 
605         _CloseBar();
606 
607         if (dwSelectType == MPOS_CANCELLEVEL)
608             return S_OK;
609 
610     case MPOS_SELECTLEFT:
611     case MPOS_SELECTRIGHT:
612     case MPOS_CHILDTRACKING:
613         if (oldParent)
614             return oldParent->OnSelect(dwSelectType);
615         break;
616     }
617 
618     return S_OK;
619 }
620 
621 HRESULT CMenuDeskBar::_CloseBar()
622 {
623     CComPtr<IDeskBarClient> dbc;
624     HRESULT hr;
625 
626     // Ensure that our data isn't destroyed while we are working
627     CComPtr<IDeskBar> safeThis = this;
628 
629     m_Shown = false;
630 
631     if (m_SubMenuParent)
632     {
633         m_SubMenuParent->SetSubMenu(this, FALSE);
634     }
635 
636     if (m_SubMenuChild)
637     {
638         hr = m_SubMenuChild->OnSelect(MPOS_CANCELLEVEL);
639         if (FAILED_UNEXPECTEDLY(hr))
640             return hr;
641     }
642 
643     hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
644     if (FAILED_UNEXPECTEDLY(hr))
645         return hr;
646 
647     hr = dbc->UIActivateDBC(FALSE);
648     if (FAILED_UNEXPECTEDLY(hr))
649         return hr;
650 
651     if (m_hWnd)
652         SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE);
653 
654     return UIActivateIO(FALSE, NULL);
655 }
656 
657 BOOL CMenuDeskBar::_IsSubMenuParent(HWND hwnd)
658 {
659     CComPtr<IMenuPopup> popup = m_SubMenuParent;
660 
661     while (popup)
662     {
663         HRESULT hr;
664         HWND parent;
665 
666         hr = IUnknown_GetWindow(popup, &parent);
667         if (FAILED_UNEXPECTEDLY(hr))
668             return FALSE;
669         if (hwnd == parent)
670             return TRUE;
671 
672         hr = IUnknown_GetSite(popup, IID_PPV_ARG(IMenuPopup, &popup));
673         if (FAILED(hr))
674             return FALSE;
675     }
676 
677     return FALSE;
678 }
679 
680 LRESULT CMenuDeskBar::_OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
681 {
682     if (m_Client)
683     {
684         RECT rc;
685 
686         GetClientRect(&rc);
687 
688         if (m_Banner && m_IconSize != BMICON_SMALL)
689         {
690             BITMAP bm;
691             ::GetObject(m_Banner, sizeof(bm), &bm);
692             rc.left += bm.bmWidth;
693         }
694 
695         ::SetWindowPos(m_ClientWindow, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0);
696     }
697 
698     return 0;
699 }
700 
701 LRESULT CMenuDeskBar::_OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
702 {
703     if (!m_Client)
704         return 0;
705 
706     CComPtr<IWinEventHandler> winEventHandler;
707     HRESULT hr = m_Client->QueryInterface(IID_PPV_ARG(IWinEventHandler, &winEventHandler));
708     if (FAILED_UNEXPECTEDLY(hr))
709         return 0;
710 
711     if (winEventHandler)
712     {
713         LRESULT result;
714         hr = winEventHandler->OnWinEvent(NULL, uMsg, wParam, lParam, &result);
715         if (FAILED_UNEXPECTEDLY(hr))
716             return 0;
717         return result;
718     }
719 
720     return 0;
721 }
722 
723 LRESULT CMenuDeskBar::_OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
724 {
725     bHandled = FALSE;
726 
727     if (m_Banner && !m_IconSize)
728     {
729         BITMAP bm;
730         PAINTSTRUCT ps;
731         HDC hdc = BeginPaint(&ps);
732 
733         HDC hdcMem = ::CreateCompatibleDC(hdc);
734         HGDIOBJ hbmOld = ::SelectObject(hdcMem, m_Banner);
735 
736         ::GetObject(m_Banner, sizeof(bm), &bm);
737 
738         RECT rc;
739         if (!GetClientRect(&rc))
740             WARN("GetClientRect failed\n");
741 
742         const int bx = bm.bmWidth;
743         const int by = bm.bmHeight;
744         const int cy = rc.bottom;
745 
746         TRACE("Painting banner: %d by %d\n", bm.bmWidth, bm.bmHeight);
747 
748         if (!::StretchBlt(hdc, 0, 0, bx, cy - by, hdcMem, 0, 0, bx, 1, SRCCOPY))
749             WARN("StretchBlt failed\n");
750 
751         if (!::BitBlt(hdc, 0, cy - by, bx, by, hdcMem, 0, 0, SRCCOPY))
752             WARN("BitBlt failed\n");
753 
754         ::SelectObject(hdcMem, hbmOld);
755         ::DeleteDC(hdcMem);
756 
757         EndPaint(&ps);
758     }
759 
760     return TRUE;
761 }
762 
763 LRESULT CMenuDeskBar::_OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
764 {
765     // BUG in ReactOS: WM_ACTIVATE/WA_INACTIVE makes no sense with lParam==hWnd
766     if (LOWORD(wParam) != 0 || reinterpret_cast<HWND>(lParam) == m_hWnd)
767     {
768         return 0;
769     }
770 
771     // HACK! I just want it to work !!!
772     CComPtr<IDeskBar> db;
773     HRESULT hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IDeskBar, &db));
774     if (FAILED_UNEXPECTEDLY(hr))
775         return 0;
776 
777     CComPtr<IUnknown> punk;
778 
779     hr = db->GetClient(&punk);
780     if (FAILED_UNEXPECTEDLY(hr))
781         return 0;
782 
783     if (!punk && m_Shown)
784     {
785         if (!_IsSubMenuParent(reinterpret_cast<HWND>(lParam)))
786         {
787             OnSelect(MPOS_FULLCANCEL);
788         }
789     }
790 
791     return 0;
792 }
793 
794 LRESULT CMenuDeskBar::_OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
795 {
796     return MA_NOACTIVATE;
797 }
798 
799 LRESULT CMenuDeskBar::_OnAppActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
800 {
801 #if 0
802     if (wParam == 0 && m_Shown)
803     {
804         OnSelect(MPOS_FULLCANCEL);
805     }
806 #endif
807     return 0;
808 }
809 
810 LRESULT CMenuDeskBar::_OnWinIniChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
811 {
812     if (wParam == SPI_SETFLATMENU)
813         return _OnNotify(uMsg, wParam, lParam, bHandled);
814 
815     return 0;
816 }
817 
818 LRESULT CMenuDeskBar::_OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
819 {
820     /* If it is a flat style menu we need to handle WM_NCPAINT
821      * and paint the border with the right colour */
822     if ((GetStyle() & WS_BORDER) == 0)
823     {
824         /* This isn't a flat style menu. */
825         bHandled = FALSE;
826         return 0;
827     }
828 
829     HDC hdc;
830     RECT rcWindow;
831 
832     hdc = GetWindowDC();
833     GetWindowRect(&rcWindow);
834     OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
835     FrameRect(hdc, &rcWindow, GetSysColorBrush(COLOR_BTNSHADOW));
836     ReleaseDC(hdc);
837     return 0;
838 }
839 
840 LRESULT CMenuDeskBar::_OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
841 {
842     /* Prevent the CMenuDeskBar from destroying on being sent a WM_CLOSE */
843     return 0;
844 }
845 
846 HRESULT CMenuDeskBar::_AdjustForTheme(BOOL bFlatStyle)
847 {
848     DWORD style = bFlatStyle ? WS_BORDER : WS_CLIPCHILDREN|WS_DLGFRAME;
849     DWORD mask = WS_BORDER|WS_CLIPCHILDREN|WS_DLGFRAME;
850     SHSetWindowBits(m_hWnd, GWL_STYLE, mask, style);
851     return S_OK;
852 }
853 
854 extern "C"
855 HRESULT WINAPI RSHELL_CMenuDeskBar_CreateInstance(REFIID riid, LPVOID *ppv)
856 {
857     return ShellObjectCreator<CMenuDeskBar>(riid, ppv);
858 }
859