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     m_Banner = hBitmap;
555 
556     BOOL bHandled;
557     _OnSize(WM_SIZE, 0, 0, bHandled);
558 
559     return S_OK;
560 }
561 
562 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetBitmap(THIS_ HBITMAP* phBitmap)
563 {
564     if (phBitmap)
565         *phBitmap = m_Banner;
566     return S_OK;
567 }
568 
569 HRESULT STDMETHODCALLTYPE CMenuDeskBar::SetSubMenu(IMenuPopup *pmp, BOOL fSet)
570 {
571     // Called by the MenuBand to assign itself as the logical child of the DeskBar
572 
573     if (fSet)
574     {
575         m_SubMenuChild = pmp;
576     }
577     else
578     {
579         if (m_SubMenuChild)
580         {
581             if (pmp == m_SubMenuChild)
582             {
583                 m_SubMenuChild = NULL;
584             }
585         }
586     }
587     return S_OK;
588 }
589 
590 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnSelect(DWORD dwSelectType)
591 {
592     CComPtr<IDeskBar> safeThis = this;
593     CComPtr<IMenuPopup> oldParent = m_SubMenuParent;
594 
595     TRACE("OnSelect dwSelectType=%d\n", this, dwSelectType);
596     switch (dwSelectType)
597     {
598     case MPOS_EXECUTE:
599     case MPOS_FULLCANCEL:
600     case MPOS_CANCELLEVEL:
601 
602         _CloseBar();
603 
604         if (dwSelectType == MPOS_CANCELLEVEL)
605             return S_OK;
606 
607     case MPOS_SELECTLEFT:
608     case MPOS_SELECTRIGHT:
609     case MPOS_CHILDTRACKING:
610         if (oldParent)
611             return oldParent->OnSelect(dwSelectType);
612         break;
613     }
614 
615     return S_OK;
616 }
617 
618 HRESULT CMenuDeskBar::_CloseBar()
619 {
620     CComPtr<IDeskBarClient> dbc;
621     HRESULT hr;
622 
623     // Ensure that our data isn't destroyed while we are working
624     CComPtr<IDeskBar> safeThis = this;
625 
626     m_Shown = false;
627 
628     if (m_SubMenuParent)
629     {
630         m_SubMenuParent->SetSubMenu(this, FALSE);
631     }
632 
633     if (m_SubMenuChild)
634     {
635         hr = m_SubMenuChild->OnSelect(MPOS_CANCELLEVEL);
636         if (FAILED_UNEXPECTEDLY(hr))
637             return hr;
638     }
639 
640     hr = m_Client->QueryInterface(IID_PPV_ARG(IDeskBarClient, &dbc));
641     if (FAILED_UNEXPECTEDLY(hr))
642         return hr;
643 
644     hr = dbc->UIActivateDBC(FALSE);
645     if (FAILED_UNEXPECTEDLY(hr))
646         return hr;
647 
648     SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE);
649 
650     return UIActivateIO(FALSE, NULL);
651 }
652 
653 BOOL CMenuDeskBar::_IsSubMenuParent(HWND hwnd)
654 {
655     CComPtr<IMenuPopup> popup = m_SubMenuParent;
656 
657     while (popup)
658     {
659         HRESULT hr;
660         HWND parent;
661 
662         hr = IUnknown_GetWindow(popup, &parent);
663         if (FAILED_UNEXPECTEDLY(hr))
664             return FALSE;
665         if (hwnd == parent)
666             return TRUE;
667 
668         hr = IUnknown_GetSite(popup, IID_PPV_ARG(IMenuPopup, &popup));
669         if (FAILED(hr))
670             return FALSE;
671     }
672 
673     return FALSE;
674 }
675 
676 LRESULT CMenuDeskBar::_OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
677 {
678     if (m_Client)
679     {
680         RECT rc;
681 
682         GetClientRect(&rc);
683 
684         if (m_Banner != NULL)
685         {
686             BITMAP bm;
687             ::GetObject(m_Banner, sizeof(bm), &bm);
688             rc.left += bm.bmWidth;
689         }
690 
691         ::SetWindowPos(m_ClientWindow, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0);
692     }
693 
694     return 0;
695 }
696 
697 LRESULT CMenuDeskBar::_OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
698 {
699     if (!m_Client)
700         return 0;
701 
702     CComPtr<IWinEventHandler> winEventHandler;
703     HRESULT hr = m_Client->QueryInterface(IID_PPV_ARG(IWinEventHandler, &winEventHandler));
704     if (FAILED_UNEXPECTEDLY(hr))
705         return 0;
706 
707     if (winEventHandler)
708     {
709         LRESULT result;
710         hr = winEventHandler->OnWinEvent(NULL, uMsg, wParam, lParam, &result);
711         if (FAILED_UNEXPECTEDLY(hr))
712             return 0;
713         return result;
714     }
715 
716     return 0;
717 }
718 
719 LRESULT CMenuDeskBar::_OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
720 {
721     bHandled = FALSE;
722 
723     if (m_Banner && !m_IconSize)
724     {
725         BITMAP bm;
726         PAINTSTRUCT ps;
727         HDC hdc = BeginPaint(&ps);
728 
729         HDC hdcMem = ::CreateCompatibleDC(hdc);
730         HGDIOBJ hbmOld = ::SelectObject(hdcMem, m_Banner);
731 
732         ::GetObject(m_Banner, sizeof(bm), &bm);
733 
734         RECT rc;
735         if (!GetClientRect(&rc))
736             WARN("GetClientRect failed\n");
737 
738         const int bx = bm.bmWidth;
739         const int by = bm.bmHeight;
740         const int cy = rc.bottom;
741 
742         TRACE("Painting banner: %d by %d\n", bm.bmWidth, bm.bmHeight);
743 
744         if (!::StretchBlt(hdc, 0, 0, bx, cy - by, hdcMem, 0, 0, bx, 1, SRCCOPY))
745             WARN("StretchBlt failed\n");
746 
747         if (!::BitBlt(hdc, 0, cy - by, bx, by, hdcMem, 0, 0, SRCCOPY))
748             WARN("BitBlt failed\n");
749 
750         ::SelectObject(hdcMem, hbmOld);
751         ::DeleteDC(hdcMem);
752 
753         EndPaint(&ps);
754     }
755 
756     return TRUE;
757 }
758 
759 LRESULT CMenuDeskBar::_OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
760 {
761     // BUG in ReactOS: WM_ACTIVATE/WA_INACTIVE makes no sense with lParam==hWnd
762     if (LOWORD(wParam) != 0 || reinterpret_cast<HWND>(lParam) == m_hWnd)
763     {
764         return 0;
765     }
766 
767     // HACK! I just want it to work !!!
768     CComPtr<IDeskBar> db;
769     HRESULT hr = IUnknown_QueryService(m_Client, SID_SMenuBandChild, IID_PPV_ARG(IDeskBar, &db));
770     if (FAILED_UNEXPECTEDLY(hr))
771         return 0;
772 
773     CComPtr<IUnknown> punk;
774 
775     hr = db->GetClient(&punk);
776     if (FAILED_UNEXPECTEDLY(hr))
777         return 0;
778 
779     if (!punk && m_Shown)
780     {
781         if (!_IsSubMenuParent(reinterpret_cast<HWND>(lParam)))
782         {
783             OnSelect(MPOS_FULLCANCEL);
784         }
785     }
786 
787     return 0;
788 }
789 
790 LRESULT CMenuDeskBar::_OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
791 {
792     return MA_NOACTIVATE;
793 }
794 
795 LRESULT CMenuDeskBar::_OnAppActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
796 {
797 #if 0
798     if (wParam == 0 && m_Shown)
799     {
800         OnSelect(MPOS_FULLCANCEL);
801     }
802 #endif
803     return 0;
804 }
805 
806 LRESULT CMenuDeskBar::_OnWinIniChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
807 {
808     if (wParam == SPI_SETFLATMENU)
809         return _OnNotify(uMsg, wParam, lParam, bHandled);
810 
811     return 0;
812 }
813 
814 LRESULT CMenuDeskBar::_OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
815 {
816     /* If it is a flat style menu we need to handle WM_NCPAINT
817      * and paint the border with the right colour */
818     if ((GetStyle() & WS_BORDER) == 0)
819     {
820         /* This isn't a flat style menu. */
821         bHandled = FALSE;
822         return 0;
823     }
824 
825     HDC hdc;
826     RECT rcWindow;
827 
828     hdc = GetWindowDC();
829     GetWindowRect(&rcWindow);
830     OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
831     FrameRect(hdc, &rcWindow, GetSysColorBrush(COLOR_BTNSHADOW));
832     ReleaseDC(hdc);
833     return 0;
834 }
835 
836 LRESULT CMenuDeskBar::_OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
837 {
838     /* Prevent the CMenuDeskBar from destroying on being sent a WM_CLOSE */
839     return 0;
840 }
841 
842 HRESULT CMenuDeskBar::_AdjustForTheme(BOOL bFlatStyle)
843 {
844     DWORD style = bFlatStyle ? WS_BORDER : WS_CLIPCHILDREN|WS_DLGFRAME;
845     DWORD mask = WS_BORDER|WS_CLIPCHILDREN|WS_DLGFRAME;
846     SHSetWindowBits(m_hWnd, GWL_STYLE, mask, style);
847     return S_OK;
848 }
849 
850 extern "C"
851 HRESULT WINAPI RSHELL_CMenuDeskBar_CreateInstance(REFIID riid, LPVOID *ppv)
852 {
853     return ShellObjectCreator<CMenuDeskBar>(riid, ppv);
854 }
855