4 * Copyright 2014 David Quintana
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.
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.
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
22 #include <shlwapi_undoc.h>
24 #include "CMenuDeskBar.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(CMenuDeskBar
);
28 const static GUID CGID_MenuDeskBar
= { 0x5C9F0A12, 0x959E, 0x11D0, { 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x08, 0x26, 0x36 } };
31 HRESULT WINAPI
CMenuDeskBar_Constructor(REFIID riid
, LPVOID
*ppv
)
33 return ShellObjectCreator
<CMenuDeskBar
>(riid
, ppv
);
36 CMenuDeskBar::CMenuDeskBar() :
46 CMenuDeskBar::~CMenuDeskBar()
50 LRESULT
CMenuDeskBar::_OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
58 void CMenuDeskBar::OnFinalMessage(HWND
/* hWnd */)
62 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Initialize(THIS
)
67 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetWindow(HWND
*lphwnd
)
75 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::ContextSensitiveHelp(BOOL fEnterMode
)
80 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnFocusChangeIS(IUnknown
*punkObj
, BOOL fSetFocus
)
82 CComPtr
<IInputObjectSite
> ios
;
84 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObjectSite
, &ios
));
85 if (FAILED_UNEXPECTEDLY(hr
))
88 return ios
->OnFocusChangeIS(punkObj
, fSetFocus
);
91 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::QueryStatus(const GUID
*pguidCmdGroup
, ULONG cCmds
,
92 OLECMD prgCmds
[], OLECMDTEXT
*pCmdText
)
97 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
,
98 DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
100 if (IsEqualIID(*pguidCmdGroup
, CGID_MenuDeskBar
))
106 case 3: // load complete
108 case 4: // set font metrics
112 if (IsEqualIID(*pguidCmdGroup
, CGID_Explorer
))
115 else if (IsEqualIID(*pguidCmdGroup
, IID_IDeskBarClient
))
131 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::QueryService(REFGUID guidService
, REFIID riid
, void **ppvObject
)
135 if (IsEqualGUID(guidService
, SID_SMenuPopup
) ||
136 IsEqualGUID(guidService
, SID_SMenuBandParent
) ||
137 IsEqualGUID(guidService
, SID_STopLevelBrowser
))
139 hr
= this->QueryInterface(riid
, ppvObject
);
144 if (IsEqualGUID(guidService
, SID_SMenuBandBottom
) ||
145 IsEqualGUID(guidService
, SID_SMenuBandBottomSelected
) ||
146 IsEqualGUID(guidService
, SID_SMenuBandChild
))
148 if (m_Client
== NULL
)
149 return E_NOINTERFACE
;
151 hr
= IUnknown_QueryService(m_Client
, guidService
, riid
, ppvObject
);
158 return E_NOINTERFACE
;
160 return IUnknown_QueryService(m_Site
, guidService
, riid
, ppvObject
);
163 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::UIActivateIO(BOOL fActivate
, LPMSG lpMsg
)
165 return IUnknown_UIActivateIO(m_Client
, fActivate
, lpMsg
);
168 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::HasFocusIO()
170 CComPtr
<IInputObject
> io
;
172 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObject
, &io
));
173 if (FAILED_UNEXPECTEDLY(hr
))
176 return io
->HasFocusIO();
179 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::TranslateAcceleratorIO(LPMSG lpMsg
)
181 CComPtr
<IInputObject
> io
;
183 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObject
, &io
));
184 if (FAILED_UNEXPECTEDLY(hr
))
187 return io
->TranslateAcceleratorIO(lpMsg
);
190 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetClient(IUnknown
*punkClient
)
192 CComPtr
<IDeskBarClient
> pDeskBandClient
;
197 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDeskBandClient
));
198 if (FAILED_UNEXPECTEDLY(hr
))
201 pDeskBandClient
->SetDeskBarSite(NULL
);
203 pDeskBandClient
= NULL
;
207 if (punkClient
== NULL
)
215 hr
= punkClient
->QueryInterface(IID_PPV_ARG(IUnknown
, &m_Client
));
216 if (FAILED_UNEXPECTEDLY(hr
))
219 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDeskBandClient
));
220 if (FAILED_UNEXPECTEDLY(hr
))
223 hr
= pDeskBandClient
->SetDeskBarSite(static_cast<IDeskBar
*>(this));
224 if (FAILED_UNEXPECTEDLY(hr
))
227 return IUnknown_GetWindow(m_Client
, &m_ClientWindow
);
230 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetClient(IUnknown
**ppunkClient
)
232 if (ppunkClient
== NULL
)
238 return m_Client
->QueryInterface(IID_PPV_ARG(IUnknown
, ppunkClient
));
241 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnPosRectChangeDB(LPRECT prc
)
249 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSite(IUnknown
*pUnkSite
)
251 // Windows closes the bar if this is called when the bar is shown
256 m_SubMenuParent
= NULL
;
262 IUnknown_QueryService(m_Site
, SID_SMenuPopup
, IID_PPV_ARG(IMenuPopup
, &m_SubMenuParent
));
273 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetSite(REFIID riid
, void **ppvSite
)
278 return m_Site
->QueryInterface(riid
, ppvSite
);
281 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Popup(POINTL
*ppt
, RECTL
*prcExclude
, MP_POPUPFLAGS dwFlags
)
284 CComPtr
<IOleCommandTarget
> oct
;
285 CComPtr
<IInputObject
> io
;
286 CComPtr
<IDeskBand
> band
;
287 CComPtr
<IDeskBarClient
> dbc
;
292 hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IOleCommandTarget
, &oct
));
293 if (FAILED_UNEXPECTEDLY(hr
))
296 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
297 if (FAILED_UNEXPECTEDLY(hr
))
300 // Windows calls this, but it appears to be unimplemented?
301 hr
= dbc
->SetModeDBC(1);
302 // Allow it to fail with E_NOTIMPL.
304 // No clue about the arg, using anything != 0
305 hr
= dbc
->UIActivateDBC(TRUE
);
306 if (FAILED_UNEXPECTEDLY(hr
))
310 hr
= dbc
->GetSize(0, &rc
);
311 if (FAILED_UNEXPECTEDLY(hr
))
316 const int CMD_EXEC_OPT
= 0;
318 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
319 if (FAILED_UNEXPECTEDLY(hr
))
322 ::AdjustWindowRect(&rc
, ::GetWindowLong(m_hWnd
, GWL_STYLE
), FALSE
);
323 ::OffsetRect(&rc
, -rc
.left
, -rc
.top
);
325 if (m_Banner
!= NULL
)
328 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
329 rc
.right
+= bm
.bmWidth
;
333 GetWindowRect(GetDesktopWindow(), &rcWorkArea
);
334 int waHeight
= rcWorkArea
.bottom
- rcWorkArea
.top
;
338 int cx
= rc
.right
- rc
.left
;
339 int cy
= rc
.bottom
- rc
.top
;
341 switch (dwFlags
& 0xFF000000)
345 y
= ppt
->y
- rc
.bottom
;
348 x
= ppt
->x
+ rc
.left
;
351 case MPPF_TOP
| MPPF_ALIGN_LEFT
:
352 x
= ppt
->x
- rc
.right
;
355 case MPPF_TOP
| MPPF_ALIGN_RIGHT
:
361 if (x
+ cx
> rcWorkArea
.right
)
363 // FIXME: Works, but it's oversimplified.
364 x
= prcExclude
->left
- cx
;
365 dwFlags
= (dwFlags
& (~MPPF_TOP
)) | MPPF_LEFT
;
368 if (y
< rcWorkArea
.top
)
378 if (y
+ cy
> rcWorkArea
.bottom
)
380 y
= rcWorkArea
.bottom
- cy
;
383 this->SetWindowPos(HWND_TOPMOST
, x
, y
, cx
, cy
, SWP_SHOWWINDOW
);
385 m_ShowFlags
= dwFlags
;
388 // HACK: The bar needs to be notified of the size AFTER it is shown.
389 // Quick & dirty way of getting it done.
391 _OnSize(WM_SIZE
, 0, 0, bHandled
);
393 UIActivateIO(TRUE
, NULL
);
395 if (dwFlags
& (MPPF_INITIALSELECT
| MPPF_FINALSELECT
))
397 const int CMD_SELECT
= 5;
398 int CMD_SELECT_OPTS
= dwFlags
& MPPF_INITIALSELECT
? 0 : -2;
399 IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD_SELECT
, CMD_SELECT_OPTS
, NULL
, NULL
);
405 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon
)
410 // Unknown meaning (set flags? set icon size?)
412 const int CMD_EXEC_OPT
= iIcon
? 0 : 2; // seems to work
414 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
415 if (FAILED_UNEXPECTEDLY(hr
))
419 _OnSize(WM_SIZE
, 0, 0, bHandled
);
424 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetIconSize(THIS_ DWORD
* piIcon
)
427 *piIcon
= m_IconSize
;
431 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap
)
436 _OnSize(WM_SIZE
, 0, 0, bHandled
);
441 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetBitmap(THIS_ HBITMAP
* phBitmap
)
444 *phBitmap
= m_Banner
;
448 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSubMenu(IMenuPopup
*pmp
, BOOL fSet
)
450 // Called by the MenuBand to assign itself as the logical child of the DeskBar
454 m_SubMenuChild
= pmp
;
460 if (pmp
== m_SubMenuChild
)
462 m_SubMenuChild
= NULL
;
469 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnSelect(DWORD dwSelectType
)
471 CComPtr
<IDeskBar
> safeThis
= this;
473 /* As far as I can tell, the submenu hierarchy looks like this:
475 * The DeskBar's Child is the Band it contains.
476 * The DeskBar's Parent is the SID_SMenuPopup of the Site.
478 * The Band's Child is the IMenuPopup of the child submenu.
479 * The Band's Parent is the SID_SMenuPopup of the Site (the DeskBar).
481 * When the DeskBar receives a selection event:
482 * If it requires closing the window, it will notify the Child (Band) using CancelLevel.
483 * If it has to spread upwards (everything but CancelLevel), it will notify the Parent.
485 * When the Band receives a selection event, this is where it gets fuzzy:
486 * In which cases does it call the Parent? Probably not CancelLevel.
487 * In which cases does it call the Child?
488 * How does it react to calls?
492 CComPtr
<IMenuPopup
> oldParent
= m_SubMenuParent
;
494 switch (dwSelectType
)
497 case MPOS_FULLCANCEL
:
498 case MPOS_CANCELLEVEL
:
502 if (dwSelectType
== MPOS_CANCELLEVEL
)
505 case MPOS_SELECTLEFT
:
506 case MPOS_SELECTRIGHT
:
507 case MPOS_CHILDTRACKING
:
509 return oldParent
->OnSelect(dwSelectType
);
516 HRESULT
CMenuDeskBar::_CloseBar()
518 CComPtr
<IDeskBar
> safeThis
= this;
519 CComPtr
<IDeskBarClient
> dbc
;
526 m_SubMenuParent
->SetSubMenu(this, FALSE
);
531 hr
= m_SubMenuChild
->OnSelect(MPOS_CANCELLEVEL
);
532 if (FAILED_UNEXPECTEDLY(hr
))
536 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
537 if (FAILED_UNEXPECTEDLY(hr
))
540 hr
= dbc
->UIActivateDBC(FALSE
);
541 if (FAILED_UNEXPECTEDLY(hr
))
544 SetWindowPos(NULL
, 0, 0, 0, 0, SWP_HIDEWINDOW
| SWP_NOACTIVATE
| SWP_NOMOVE
);
546 return UIActivateIO(FALSE
, NULL
);
549 BOOL
CMenuDeskBar::_IsSubMenuParent(HWND hwnd
)
551 CComPtr
<IMenuPopup
> popup
= m_SubMenuParent
;
556 CComPtr
<IOleWindow
> window
;
558 hr
= popup
->QueryInterface(IID_PPV_ARG(IOleWindow
, &window
));
559 if (FAILED_UNEXPECTEDLY(hr
))
564 hr
= window
->GetWindow(&parent
);
565 if (SUCCEEDED(hr
) && hwnd
== parent
)
569 hr
= IUnknown_GetSite(window
, IID_PPV_ARG(IMenuPopup
, &popup
));
577 LRESULT
CMenuDeskBar::_OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
585 if (m_Banner
!= NULL
)
588 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
589 rc
.left
+= bm
.bmWidth
;
592 ::SetWindowPos(m_ClientWindow
, NULL
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, 0);
598 LRESULT
CMenuDeskBar::_OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
603 CComPtr
<IWinEventHandler
> winEventHandler
;
604 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &winEventHandler
));
605 if (FAILED_UNEXPECTEDLY(hr
))
611 hr
= winEventHandler
->OnWinEvent(NULL
, uMsg
, wParam
, lParam
, &result
);
612 if (FAILED_UNEXPECTEDLY(hr
))
620 LRESULT
CMenuDeskBar::_OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
624 if (m_Banner
&& !m_IconSize
)
628 HDC hdc
= BeginPaint(&ps
);
630 HDC hdcMem
= ::CreateCompatibleDC(hdc
);
631 HGDIOBJ hbmOld
= ::SelectObject(hdcMem
, m_Banner
);
633 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
636 if (!GetClientRect(&rc
))
637 WARN("GetClientRect failed\n");
639 const int bx
= bm
.bmWidth
;
640 const int by
= bm
.bmHeight
;
641 const int cy
= rc
.bottom
;
643 TRACE("Painting banner: %d by %d\n", bm
.bmWidth
, bm
.bmHeight
);
645 if (!::StretchBlt(hdc
, 0, 0, bx
, cy
- by
, hdcMem
, 0, 0, bx
, 1, SRCCOPY
))
646 WARN("StretchBlt failed\n");
648 if (!::BitBlt(hdc
, 0, cy
- by
, bx
, by
, hdcMem
, 0, 0, SRCCOPY
))
649 WARN("BitBlt failed\n");
651 ::SelectObject(hdcMem
, hbmOld
);
660 LRESULT
CMenuDeskBar::_OnActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
662 // BUG in ReactOS: WM_ACTIVATE/WA_INACTIVE makes no sense with lParam==hWnd
663 if (LOWORD(wParam
) != 0 || reinterpret_cast<HWND
>(lParam
) == m_hWnd
)
668 // HACK! I just want it to work !!!
669 CComPtr
<IDeskBar
> db
;
670 HRESULT hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IDeskBar
, &db
));
671 if (FAILED_UNEXPECTEDLY(hr
))
674 CComPtr
<IUnknown
> punk
;
676 hr
= db
->GetClient(&punk
);
677 if (FAILED_UNEXPECTEDLY(hr
))
680 if (!punk
&& m_Shown
)
682 if (!_IsSubMenuParent(reinterpret_cast<HWND
>(lParam
)))
684 OnSelect(MPOS_FULLCANCEL
);
691 LRESULT
CMenuDeskBar::_OnMouseActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
693 return MA_NOACTIVATE
;
696 LRESULT
CMenuDeskBar::_OnAppActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
698 if (wParam
== 0 && m_Shown
)
700 OnSelect(MPOS_FULLCANCEL
);