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
)
35 CMenuDeskBar
* deskbar
= new CComObject
<CMenuDeskBar
>();
40 HRESULT hr
= deskbar
->QueryInterface(riid
, ppv
);
42 if (FAILED_UNEXPECTEDLY(hr
))
48 CMenuDeskBar::CMenuDeskBar() :
55 CMenuDeskBar::~CMenuDeskBar()
59 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Initialize(THIS
)
64 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetWindow(HWND
*lphwnd
)
72 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::ContextSensitiveHelp(BOOL fEnterMode
)
77 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnFocusChangeIS(IUnknown
*punkObj
, BOOL fSetFocus
)
79 CComPtr
<IInputObjectSite
> ios
;
81 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObjectSite
, &ios
));
82 if (FAILED_UNEXPECTEDLY(hr
))
85 return ios
->OnFocusChangeIS(punkObj
, fSetFocus
);
88 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::QueryStatus(const GUID
*pguidCmdGroup
, ULONG cCmds
,
89 OLECMD prgCmds
[], OLECMDTEXT
*pCmdText
)
94 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Exec(const GUID
*pguidCmdGroup
, DWORD nCmdID
,
95 DWORD nCmdexecopt
, VARIANT
*pvaIn
, VARIANT
*pvaOut
)
97 if (IsEqualIID(*pguidCmdGroup
, CGID_MenuDeskBar
))
103 case 3: // load complete
105 case 4: // set font metrics
109 if (IsEqualIID(*pguidCmdGroup
, CGID_Explorer
))
112 else if (IsEqualIID(*pguidCmdGroup
, IID_IDeskBarClient
))
128 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::QueryService(REFGUID guidService
, REFIID riid
, void **ppvObject
)
130 if (IsEqualGUID(guidService
, SID_SMenuPopup
) ||
131 IsEqualGUID(guidService
, SID_SMenuBandParent
) ||
132 IsEqualGUID(guidService
, SID_STopLevelBrowser
))
134 return this->QueryInterface(riid
, ppvObject
);
138 return E_NOINTERFACE
;
140 return IUnknown_QueryService(m_Site
, guidService
, riid
, ppvObject
);
143 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::UIActivateIO(BOOL fActivate
, LPMSG lpMsg
)
145 return IUnknown_UIActivateIO(m_Client
, fActivate
, lpMsg
);
148 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::HasFocusIO()
150 CComPtr
<IInputObject
> io
;
152 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObject
, &io
));
153 if (FAILED_UNEXPECTEDLY(hr
))
156 return io
->HasFocusIO();
159 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::TranslateAcceleratorIO(LPMSG lpMsg
)
161 CComPtr
<IInputObject
> io
;
163 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IInputObject
, &io
));
164 if (FAILED_UNEXPECTEDLY(hr
))
167 return io
->TranslateAcceleratorIO(lpMsg
);
170 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetClient(IUnknown
*punkClient
)
172 CComPtr
<IDeskBarClient
> pDeskBandClient
;
177 if (punkClient
== NULL
)
185 hr
= punkClient
->QueryInterface(IID_PPV_ARG(IUnknown
, &m_Client
));
186 if (FAILED_UNEXPECTEDLY(hr
))
189 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &pDeskBandClient
));
190 if (FAILED_UNEXPECTEDLY(hr
))
193 hr
= pDeskBandClient
->SetDeskBarSite(static_cast<IDeskBar
*>(this));
194 if (FAILED_UNEXPECTEDLY(hr
))
197 return IUnknown_GetWindow(m_Client
, &m_ClientWindow
);
200 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetClient(IUnknown
**ppunkClient
)
202 if (ppunkClient
== NULL
)
208 return m_Client
->QueryInterface(IID_PPV_ARG(IUnknown
, ppunkClient
));
211 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnPosRectChangeDB(LPRECT prc
)
219 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSite(IUnknown
*pUnkSite
)
221 // Windows closes the bar if this is called when the bar is shown
228 IUnknown_QueryService(m_Site
, SID_SMenuPopup
, IID_PPV_ARG(IMenuPopup
, &m_SubMenuParent
));
233 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetSite(REFIID riid
, void **ppvSite
)
238 return m_Site
->QueryInterface(riid
, ppvSite
);
241 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::Popup(POINTL
*ppt
, RECTL
*prcExclude
, MP_POPUPFLAGS dwFlags
)
244 CComPtr
<IOleCommandTarget
> oct
;
245 CComPtr
<IInputObject
> io
;
246 CComPtr
<IDeskBand
> band
;
247 CComPtr
<IDeskBarClient
> dbc
;
252 hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IOleCommandTarget
, &oct
));
253 if (FAILED_UNEXPECTEDLY(hr
))
256 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
257 if (FAILED_UNEXPECTEDLY(hr
))
260 // Windows calls this, but it appears to be unimplemented?
261 hr
= dbc
->SetModeDBC(1);
262 // Allow it to fail with E_NOTIMPL.
264 // No clue about the arg, using anything != 0
265 hr
= dbc
->UIActivateDBC(TRUE
);
266 if (FAILED_UNEXPECTEDLY(hr
))
270 hr
= dbc
->GetSize(0, &rc
);
271 if (FAILED_UNEXPECTEDLY(hr
))
276 const int CMD_EXEC_OPT
= 0;
278 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
279 if (FAILED_UNEXPECTEDLY(hr
))
282 ::AdjustWindowRect(&rc
, ::GetWindowLong(m_hWnd
, GWL_STYLE
), FALSE
);
283 ::OffsetRect(&rc
, -rc
.left
, -rc
.top
);
285 if (m_Banner
!= NULL
)
288 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
289 rc
.right
+= bm
.bmWidth
;
295 GetWindowRect(GetDesktopWindow(), &rcWorkArea
);
296 int waHeight
= rcWorkArea
.bottom
- rcWorkArea
.top
;
298 switch (dwFlags
& 0xFF000000)
302 cx
= rc
.right
- rc
.left
;
303 y
= ppt
->y
- rc
.bottom
;
304 cy
= rc
.bottom
- rc
.top
;
307 x
= ppt
->x
+ rc
.left
;
308 cx
= rc
.right
- rc
.left
;
310 cy
= rc
.bottom
- rc
.top
;
312 case MPPF_TOP
| MPPF_ALIGN_LEFT
:
313 x
= ppt
->x
- rc
.right
;
314 cx
= rc
.right
- rc
.left
;
316 cy
= rc
.bottom
- rc
.top
;
318 case MPPF_TOP
| MPPF_ALIGN_RIGHT
:
320 cx
= rc
.right
- rc
.left
;
322 cy
= rc
.bottom
- rc
.top
;
326 if (x
+ cx
> rcWorkArea
.right
)
328 // FIXME: Works, but it's oversimplified.
329 x
= prcExclude
->left
- cx
;
330 dwFlags
= (dwFlags
& (~MPPF_TOP
)) | MPPF_LEFT
;
333 if (y
< rcWorkArea
.top
)
343 if (y
+ cy
> rcWorkArea
.bottom
)
345 y
= rcWorkArea
.bottom
- cy
;
350 this->SetWindowPos(HWND_TOPMOST
, x
, y
, cx
, cy
, SWP_SHOWWINDOW
);
352 m_ShowFlags
= dwFlags
;
355 // HACK: The bar needs to be notified of the size AFTER it is shown.
356 // Quick & dirty way of getting it done.
358 _OnSize(WM_SIZE
, 0, 0, bHandled
);
360 UIActivateIO(TRUE
, NULL
);
365 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetIconSize(THIS_ DWORD iIcon
)
370 // Unknown meaning (set flags? set icon size?)
372 const int CMD_EXEC_OPT
= iIcon
? 0 : 2; // seems to work
374 hr
= IUnknown_QueryServiceExec(m_Client
, SID_SMenuBandChild
, &CLSID_MenuBand
, CMD
, CMD_EXEC_OPT
, NULL
, NULL
);
375 if (FAILED_UNEXPECTEDLY(hr
))
379 _OnSize(WM_SIZE
, 0, 0, bHandled
);
384 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetIconSize(THIS_ DWORD
* piIcon
)
387 *piIcon
= m_IconSize
;
391 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetBitmap(THIS_ HBITMAP hBitmap
)
396 _OnSize(WM_SIZE
, 0, 0, bHandled
);
401 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::GetBitmap(THIS_ HBITMAP
* phBitmap
)
404 *phBitmap
= m_Banner
;
408 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::SetSubMenu(IMenuPopup
*pmp
, BOOL fSet
)
410 // Called by the MenuBand to assign itself as the logical child of the DeskBar
414 m_SubMenuChild
= pmp
;
420 if (SHIsSameObject(pmp
, m_SubMenuChild
))
422 m_SubMenuChild
= NULL
;
429 HRESULT STDMETHODCALLTYPE
CMenuDeskBar::OnSelect(DWORD dwSelectType
)
431 /* As far as I can tell, the submenu hierarchy looks like this:
433 The DeskBar's Child is the Band it contains.
434 The DeskBar's Parent is the SID_SMenuPopup of the Site.
436 The Band's Child is the IMenuPopup of the child submenu.
437 The Band's Parent is the SID_SMenuPopup of the Site (the DeskBar).
439 When the DeskBar receives a selection event:
440 If it requires closing the window, it will notify the Child (Band) using CancelLevel.
441 If it has to spread upwards (everything but CancelLevel), it will notify the Parent.
443 When the Band receives a selection event, this is where it gets fuzzy:
444 In which cases does it call the Parent? Probably not CancelLevel.
445 In which cases does it call the Child?
446 How does it react to calls?
450 switch (dwSelectType
)
453 case MPOS_FULLCANCEL
:
454 case MPOS_CANCELLEVEL
:
458 if (dwSelectType
== MPOS_CANCELLEVEL
)
461 case MPOS_SELECTLEFT
:
462 case MPOS_SELECTRIGHT
:
463 case MPOS_CHILDTRACKING
:
465 return m_SubMenuParent
->OnSelect(dwSelectType
);
472 HRESULT
CMenuDeskBar::_CloseBar()
474 CComPtr
<IDeskBarClient
> dbc
;
481 hr
= m_SubMenuChild
->OnSelect(MPOS_CANCELLEVEL
);
482 if (FAILED_UNEXPECTEDLY(hr
))
486 hr
= m_Client
->QueryInterface(IID_PPV_ARG(IDeskBarClient
, &dbc
));
487 if (FAILED_UNEXPECTEDLY(hr
))
490 hr
= dbc
->UIActivateDBC(FALSE
);
491 if (FAILED_UNEXPECTEDLY(hr
))
494 SetWindowPos(m_hWnd
, 0, 0, 0, 0, SWP_HIDEWINDOW
| SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOZORDER
);
496 return UIActivateIO(FALSE
, NULL
);
499 BOOL
CMenuDeskBar::_IsSubMenuParent(HWND hwnd
)
501 CComPtr
<IMenuPopup
> popup
= m_SubMenuParent
;
506 CComPtr
<IOleWindow
> window
;
508 hr
= popup
->QueryInterface(IID_PPV_ARG(IOleWindow
, &window
));
509 if (FAILED_UNEXPECTEDLY(hr
))
514 hr
= window
->GetWindow(&parent
);
515 if (SUCCEEDED(hr
) && hwnd
== parent
)
519 hr
= IUnknown_GetSite(window
, IID_PPV_ARG(IMenuPopup
, &popup
));
527 LRESULT
CMenuDeskBar::_OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
535 if (m_Banner
!= NULL
)
538 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
539 rc
.left
+= bm
.bmWidth
;
542 ::SetWindowPos(m_ClientWindow
, NULL
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, 0);
548 LRESULT
CMenuDeskBar::_OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
553 CComPtr
<IWinEventHandler
> winEventHandler
;
554 HRESULT hr
= m_Client
->QueryInterface(IID_PPV_ARG(IWinEventHandler
, &winEventHandler
));
555 if (FAILED_UNEXPECTEDLY(hr
))
561 hr
= winEventHandler
->OnWinEvent(NULL
, uMsg
, wParam
, lParam
, &result
);
562 if (FAILED_UNEXPECTEDLY(hr
))
570 LRESULT
CMenuDeskBar::_OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
574 if (m_Banner
&& !m_IconSize
)
578 HDC hdc
= BeginPaint(&ps
);
580 HDC hdcMem
= ::CreateCompatibleDC(hdc
);
581 HGDIOBJ hbmOld
= ::SelectObject(hdcMem
, m_Banner
);
583 ::GetObject(m_Banner
, sizeof(bm
), &bm
);
586 if (!GetClientRect(&rc
))
587 WARN("GetClientRect failed\n");
589 const int bx
= bm
.bmWidth
;
590 const int by
= bm
.bmHeight
;
591 const int cy
= rc
.bottom
;
593 TRACE("Painting banner: %d by %d\n", bm
.bmWidth
, bm
.bmHeight
);
595 if (!::StretchBlt(hdc
, 0, 0, bx
, cy
- by
, hdcMem
, 0, 0, bx
, 1, SRCCOPY
))
596 WARN("StretchBlt failed\n");
598 if (!::BitBlt(hdc
, 0, cy
- by
, bx
, by
, hdcMem
, 0, 0, SRCCOPY
))
599 WARN("BitBlt failed\n");
601 ::SelectObject(hdcMem
, hbmOld
);
610 LRESULT
CMenuDeskBar::_OnActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
615 // HACK! I just want it to work !!!
616 CComPtr
<IDeskBar
> db
;
617 HRESULT hr
= IUnknown_QueryService(m_Client
, SID_SMenuBandChild
, IID_PPV_ARG(IDeskBar
, &db
));
618 if (FAILED_UNEXPECTEDLY(hr
))
621 CComPtr
<IUnknown
> punk
;
623 hr
= db
->GetClient(&punk
);
624 if (FAILED_UNEXPECTEDLY(hr
))
627 if (!punk
&& m_Shown
)
629 if (!_IsSubMenuParent(reinterpret_cast<HWND
>(lParam
)))
631 OnSelect(MPOS_FULLCANCEL
);
638 LRESULT
CMenuDeskBar::_OnAppActivate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
642 OnSelect(MPOS_FULLCANCEL
);