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
CMenuDeskBar()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
~CMenuDeskBar()59 CMenuDeskBar::~CMenuDeskBar()
60 {
61 }
62
_OnCreate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnFinalMessage(HWND)75 void CMenuDeskBar::OnFinalMessage(HWND /* hWnd */)
76 {
77 if (m_didAddRef)
78 {
79 this->Release();
80 m_didAddRef = FALSE;
81 }
82 }
83
Initialize(THIS)84 HRESULT STDMETHODCALLTYPE CMenuDeskBar::Initialize(THIS)
85 {
86 return S_OK;
87 }
88
GetWindow(HWND * lphwnd)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
ContextSensitiveHelp(BOOL fEnterMode)97 HRESULT STDMETHODCALLTYPE CMenuDeskBar::ContextSensitiveHelp(BOOL fEnterMode)
98 {
99 return E_NOTIMPL;
100 }
101
OnFocusChangeIS(IUnknown * punkObj,BOOL fSetFocus)102 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnFocusChangeIS(IUnknown *punkObj, BOOL fSetFocus)
103 {
104 return IUnknown_OnFocusChangeIS(m_Client, punkObj, fSetFocus);
105 }
106
QueryStatus(const GUID * pguidCmdGroup,ULONG cCmds,OLECMD prgCmds[],OLECMDTEXT * pCmdText)107 HRESULT STDMETHODCALLTYPE CMenuDeskBar::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
108 OLECMD prgCmds [], OLECMDTEXT *pCmdText)
109 {
110 return E_NOTIMPL;
111 }
112
Exec(const GUID * pguidCmdGroup,DWORD nCmdID,DWORD nCmdexecopt,VARIANT * pvaIn,VARIANT * pvaOut)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
QueryService(REFGUID guidService,REFIID riid,void ** ppvObject)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
UIActivateIO(BOOL fActivate,LPMSG lpMsg)179 HRESULT STDMETHODCALLTYPE CMenuDeskBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
180 {
181 return IUnknown_UIActivateIO(m_Client, fActivate, lpMsg);
182 }
183
HasFocusIO()184 HRESULT STDMETHODCALLTYPE CMenuDeskBar::HasFocusIO()
185 {
186 return IUnknown_HasFocusIO(m_Client);
187 }
188
TranslateAcceleratorIO(LPMSG lpMsg)189 HRESULT STDMETHODCALLTYPE CMenuDeskBar::TranslateAcceleratorIO(LPMSG lpMsg)
190 {
191 return IUnknown_TranslateAcceleratorIO(m_Client, lpMsg);
192 }
193
SetClient(IUnknown * punkClient)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
GetClient(IUnknown ** ppunkClient)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
OnPosRectChangeDB(LPRECT prc)245 HRESULT STDMETHODCALLTYPE CMenuDeskBar::OnPosRectChangeDB(LPRECT prc)
246 {
247 if (prc == NULL)
248 return E_POINTER;
249
250 return S_OK;
251 }
252
SetSite(IUnknown * pUnkSite)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
GetSite(REFIID riid,void ** ppvSite)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
AdjustForExcludeArea(BOOL alignLeft,BOOL alignTop,BOOL preferVertical,PINT px,PINT py,INT cx,INT cy,RECTL rcExclude)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
Popup(POINTL * ppt,RECTL * prcExclude,MP_POPUPFLAGS dwFlags)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
SetIconSize(THIS_ DWORD iIcon)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
GetIconSize(THIS_ DWORD * piIcon)545 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetIconSize(THIS_ DWORD* piIcon)
546 {
547 if (piIcon)
548 *piIcon = m_IconSize;
549 return S_OK;
550 }
551
SetBitmap(THIS_ HBITMAP hBitmap)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
GetBitmap(THIS_ HBITMAP * phBitmap)565 HRESULT STDMETHODCALLTYPE CMenuDeskBar::GetBitmap(THIS_ HBITMAP* phBitmap)
566 {
567 if (phBitmap)
568 *phBitmap = m_Banner;
569 return S_OK;
570 }
571
SetSubMenu(IMenuPopup * pmp,BOOL fSet)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
OnSelect(DWORD dwSelectType)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
_CloseBar()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
_IsSubMenuParent(HWND hwnd)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
_OnSize(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnNotify(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnPaint(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnActivate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnMouseActivate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)794 LRESULT CMenuDeskBar::_OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
795 {
796 return MA_NOACTIVATE;
797 }
798
_OnAppActivate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnWinIniChange(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnNcPaint(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_OnClose(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
_AdjustForTheme(BOOL bFlatStyle)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"
RSHELL_CMenuDeskBar_CreateInstance(REFIID riid,LPVOID * ppv)855 HRESULT WINAPI RSHELL_CMenuDeskBar_CreateInstance(REFIID riid, LPVOID *ppv)
856 {
857 return ShellObjectCreator<CMenuDeskBar>(riid, ppv);
858 }
859