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
);
403 if (m_Banner
!= NULL
)
406 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
407 rc
.right
+= bm
.bmWidth
;
413 SystemParametersInfo(SPI_GETWORKAREA
, 0, &rcWorkArea
, 0);
414 int waHeight
= rcWorkArea
.bottom
- rcWorkArea
.top
;
416 switch (dwFlags
& MPPF_POS_MASK
)
420 x
= ppt
->x
- rc
.right
;
421 cx
= rc
.right
- rc
.left
;
425 cx
= rc
.right
- rc
.left
;
429 if (dwFlags
& MPPF_BOTTOM
)
431 y
= ppt
->y
- rc
.bottom
;
432 cy
= rc
.bottom
- rc
.top
;
437 cy
= rc
.bottom
- rc
.top
;
440 if (y
< rcWorkArea
.top
)
449 else if (y
+ cy
> rcWorkArea
.bottom
)
451 y
= rcWorkArea
.bottom
- cy
;
454 this->SetWindowPos(HWND_TOPMOST
, x
, y
, cx
, cy
, SWP_SHOWWINDOW
);
458 // HACK: The bar needs to be notified of the size AFTER it is shown.
459 // Quick & dirty way of getting it done.
461 _OnSize(WM_SIZE
, 0, 0, bHandled
);
463 UIActivateIO(TRUE
, NULL
);
469 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon
)
474 // Unknown meaning (set flags? set icon size?)
476 const int CMD_EXEC_OPT
= iIcon
? 0 : 2; // seems to work
478 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
483 _OnSize(WM_SIZE
, 0, 0, bHandled
);
488 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetIconSize(THIS_ DWORD
* piIcon
)
491 *piIcon
= m_IconSize
;
495 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap
)
500 _OnSize(WM_SIZE
, 0, 0, bHandled
);
505 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetBitmap(THIS_ HBITMAP
* phBitmap
)
508 *phBitmap
= m_Banner
;
512 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSubMenu(IMenuPopup
*pmp
, BOOL fSet
)
514 // Called by the MenuBand to assign itself as the logical child of the DeskBar
518 m_SubMenuChild
= pmp
;
524 if (SHIsSameObject(pmp
, m_SubMenuChild
))
526 m_SubMenuChild
= NULL
;
533 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnSelect(DWORD dwSelectType
)
535 /* As far as I can tell, the submenu hierarchy looks like this:
537 The DeskBar's Child is the Band it contains.
538 The DeskBar's Parent is the SID_SMenuPopup of the Site.
540 The Band's Child is the IMenuPopup of the child submenu.
541 The Band's Parent is the SID_SMenuPopup of the Site (the DeskBar).
543 When the DeskBar receives a selection event:
544 If it requires closing the window, it will notify the Child (Band) using CancelLevel.
545 If it has to spread upwards (everything but CancelLevel), it will notify the Parent.
547 When the Band receives a selection event, this is where it gets fuzzy:
548 In which cases does it call the Parent? Probably not CancelLevel.
549 In which cases does it call the Child?
550 How does it react to calls?
554 switch (dwSelectType
)
557 case MPOS_FULLCANCEL
:
558 case MPOS_CANCELLEVEL
:
562 if (dwSelectType
== MPOS_CANCELLEVEL
)
565 case MPOS_SELECTLEFT
:
566 case MPOS_SELECTRIGHT
:
567 case MPOS_CHILDTRACKING
:
569 return m_SubMenuParent
->OnSelect(dwSelectType
);
576 HRESULT
CMenuDeskBar::_CloseBar()
578 CComPtr
<IDeskBarClient
> dbc
;
585 hr
= m_SubMenuChild
->OnSelect(MPOS_CANCELLEVEL
);
590 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
594 hr
= dbc
->UIActivateDBC(FALSE
);
598 SetWindowPos(m_hWnd
, 0, 0, 0, 0, SWP_HIDEWINDOW
| SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
600 return UIActivateIO(FALSE
, NULL
);
603 BOOL
CMenuDeskBar::_IsSubMenuParent(HWND hwnd
)
605 CComPtr
<IMenuPopup
> popup
= m_SubMenuParent
;
610 CComPtr
<IOleWindow
> window
;
612 hr
= popup
->QueryInterface(IID_PPV_ARG(IOleWindow
, &window
));
618 hr
= window
->GetWindow(&parent
);
619 if (SUCCEEDED(hr
) && hwnd
== parent
)
623 hr
= IUnknown_GetSite(window
, IID_PPV_ARG(IMenuPopup
, &popup
));
631 LRESULT
CMenuDeskBar::_OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
639 if (m_Banner
!= NULL
)
642 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
643 rc
.left
+= bm
.bmWidth
;
646 ::SetWindowPos(m_ClientWindow
, NULL
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, 0);
652 LRESULT
CMenuDeskBar::_OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
657 CComPtr
<IWinEventHandler
> winEventHandler
;
658 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &winEventHandler
));
665 hr
= winEventHandler
->OnWinEvent(NULL
, uMsg
, wParam
, lParam
, &result
);
674 LRESULT
CMenuDeskBar::_OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
678 if (m_Banner
&& !m_IconSize
)
682 HDC hdc
= BeginPaint(&ps
);
684 HDC hdcMem
= ::CreateCompatibleDC(hdc
);
685 HGDIOBJ hbmOld
= ::SelectObject(hdcMem
, m_Banner
);
687 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
690 if (!GetClientRect(&rc
))
691 WARN("GetClientRect failed\n");
693 const int bx
= bm
.bmWidth
;
694 const int by
= bm
.bmHeight
;
695 const int cy
= rc
.bottom
;
697 TRACE("Painting banner: %d by %d\n", bm
.bmWidth
, bm
.bmHeight
);
699 if (!::StretchBlt(hdc
, 0, 0, bx
, cy
- by
, hdcMem
, 0, 0, bx
, 1, SRCCOPY
))
700 WARN("StretchBlt failed\n");
702 if (!::BitBlt(hdc
, 0, cy
- by
, bx
, by
, hdcMem
, 0, 0, SRCCOPY
))
703 WARN("BitBlt failed\n");
705 ::SelectObject(hdcMem
, hbmOld
);
714 LRESULT
CMenuDeskBar::_OnActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
719 // HACK! I just want it to work !!!
720 CComPtr
<IDeskBar
> db
;
721 HRESULT hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IDeskBar
, &db
));
725 CComPtr
<IUnknown
> punk
;
727 hr
= db
->GetClient(&punk
);
731 if (!punk
&& m_Shown
)
733 if (!_IsSubMenuParent(reinterpret_cast<HWND
>(lParam
)))
735 OnSelect(MPOS_FULLCANCEL
);
742 LRESULT
CMenuDeskBar::_OnAppActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
746 OnSelect(MPOS_FULLCANCEL
);