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 WINE_DEFAULT_DEBUG_CHANNEL(CMenuDeskBar
);
26 const static GUID CGID_MenuDeskBar
= { 0x5C9F0A12, 0x959E, 0x11D0, { 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x08, 0x26, 0x36 } };
29 WS_POPUP
| WS_DLGFRAME
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
30 WS_EX_LEFT
| WS_EX_LTRREADING
| WS_EX_RIGHTSCROLLBAR
| WS_EX_PALETTEWINDOW
34 public CWindowImpl
<CMenuDeskBar
, CWindow
, CMenuWinTraits
>,
35 public CComCoClass
<CMenuDeskBar
>,
36 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
37 public IOleCommandTarget
,
38 public IServiceProvider
,
39 public IInputObjectSite
,
42 public IObjectWithSite
,
44 public IInitializeObject
47 CComPtr
<IUnknown
> m_Site
;
48 CComPtr
<IUnknown
> m_Client
;
49 CComPtr
<IMenuPopup
> m_SubMenuParent
;
50 CComPtr
<IMenuPopup
> m_SubMenuChild
;
65 DECLARE_NOT_AGGREGATABLE(CMenuDeskBar
)
66 DECLARE_PROTECT_FINAL_CONSTRUCT()
68 DECLARE_WND_CLASS_EX(_T("BaseBar"), CS_SAVEBITS
| CS_DROPSHADOW
, COLOR_3DFACE
)
70 BEGIN_MSG_MAP(CMenuDeskBar
)
71 MESSAGE_HANDLER(WM_SIZE
, _OnSize
)
72 MESSAGE_HANDLER(WM_NOTIFY
, _OnNotify
)
73 MESSAGE_HANDLER(WM_PAINT
, _OnPaint
)
74 MESSAGE_HANDLER(WM_ACTIVATE
, _OnActivate
)
75 MESSAGE_HANDLER(WM_ACTIVATEAPP
, _OnAppActivate
)
78 BEGIN_COM_MAP(CMenuDeskBar
)
79 COM_INTERFACE_ENTRY_IID(IID_IMenuPopup
, IMenuPopup
)
80 COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget
, IOleCommandTarget
)
81 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider
, IServiceProvider
)
82 COM_INTERFACE_ENTRY_IID(IID_IInputObjectSite
, IInputObjectSite
)
83 COM_INTERFACE_ENTRY_IID(IID_IInputObject
, IInputObject
)
84 COM_INTERFACE_ENTRY_IID(IID_IDeskBar
, IMenuPopup
)
85 COM_INTERFACE_ENTRY_IID(IID_IOleWindow
, IMenuPopup
)
86 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite
, IObjectWithSite
)
87 COM_INTERFACE_ENTRY_IID(IID_IBanneredBar
, IBanneredBar
)
88 COM_INTERFACE_ENTRY_IID(IID_IInitializeObject
, IInitializeObject
)
91 // *** IMenuPopup methods ***
92 virtual HRESULT STDMETHODCALLTYPE
Popup(POINTL
*ppt
, RECTL
*prcExclude
, MP_POPUPFLAGS dwFlags
);
93 virtual HRESULT STDMETHODCALLTYPE
OnSelect(DWORD dwSelectType
);
94 virtual HRESULT STDMETHODCALLTYPE
SetSubMenu(IMenuPopup
*pmp
, BOOL fSet
);
96 // *** IOleWindow methods ***
97 virtual HRESULT STDMETHODCALLTYPE
GetWindow(HWND
*phwnd
);
98 virtual HRESULT STDMETHODCALLTYPE
ContextSensitiveHelp(BOOL fEnterMode
);
100 // *** IObjectWithSite methods ***
101 virtual HRESULT STDMETHODCALLTYPE
SetSite(IUnknown
*pUnkSite
);
102 virtual HRESULT STDMETHODCALLTYPE
GetSite(REFIID riid
, PVOID
*ppvSite
);
104 // *** IBanneredBar methods ***
105 virtual HRESULT STDMETHODCALLTYPE
SetIconSize(DWORD iIcon
);
106 virtual HRESULT STDMETHODCALLTYPE
GetIconSize(DWORD
* piIcon
);
107 virtual HRESULT STDMETHODCALLTYPE
SetBitmap(HBITMAP hBitmap
);
108 virtual HRESULT STDMETHODCALLTYPE
GetBitmap(HBITMAP
* phBitmap
);
110 // *** IInitializeObject methods ***
111 virtual HRESULT STDMETHODCALLTYPE
Initialize(THIS
);
113 // *** IOleCommandTarget methods ***
114 virtual HRESULT STDMETHODCALLTYPE
QueryStatus(const GUID
*pguidCmdGroup
, ULONG cCmds
, OLECMD prgCmds
[], OLECMDTEXT
*pCmdText
);
115 virtual HRESULT STDMETHODCALLTYPE
Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
, DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
);
117 // *** IServiceProvider methods ***
118 virtual HRESULT STDMETHODCALLTYPE
QueryService(REFGUID guidService
, REFIID riid
, void **ppvObject
);
120 // *** IInputObjectSite methods ***
121 virtual HRESULT STDMETHODCALLTYPE
OnFocusChangeIS(LPUNKNOWN lpUnknown
, BOOL bFocus
);
123 // *** IInputObject methods ***
124 virtual HRESULT STDMETHODCALLTYPE
UIActivateIO(BOOL bActivating
, LPMSG lpMsg
);
125 virtual HRESULT STDMETHODCALLTYPE
HasFocusIO(THIS
);
126 virtual HRESULT STDMETHODCALLTYPE
TranslateAcceleratorIO(LPMSG lpMsg
);
128 // *** IDeskBar methods ***
129 virtual HRESULT STDMETHODCALLTYPE
SetClient(IUnknown
*punkClient
);
130 virtual HRESULT STDMETHODCALLTYPE
GetClient(IUnknown
**ppunkClient
);
131 virtual HRESULT STDMETHODCALLTYPE
OnPosRectChangeDB(LPRECT prc
);
135 LRESULT
_OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
);
136 LRESULT
_OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
);
137 LRESULT
_OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
);
138 LRESULT
_OnActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
);
139 LRESULT
_OnAppActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
);
141 BOOL
_IsSubMenuParent(HWND hwnd
);
146 HRESULT
CMenuDeskBar_Constructor(REFIID riid
, LPVOID
*ppv
)
150 CMenuDeskBar
* deskbar
= new CComObject
<CMenuDeskBar
>();
153 return E_OUTOFMEMORY
;
155 HRESULT hr
= deskbar
->QueryInterface(riid
, ppv
);
165 CMenuDeskBar::CMenuDeskBar() :
168 m_Level(deskBarCount
++),
173 CMenuDeskBar::~CMenuDeskBar()
178 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Initialize(THIS
)
183 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetWindow(HWND
*lphwnd
)
191 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::ContextSensitiveHelp(BOOL fEnterMode
)
196 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnFocusChangeIS(IUnknown
*punkObj
, BOOL fSetFocus
)
198 CComPtr
<IInputObjectSite
> ios
;
200 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObjectSite
, &ios
));
204 return ios
->OnFocusChangeIS(punkObj
, fSetFocus
);
207 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::QueryStatus(const GUID
*pguidCmdGroup
, ULONG cCmds
,
208 OLECMD prgCmds
[], OLECMDTEXT
*pCmdText
)
213 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
,
214 DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
216 if (IsEqualIID(*pguidCmdGroup
, CGID_MenuDeskBar
))
222 case 3: // load complete
224 case 4: // set font metrics
228 if (IsEqualIID(*pguidCmdGroup
, CGID_Explorer
))
231 else if (IsEqualIID(*pguidCmdGroup
, IID_IDeskBarClient
))
247 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::QueryService(REFGUID guidService
, REFIID riid
, void **ppvObject
)
249 if (IsEqualGUID(guidService
, SID_SMenuPopup
) ||
250 IsEqualGUID(guidService
, SID_SMenuBandParent
) ||
251 IsEqualGUID(guidService
, SID_STopLevelBrowser
))
253 return this->QueryInterface(riid
, ppvObject
);
257 return E_NOINTERFACE
;
259 return IUnknown_QueryService(m_Site
, guidService
, riid
, ppvObject
);
262 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::UIActivateIO(BOOL fActivate
, LPMSG lpMsg
)
264 return IUnknown_UIActivateIO(m_Client
, fActivate
, lpMsg
);
267 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::HasFocusIO()
269 CComPtr
<IInputObject
> io
;
271 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObject
, &io
));
275 return io
->HasFocusIO();
278 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::TranslateAcceleratorIO(LPMSG lpMsg
)
280 CComPtr
<IInputObject
> io
;
282 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObject
, &io
));
286 return io
->TranslateAcceleratorIO(lpMsg
);
289 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetClient(IUnknown
*punkClient
)
291 CComPtr
<IDeskBarClient
> pDeskBandClient
;
296 if (punkClient
== NULL
)
304 hr
= punkClient
->QueryInterface(IID_PPV_ARG(IUnknown
, &m_Client
));
308 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDeskBandClient
));
312 hr
= pDeskBandClient
->SetDeskBarSite(static_cast<IDeskBar
*>(this));
316 return IUnknown_GetWindow(m_Client
, &m_ClientWindow
);
319 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetClient(IUnknown
**ppunkClient
)
321 if (ppunkClient
== NULL
)
327 return m_Client
->QueryInterface(IID_PPV_ARG(IUnknown
, ppunkClient
));
330 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnPosRectChangeDB(LPRECT prc
)
338 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSite(IUnknown
*pUnkSite
)
340 // Windows closes the bar if this is called when the bar is shown
347 IUnknown_QueryService(m_Site
, SID_SMenuPopup
, IID_PPV_ARG(IMenuPopup
, &m_SubMenuParent
));
352 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetSite(REFIID riid
, void **ppvSite
)
357 return m_Site
->QueryInterface(riid
, ppvSite
);
360 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Popup(POINTL
*ppt
, RECTL
*prcExclude
, MP_POPUPFLAGS dwFlags
)
363 CComPtr
<IOleCommandTarget
> oct
;
364 CComPtr
<IInputObject
> io
;
365 CComPtr
<IDeskBand
> band
;
366 CComPtr
<IDeskBarClient
> dbc
;
371 hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IOleCommandTarget
, &oct
));
375 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
379 // Windows calls this, but it appears to be unimplemented?
380 hr
= dbc
->SetModeDBC(1);
381 // Allow it to fail with E_NOTIMPL.
383 // No clue about the arg, using anything != 0
384 hr
= dbc
->UIActivateDBC(TRUE
);
389 hr
= dbc
->GetSize(0, &rc
);
395 const int CMD_EXEC_OPT
= 0;
397 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
401 ::AdjustWindowRect(&rc
, ::GetWindowLong(m_hWnd
, GWL_STYLE
), FALSE
);
405 if (m_Banner
!= NULL
)
408 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
409 rc
.right
+= bm
.bmWidth
;
413 int y
= ppt
->y
- rc
.bottom
;
418 SystemParametersInfo(SPI_GETWORKAREA
, 0, &rcWorkArea
, 0);
420 int waHeight
= rcWorkArea
.bottom
- rcWorkArea
.top
;
422 if (y
< rcWorkArea
.top
)
431 else if (y
+ cy
> rcWorkArea
.bottom
)
433 y
= rcWorkArea
.bottom
- cy
;
436 this->SetWindowPos(HWND_TOPMOST
, x
, y
, cx
, cy
, SWP_SHOWWINDOW
);
440 // HACK: The bar needs to be notified of the size AFTER it is shown.
441 // Quick & dirty way of getting it done.
443 _OnSize(WM_SIZE
, 0, 0, bHandled
);
445 UIActivateIO(TRUE
, NULL
);
451 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon
)
456 // Unknown meaning (set flags? set icon size?)
458 const int CMD_EXEC_OPT
= iIcon
? 0 : 2; // seems to work
460 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
465 _OnSize(WM_SIZE
, 0, 0, bHandled
);
470 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetIconSize(THIS_ DWORD
* piIcon
)
473 *piIcon
= m_IconSize
;
477 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap
)
482 _OnSize(WM_SIZE
, 0, 0, bHandled
);
487 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetBitmap(THIS_ HBITMAP
* phBitmap
)
490 *phBitmap
= m_Banner
;
494 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSubMenu(IMenuPopup
*pmp
, BOOL fSet
)
496 // Called by the MenuBand to assign itself as the logical child of the DeskBar
500 m_SubMenuChild
= pmp
;
506 if (SHIsSameObject(pmp
, m_SubMenuChild
))
508 m_SubMenuChild
= NULL
;
515 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnSelect(DWORD dwSelectType
)
517 /* As far as I can tell, the submenu hierarchy looks like this:
519 The DeskBar's Child is the Band it contains.
520 The DeskBar's Parent is the SID_SMenuPopup of the Site.
522 The Band's Child is the IMenuPopup of the child submenu.
523 The Band's Parent is the SID_SMenuPopup of the Site (the DeskBar).
525 When the DeskBar receives a selection event:
526 If it requires closing the window, it will notify the Child (Band) using CancelLevel.
527 If it has to spread upwards (everything but CancelLevel), it will notify the Parent.
529 When the Band receives a selection event, this is where it gets fuzzy:
530 In which cases does it call the Parent? Probably not CancelLevel.
531 In which cases does it call the Child?
532 How does it react to calls?
536 switch (dwSelectType
)
539 case MPOS_FULLCANCEL
:
540 case MPOS_CANCELLEVEL
:
544 if (dwSelectType
== MPOS_CANCELLEVEL
)
547 case MPOS_SELECTLEFT
:
548 case MPOS_SELECTRIGHT
:
549 case MPOS_CHILDTRACKING
:
551 return m_SubMenuParent
->OnSelect(dwSelectType
);
558 HRESULT
CMenuDeskBar::_CloseBar()
560 CComPtr
<IDeskBarClient
> dbc
;
567 hr
= m_SubMenuChild
->OnSelect(MPOS_CANCELLEVEL
);
572 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
576 hr
= dbc
->UIActivateDBC(FALSE
);
580 SetWindowPos(m_hWnd
, 0, 0, 0, 0, SWP_HIDEWINDOW
| SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
582 return UIActivateIO(FALSE
, NULL
);
585 BOOL
CMenuDeskBar::_IsSubMenuParent(HWND hwnd
)
587 CComPtr
<IMenuPopup
> popup
= m_SubMenuParent
;
592 CComPtr
<IOleWindow
> window
;
594 hr
= popup
->QueryInterface(IID_PPV_ARG(IOleWindow
, &window
));
600 hr
= window
->GetWindow(&parent
);
601 if (SUCCEEDED(hr
) && hwnd
== parent
)
605 hr
= IUnknown_GetSite(window
, IID_PPV_ARG(IMenuPopup
, &popup
));
613 LRESULT
CMenuDeskBar::_OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
621 if (m_Banner
!= NULL
)
624 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
625 rc
.left
+= bm
.bmWidth
;
628 ::SetWindowPos(m_ClientWindow
, NULL
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, 0);
634 LRESULT
CMenuDeskBar::_OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
639 CComPtr
<IWinEventHandler
> winEventHandler
;
640 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &winEventHandler
));
647 hr
= winEventHandler
->OnWinEvent(NULL
, uMsg
, wParam
, lParam
, &result
);
656 LRESULT
CMenuDeskBar::_OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
660 if (m_Banner
&& !m_IconSize
)
664 HDC hdc
= BeginPaint(&ps
);
666 HDC hdcMem
= ::CreateCompatibleDC(hdc
);
667 HGDIOBJ hbmOld
= ::SelectObject(hdcMem
, m_Banner
);
669 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
672 if (!GetClientRect(&rc
))
673 WARN("GetClientRect failed\n");
675 const int bx
= bm
.bmWidth
;
676 const int by
= bm
.bmHeight
;
677 const int cy
= rc
.bottom
;
679 TRACE("Painting banner: %d by %d\n", bm
.bmWidth
, bm
.bmHeight
);
681 if (!::StretchBlt(hdc
, 0, 0, bx
, cy
- by
, hdcMem
, 0, 0, bx
, 1, SRCCOPY
))
682 WARN("StretchBlt failed\n");
684 if (!::BitBlt(hdc
, 0, cy
- by
, bx
, by
, hdcMem
, 0, 0, SRCCOPY
))
685 WARN("BitBlt failed\n");
687 ::SelectObject(hdcMem
, hbmOld
);
696 LRESULT
CMenuDeskBar::_OnActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
701 // HACK! I just want it to work !!!
702 CComPtr
<IDeskBar
> db
;
703 HRESULT hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IDeskBar
, &db
));
707 CComPtr
<IUnknown
> punk
;
709 hr
= db
->GetClient(&punk
);
713 if (!punk
&& m_Shown
)
715 if (!_IsSubMenuParent(reinterpret_cast<HWND
>(lParam
)))
717 OnSelect(MPOS_FULLCANCEL
);
724 LRESULT
CMenuDeskBar::_OnAppActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
728 OnSelect(MPOS_FULLCANCEL
);