1 /*
2  * Shell Menu Band
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 <windowsx.h>
22 #include <commoncontrols.h>
23 #include <shlwapi_undoc.h>
24 
25 #include "CMenuBand.h"
26 #include "CMenuToolbars.h"
27 #include "CMenuFocusManager.h"
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(CMenuBand);
30 
31 #undef UNIMPLEMENTED
32 
33 #define UNIMPLEMENTED TRACE("%s is UNIMPLEMENTED!\n", __FUNCTION__)
34 
35 extern "C"
36 HRESULT WINAPI CMenuBand_Constructor(REFIID riid, LPVOID *ppv)
37 {
38     return ShellObjectCreator<CMenuBand>(riid, ppv);
39 }
40 
41 CMenuBand::CMenuBand() :
42     m_staticToolbar(NULL),
43     m_SFToolbar(NULL),
44     m_site(NULL),
45     m_psmc(NULL),
46     m_subMenuChild(NULL),
47     m_subMenuParent(NULL),
48     m_childBand(NULL),
49     m_parentBand(NULL),
50     m_hmenu(NULL),
51     m_menuOwner(NULL),
52     m_useBigIcons(FALSE),
53     m_topLevelWindow(NULL),
54     m_hotBar(NULL),
55     m_hotItem(-1),
56     m_popupBar(NULL),
57     m_popupItem(-1),
58     m_Show(FALSE),
59     m_shellBottom(FALSE),
60     m_trackedPopup(NULL),
61     m_trackedHwnd(NULL)
62 {
63     m_focusManager = CMenuFocusManager::AcquireManager();
64 }
65 
66 CMenuBand::~CMenuBand()
67 {
68     CMenuFocusManager::ReleaseManager(m_focusManager);
69 
70     if (m_staticToolbar)
71         delete m_staticToolbar;
72 
73     if (m_SFToolbar)
74         delete m_SFToolbar;
75 
76     if (m_hmenu)
77         DestroyMenu(m_hmenu);
78 }
79 
80 HRESULT STDMETHODCALLTYPE  CMenuBand::Initialize(
81     IShellMenuCallback *psmc,
82     UINT uId,
83     UINT uIdAncestor,
84     DWORD dwFlags)
85 {
86     if (m_psmc != psmc)
87         m_psmc = psmc;
88     m_uId = uId;
89     m_uIdAncestor = uIdAncestor;
90     m_dwFlags = dwFlags;
91 
92     if (m_psmc)
93     {
94         _CallCB(SMC_CREATE, 0, reinterpret_cast<LPARAM>(&m_UserData));
95     }
96 
97     return S_OK;
98 }
99 
100 HRESULT STDMETHODCALLTYPE  CMenuBand::GetMenuInfo(
101     IShellMenuCallback **ppsmc,
102     UINT *puId,
103     UINT *puIdAncestor,
104     DWORD *pdwFlags)
105 {
106     if (!pdwFlags) // maybe?
107         return E_INVALIDARG;
108 
109     if (ppsmc)
110     {
111         if (m_psmc)
112             m_psmc->AddRef();
113         *ppsmc = m_psmc;
114     }
115 
116     if (puId)
117         *puId = m_uId;
118 
119     if (puIdAncestor)
120         *puIdAncestor = m_uIdAncestor;
121 
122     *pdwFlags = m_dwFlags;
123 
124     return S_OK;
125 }
126 
127 HRESULT STDMETHODCALLTYPE  CMenuBand::SetMenu(
128     HMENU hmenu,
129     HWND hwnd,
130     DWORD dwFlags)
131 {
132     HRESULT hr;
133 
134     TRACE("CMenuBand::SetMenu called, hmenu=%p; hwnd=%p, flags=%x\n", hmenu, hwnd, dwFlags);
135 
136     BOOL created = FALSE;
137 
138     if (m_hmenu && m_hmenu != hmenu)
139     {
140         DestroyMenu(m_hmenu);
141         m_hmenu = NULL;
142     }
143 
144     m_hmenu = hmenu;
145     m_menuOwner = hwnd;
146 
147     if (m_hmenu && m_staticToolbar == NULL)
148     {
149         m_staticToolbar = new CMenuStaticToolbar(this);
150         created = true;
151     }
152 
153     if (m_staticToolbar)
154     {
155         hr = m_staticToolbar->SetMenu(hmenu, hwnd, dwFlags);
156         if (FAILED_UNEXPECTEDLY(hr))
157             return hr;
158     }
159 
160     if (m_site)
161     {
162         HWND hwndParent;
163 
164         hr = m_site->GetWindow(&hwndParent);
165         if (FAILED_UNEXPECTEDLY(hr))
166             return hr;
167 
168         if (created)
169         {
170             hr = m_staticToolbar->CreateToolbar(hwndParent, m_dwFlags);
171             if (FAILED_UNEXPECTEDLY(hr))
172                 return hr;
173 
174             hr = m_staticToolbar->FillToolbar();
175         }
176         else
177         {
178             hr = m_staticToolbar->FillToolbar(TRUE);
179         }
180     }
181 
182     return hr;
183 }
184 
185 HRESULT STDMETHODCALLTYPE  CMenuBand::GetMenu(
186     HMENU *phmenu,
187     HWND *phwnd,
188     DWORD *pdwFlags)
189 {
190     if (m_staticToolbar == NULL)
191         return E_FAIL;
192 
193     return m_staticToolbar->GetMenu(phmenu, phwnd, pdwFlags);
194 }
195 
196 HRESULT STDMETHODCALLTYPE  CMenuBand::SetSite(IUnknown *pUnkSite)
197 {
198     HWND    hwndParent;
199     HRESULT hr;
200 
201     m_site = NULL;
202 
203     if (pUnkSite == NULL)
204         return S_OK;
205 
206     hwndParent = NULL;
207     hr = pUnkSite->QueryInterface(IID_PPV_ARG(IOleWindow, &m_site));
208     if (FAILED_UNEXPECTEDLY(hr))
209         return hr;
210 
211     hr = m_site->GetWindow(&hwndParent);
212     if (FAILED_UNEXPECTEDLY(hr))
213         return hr;
214 
215     if (!::IsWindow(hwndParent))
216         return E_FAIL;
217 
218     if (m_staticToolbar != NULL)
219     {
220         hr = m_staticToolbar->CreateToolbar(hwndParent, m_dwFlags);
221         if (FAILED_UNEXPECTEDLY(hr))
222             return hr;
223 
224         hr = m_staticToolbar->FillToolbar();
225         if (FAILED_UNEXPECTEDLY(hr))
226             return hr;
227     }
228 
229     if (m_SFToolbar != NULL)
230     {
231         hr = m_SFToolbar->CreateToolbar(hwndParent, m_dwFlags);
232         if (FAILED_UNEXPECTEDLY(hr))
233             return hr;
234 
235         hr = m_SFToolbar->FillToolbar();
236         if (FAILED_UNEXPECTEDLY(hr))
237             return hr;
238     }
239 
240     hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &m_subMenuParent));
241     if (hr != E_NOINTERFACE && FAILED_UNEXPECTEDLY(hr))
242         return hr;
243 
244     CComPtr<IOleWindow> pTopLevelWindow;
245     hr = IUnknown_QueryService(m_site, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pTopLevelWindow));
246     if (FAILED_UNEXPECTEDLY(hr))
247         return hr;
248 
249     return pTopLevelWindow->GetWindow(&m_topLevelWindow);
250 }
251 
252 HRESULT STDMETHODCALLTYPE  CMenuBand::GetSite(REFIID riid, PVOID *ppvSite)
253 {
254     if (m_site == NULL)
255         return E_FAIL;
256 
257     return m_site->QueryInterface(riid, ppvSite);
258 }
259 
260 HRESULT STDMETHODCALLTYPE CMenuBand::GetWindow(HWND *phwnd)
261 {
262     if (m_SFToolbar != NULL)
263         return m_SFToolbar->GetWindow(phwnd);
264 
265     if (m_staticToolbar != NULL)
266         return m_staticToolbar->GetWindow(phwnd);
267 
268     if (phwnd) *phwnd = NULL;
269 
270     return E_FAIL;
271 }
272 
273 HRESULT STDMETHODCALLTYPE CMenuBand::OnPosRectChangeDB(RECT *prc)
274 {
275     SIZE maxStatic = { 0 };
276     SIZE maxShlFld = { 0 };
277     HRESULT hr = S_OK;
278 
279     if (m_staticToolbar != NULL)
280         hr = m_staticToolbar->GetSizes(NULL, &maxStatic, NULL);
281     if (FAILED_UNEXPECTEDLY(hr))
282         return hr;
283 
284     if (m_SFToolbar != NULL)
285         hr = m_SFToolbar->GetSizes(NULL, &maxShlFld, NULL);
286     if (FAILED_UNEXPECTEDLY(hr))
287         return hr;
288 
289     if (m_staticToolbar == NULL && m_SFToolbar == NULL)
290         return E_FAIL;
291 
292     int sy = min(prc->bottom - prc->top, maxStatic.cy + maxShlFld.cy);
293 
294     int syStatic = maxStatic.cy;
295     int syShlFld = sy - syStatic;
296 
297     // TODO: Windows has a more complex system to decide ordering.
298     // Because we only support two toolbars at once, this is enough for us.
299     if (m_shellBottom)
300     {
301         // Static menu on top
302         if (m_SFToolbar)
303         {
304             m_SFToolbar->SetPosSize(
305                 prc->left,
306                 prc->top + syStatic,
307                 prc->right - prc->left,
308                 syShlFld);
309         }
310         if (m_staticToolbar)
311         {
312             m_staticToolbar->SetPosSize(
313                 prc->left,
314                 prc->top,
315                 prc->right - prc->left,
316                 syStatic);
317         }
318     }
319     else
320     {
321         // Folder menu on top
322         if (m_SFToolbar)
323         {
324             m_SFToolbar->SetPosSize(
325                 prc->left,
326                 prc->top,
327                 prc->right - prc->left,
328                 syShlFld);
329         }
330         if (m_staticToolbar)
331         {
332             m_staticToolbar->SetPosSize(
333                 prc->left,
334                 prc->top + syShlFld,
335                 prc->right - prc->left,
336                 syStatic);
337         }
338     }
339 
340     return S_OK;
341 }
342 
343 HRESULT STDMETHODCALLTYPE  CMenuBand::GetBandInfo(
344     DWORD dwBandID,
345     DWORD dwViewMode,
346     DESKBANDINFO *pdbi)
347 {
348     SIZE minStatic = { 0 };
349     SIZE minShlFld = { 0 };
350     SIZE maxStatic = { 0 };
351     SIZE maxShlFld = { 0 };
352     SIZE intStatic = { 0 };
353     SIZE intShlFld = { 0 };
354 
355     HRESULT hr = S_OK;
356 
357     if (m_staticToolbar != NULL)
358         hr = m_staticToolbar->GetSizes(&minStatic, &maxStatic, &intStatic);
359     if (FAILED_UNEXPECTEDLY(hr))
360         return hr;
361 
362     if (m_SFToolbar != NULL)
363         hr = m_SFToolbar->GetSizes(&minShlFld, &maxShlFld, &intShlFld);
364     if (FAILED_UNEXPECTEDLY(hr))
365         return hr;
366 
367     if (m_staticToolbar == NULL && m_SFToolbar == NULL)
368         return E_FAIL;
369 
370     if (m_dwFlags & SMINIT_VERTICAL)
371     {
372         pdbi->ptMinSize.x = max(minStatic.cx, minShlFld.cx) + 20;
373         pdbi->ptMinSize.y = minStatic.cy + minShlFld.cy;
374         pdbi->ptMaxSize.x = max(maxStatic.cx, maxShlFld.cx) + 20;
375         pdbi->ptMaxSize.y = maxStatic.cy + maxShlFld.cy;
376         pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
377     }
378     else
379     {
380         pdbi->ptMinSize.x = minStatic.cx + minShlFld.cx;
381         pdbi->ptMinSize.y = max(minStatic.cy, minShlFld.cy);
382         pdbi->ptMaxSize.x = maxStatic.cx + maxShlFld.cx;
383         pdbi->ptMaxSize.y = max(maxStatic.cy, maxShlFld.cy);
384     }
385     pdbi->ptIntegral.x = max(intStatic.cx, intShlFld.cx);
386     pdbi->ptIntegral.y = max(intStatic.cy, intShlFld.cy);
387     pdbi->ptActual = pdbi->ptMinSize;
388 
389     return S_OK;
390 }
391 
392 HRESULT STDMETHODCALLTYPE  CMenuBand::ShowDW(BOOL fShow)
393 {
394     HRESULT hr = S_OK;
395 
396     if (m_Show == fShow)
397         return S_OK;
398 
399     m_Show = fShow;
400 
401     if (m_staticToolbar != NULL)
402     {
403         hr = m_staticToolbar->ShowDW(fShow);
404         if (FAILED_UNEXPECTEDLY(hr))
405             return hr;
406     }
407 
408     if (m_SFToolbar != NULL)
409     {
410         hr = m_SFToolbar->ShowDW(fShow);
411         if (FAILED_UNEXPECTEDLY(hr))
412             return hr;
413     }
414 
415     if (fShow)
416     {
417         hr = _CallCB(SMC_INITMENU, 0, 0);
418         if (FAILED_UNEXPECTEDLY(hr))
419             return hr;
420     }
421     else if (m_parentBand)
422     {
423         m_parentBand->SetClient(NULL);
424     }
425 
426     if (_IsPopup() == S_OK)
427     {
428         if (fShow)
429             hr = m_focusManager->PushMenuPopup(this);
430         else
431             hr = m_focusManager->PopMenuPopup(this);
432     }
433     else
434     {
435         if (fShow)
436             hr = m_focusManager->PushMenuBar(this);
437         else
438             hr = m_focusManager->PopMenuBar(this);
439     }
440 
441     return S_OK;
442 }
443 
444 HRESULT STDMETHODCALLTYPE CMenuBand::CloseDW(DWORD dwReserved)
445 {
446     if (m_subMenuChild)
447     {
448         m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
449     }
450 
451     if (m_subMenuChild)
452     {
453         TRACE("Child object should have removed itself.\n");
454     }
455 
456     ShowDW(FALSE);
457 
458     if (m_staticToolbar != NULL)
459     {
460         m_staticToolbar->Close();
461     }
462 
463     if (m_SFToolbar != NULL)
464     {
465         m_SFToolbar->Close();
466     }
467 
468     if (m_site) m_site.Release();
469     if (m_subMenuChild) m_subMenuChild.Release();
470     if (m_subMenuParent) m_subMenuParent.Release();
471     if (m_childBand) m_childBand.Release();
472     if (m_parentBand) m_parentBand.Release();
473 
474     return S_OK;
475 }
476 
477 HRESULT STDMETHODCALLTYPE CMenuBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
478 {
479     HRESULT hr;
480 
481     if (m_subMenuParent)
482     {
483         hr = m_subMenuParent->SetSubMenu(this, fActivate);
484         if (FAILED_UNEXPECTEDLY(hr))
485             return hr;
486     }
487 
488     if (fActivate)
489     {
490         CComPtr<IOleWindow> pTopLevelWindow;
491         hr = IUnknown_QueryService(m_site, SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &pTopLevelWindow));
492         if (FAILED_UNEXPECTEDLY(hr))
493             return hr;
494 
495         hr = pTopLevelWindow->GetWindow(&m_topLevelWindow);
496         if (FAILED_UNEXPECTEDLY(hr))
497             return hr;
498     }
499     else
500     {
501         m_topLevelWindow = NULL;
502     }
503 
504     return S_FALSE;
505 }
506 
507 HRESULT STDMETHODCALLTYPE CMenuBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
508 {
509     if (!pguidCmdGroup)
510         return E_FAIL;
511 
512     if (IsEqualGUID(*pguidCmdGroup, CLSID_MenuBand))
513     {
514         if (nCmdID == 16) // set (big) icon size
515         {
516             this->m_useBigIcons = nCmdexecopt == 2;
517             return S_OK;
518         }
519         else if (nCmdID == 19) // popup-related
520         {
521             return S_FALSE;
522         }
523         else if (nCmdID == 5) // select an item
524         {
525             if (nCmdexecopt == 0) // first
526             {
527                 _KeyboardItemChange(VK_HOME);
528             }
529             else // last
530             {
531                 _KeyboardItemChange(VK_END);
532             }
533             return S_FALSE;
534         }
535 
536         return S_FALSE;
537     }
538 
539     UNIMPLEMENTED;
540     return S_OK;
541 }
542 
543 HRESULT STDMETHODCALLTYPE CMenuBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
544 {
545     if (IsEqualIID(guidService, SID_SMenuBandChild) ||
546         IsEqualIID(guidService, SID_SMenuBandBottom) ||
547         IsEqualIID(guidService, SID_SMenuBandBottomSelected))
548         return this->QueryInterface(riid, ppvObject);
549     WARN("Unknown service requested %s\n", wine_dbgstr_guid(&guidService));
550     return E_NOINTERFACE;
551 }
552 
553 HRESULT STDMETHODCALLTYPE CMenuBand::Popup(POINTL *ppt, RECTL *prcExclude, MP_POPUPFLAGS dwFlags)
554 {
555     UNIMPLEMENTED;
556     return S_OK;
557 }
558 
559 HRESULT STDMETHODCALLTYPE CMenuBand::OnSelect(DWORD dwSelectType)
560 {
561     // When called from outside, this is straightforward:
562     // Things that a submenu needs to know, are spread down, and
563     // things that the parent needs to know, are spread up. No drama.
564     // The fun is in _MenuItemSelect (internal method).
565     switch (dwSelectType)
566     {
567     case MPOS_CHILDTRACKING:
568         if (!m_subMenuParent)
569             break;
570         // TODO: Cancel timers?
571         return m_subMenuParent->OnSelect(dwSelectType);
572     case MPOS_SELECTLEFT:
573         if (m_subMenuChild)
574             m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
575         if (!m_subMenuParent)
576             break;
577         return m_subMenuParent->OnSelect(dwSelectType);
578     case MPOS_SELECTRIGHT:
579         if (!m_subMenuParent)
580             break;
581         return m_subMenuParent->OnSelect(dwSelectType);
582     case MPOS_EXECUTE:
583     case MPOS_FULLCANCEL:
584         if (m_subMenuChild)
585             m_subMenuChild->OnSelect(dwSelectType);
586         if (!m_subMenuParent)
587             break;
588         return m_subMenuParent->OnSelect(dwSelectType);
589     case MPOS_CANCELLEVEL:
590         if (m_subMenuChild)
591             m_subMenuChild->OnSelect(dwSelectType);
592         break;
593     }
594     return S_FALSE;
595 }
596 
597 HRESULT STDMETHODCALLTYPE CMenuBand::SetSubMenu(IMenuPopup *pmp, BOOL fSet)
598 {
599     UNIMPLEMENTED;
600     return S_OK;
601 }
602 
603 // Used by the focus manager to update the child band pointer
604 HRESULT CMenuBand::_SetChildBand(CMenuBand * child)
605 {
606     m_childBand = child;
607     if (!child)
608     {
609         _ChangePopupItem(NULL, -1);
610     }
611     return S_OK;
612 }
613 
614 // User by the focus manager to update the parent band pointer
615 HRESULT CMenuBand::_SetParentBand(CMenuBand * parent)
616 {
617     m_parentBand = parent;
618     return S_OK;
619 }
620 
621 HRESULT CMenuBand::_IsPopup()
622 {
623     return !(m_dwFlags & SMINIT_VERTICAL);
624 }
625 
626 HRESULT CMenuBand::_IsTracking()
627 {
628     return m_popupBar != NULL;
629 }
630 
631 HRESULT STDMETHODCALLTYPE CMenuBand::SetClient(IUnknown *punkClient)
632 {
633     CComPtr<IMenuPopup> child = m_subMenuChild;
634 
635     m_subMenuChild = NULL;
636 
637     if (child)
638     {
639         IUnknown_SetSite(child, NULL);
640         child.Release();
641     }
642 
643     if (!punkClient)
644     {
645         return S_OK;
646     }
647 
648     return punkClient->QueryInterface(IID_PPV_ARG(IMenuPopup, &m_subMenuChild));
649 }
650 
651 HRESULT STDMETHODCALLTYPE CMenuBand::GetClient(IUnknown **ppunkClient)
652 {
653     if (!ppunkClient)
654         return E_POINTER;
655     *ppunkClient = NULL;
656 
657     if (m_subMenuChild)
658     {
659         m_subMenuChild->AddRef();
660         *ppunkClient = m_subMenuChild;
661     }
662 
663     return S_OK;
664 }
665 
666 HRESULT STDMETHODCALLTYPE CMenuBand::IsMenuMessage(MSG *pmsg)
667 {
668     return S_FALSE;
669 }
670 
671 HRESULT STDMETHODCALLTYPE CMenuBand::TranslateMenuMessage(MSG *pmsg, LRESULT *plRet)
672 {
673     return S_FALSE;
674 }
675 
676 HRESULT STDMETHODCALLTYPE CMenuBand::SetShellFolder(IShellFolder *psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags)
677 {
678     if (m_SFToolbar == NULL)
679     {
680         m_SFToolbar = new CMenuSFToolbar(this);
681     }
682 
683     HRESULT hr = m_SFToolbar->SetShellFolder(psf, pidlFolder, hKey, dwFlags);
684     if (FAILED_UNEXPECTEDLY(hr))
685         return hr;
686 
687     m_shellBottom = (dwFlags & SMSET_BOTTOM) != 0;
688 
689     if (m_site)
690     {
691         HWND hwndParent;
692 
693         hr = m_site->GetWindow(&hwndParent);
694         if (FAILED_UNEXPECTEDLY(hr))
695             return hr;
696 
697         hr = m_SFToolbar->CreateToolbar(hwndParent, m_dwFlags);
698         if (FAILED_UNEXPECTEDLY(hr))
699             return hr;
700 
701         hr = m_SFToolbar->FillToolbar();
702     }
703 
704     return hr;
705 }
706 
707 HRESULT STDMETHODCALLTYPE CMenuBand::GetShellFolder(DWORD *pdwFlags, LPITEMIDLIST *ppidl, REFIID riid, void **ppv)
708 {
709     if (m_SFToolbar)
710         return m_SFToolbar->GetShellFolder(pdwFlags, ppidl, riid, ppv);
711     return E_FAIL;
712 }
713 
714 HRESULT STDMETHODCALLTYPE CMenuBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
715 {
716     *theResult = 0;
717 
718     if (uMsg == WM_WININICHANGE && wParam == SPI_SETFLATMENU)
719     {
720         BOOL bFlatMenus;
721         SystemParametersInfo(SPI_GETFLATMENU, 0, &bFlatMenus, 0);
722         AdjustForTheme(bFlatMenus);
723         return S_OK;
724     }
725 
726     if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hWnd) == S_OK)
727     {
728         return m_staticToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
729     }
730 
731     if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hWnd) == S_OK)
732     {
733         return m_SFToolbar->OnWinEvent(hWnd, uMsg, wParam, lParam, theResult);
734     }
735 
736     return S_FALSE;
737 }
738 
739 HRESULT STDMETHODCALLTYPE CMenuBand::IsWindowOwner(HWND hWnd)
740 {
741     if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hWnd) == S_OK)
742         return S_OK;
743 
744     if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hWnd) == S_OK)
745         return S_OK;
746 
747     return S_FALSE;
748 }
749 
750 HRESULT CMenuBand::_CallCBWithItemId(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam)
751 {
752     return _CallCB(uMsg, wParam, lParam, id);
753 }
754 
755 HRESULT CMenuBand::_CallCBWithItemPidl(LPITEMIDLIST pidl, UINT uMsg, WPARAM wParam, LPARAM lParam)
756 {
757     return _CallCB(uMsg, wParam, lParam, 0, pidl);
758 }
759 
760 HRESULT CMenuBand::_CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam, UINT id, LPITEMIDLIST pidl)
761 {
762     if (!m_psmc)
763         return S_FALSE;
764 
765     SMDATA smData = { 0 };
766     smData.punk = static_cast<IShellMenu2*>(this);
767     smData.uId = id;
768     smData.uIdParent = m_uId;
769     smData.uIdAncestor = m_uIdAncestor;
770     smData.pidlItem = pidl;
771     smData.hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow;
772     smData.hmenu = m_hmenu;
773     if (m_SFToolbar)
774         m_SFToolbar->GetShellFolder(NULL, &smData.pidlFolder, IID_PPV_ARG(IShellFolder, &smData.psf));
775     HRESULT hr = m_psmc->CallbackSM(&smData, uMsg, wParam, lParam);
776     ILFree(smData.pidlFolder);
777     if (smData.psf)
778         smData.psf->Release();
779     return hr;
780 }
781 
782 HRESULT CMenuBand::_TrackSubMenu(HMENU popup, INT x, INT y, RECT& rcExclude)
783 {
784     TPMPARAMS params = { sizeof(TPMPARAMS), rcExclude };
785     UINT      flags  = TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_LEFTALIGN;
786     HWND      hwnd   = m_menuOwner ? m_menuOwner : m_topLevelWindow;
787 
788     m_trackedPopup = popup;
789     m_trackedHwnd = hwnd;
790 
791     m_focusManager->PushTrackedPopup(popup);
792     ::TrackPopupMenuEx(popup, flags, x, y, hwnd, &params);
793     m_focusManager->PopTrackedPopup(popup);
794 
795     m_trackedPopup = NULL;
796     m_trackedHwnd = NULL;
797 
798     _DisableMouseTrack(FALSE);
799 
800     return S_OK;
801 }
802 
803 HRESULT CMenuBand::_TrackContextMenu(IContextMenu * contextMenu, INT x, INT y)
804 {
805     HRESULT hr;
806     UINT uCommand;
807 
808     // Ensure that the menu doesn't disappear on us
809     CComPtr<IContextMenu> ctxMenu = contextMenu;
810 
811     HMENU popup = CreatePopupMenu();
812 
813     if (popup == NULL)
814         return E_FAIL;
815 
816     TRACE("Before Query\n");
817     hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL);
818     if (FAILED_UNEXPECTEDLY(hr))
819     {
820         TRACE("Query failed\n");
821         DestroyMenu(popup);
822         return hr;
823     }
824 
825     HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow;
826 
827     m_focusManager->PushTrackedPopup(popup);
828 
829     TRACE("Before Tracking\n");
830     uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, hwnd, NULL);
831 
832     m_focusManager->PopTrackedPopup(popup);
833 
834     if (uCommand != 0)
835     {
836         _MenuItemSelect(MPOS_FULLCANCEL);
837 
838         TRACE("Before InvokeCommand\n");
839         CMINVOKECOMMANDINFO cmi = { 0 };
840         cmi.cbSize = sizeof(cmi);
841         cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
842         cmi.hwnd = hwnd;
843         hr = contextMenu->InvokeCommand(&cmi);
844         TRACE("InvokeCommand returned hr=%08x\n", hr);
845     }
846     else
847     {
848         TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError());
849         hr = S_FALSE;
850     }
851 
852     DestroyMenu(popup);
853     return hr;
854 }
855 
856 HRESULT CMenuBand::_GetTopLevelWindow(HWND*topLevel)
857 {
858     *topLevel = m_topLevelWindow;
859     return S_OK;
860 }
861 
862 HRESULT CMenuBand::_ChangeHotItem(CMenuToolbarBase * tb, INT id, DWORD dwFlags)
863 {
864     if (m_hotBar == tb && m_hotItem == id)
865         return S_FALSE;
866 
867     TRACE("Hot item changed from %p %p, to %p %p\n", m_hotBar, m_hotItem, tb, id);
868 
869     _KillPopupTimers();
870 
871     m_hotBar = tb;
872     m_hotItem = id;
873     if (m_staticToolbar) m_staticToolbar->ChangeHotItem(tb, id, dwFlags);
874     if (m_SFToolbar) m_SFToolbar->ChangeHotItem(tb, id, dwFlags);
875 
876     _MenuItemSelect(MPOS_CHILDTRACKING);
877 
878     return S_OK;
879 }
880 
881 HRESULT CMenuBand::_ChangePopupItem(CMenuToolbarBase * tb, INT id)
882 {
883     TRACE("Popup item changed from %p %p, to %p %p\n", m_popupBar, m_popupItem, tb, id);
884 
885     m_popupBar = tb;
886     m_popupItem = id;
887     if (m_staticToolbar) m_staticToolbar->ChangePopupItem(tb, id);
888     if (m_SFToolbar) m_SFToolbar->ChangePopupItem(tb, id);
889 
890     return S_OK;
891 }
892 
893 HRESULT  CMenuBand::_KeyboardItemChange(DWORD change)
894 {
895     HRESULT hr;
896     CMenuToolbarBase *tb = m_hotBar;
897 
898     if (!tb)
899     {
900         // If no hot item was selected choose the appropriate toolbar
901         if (change == VK_UP || change == VK_END)
902         {
903             if (m_staticToolbar)
904                 tb = m_staticToolbar;
905             else
906                 tb = m_SFToolbar;
907         }
908         else if (change == VK_DOWN || change == VK_HOME)
909         {
910             if (m_SFToolbar)
911                 tb = m_SFToolbar;
912             else
913                 tb = m_staticToolbar;
914         }
915     }
916 
917     // Ask the first toolbar to change
918     hr = tb->KeyboardItemChange(change);
919 
920     if (hr != S_FALSE)
921         return hr;
922 
923     // Select the second toolbar based on the first
924     if (tb == m_SFToolbar && m_staticToolbar)
925         tb = m_staticToolbar;
926     else if (m_SFToolbar)
927         tb = m_SFToolbar;
928 
929     if (!tb)
930         return hr;
931 
932     // Ask the second toolbar to change
933     return tb->KeyboardItemChange(change == VK_DOWN ? VK_HOME : VK_END);
934 }
935 
936 HRESULT CMenuBand::_MenuItemSelect(DWORD changeType)
937 {
938     // Needed to prevent the this point from vanishing mid-function
939     CComPtr<CMenuBand> safeThis = this;
940     HRESULT hr;
941 
942     if (m_dwFlags & SMINIT_VERTICAL)
943     {
944         switch (changeType)
945         {
946         case VK_UP:
947         case VK_DOWN:
948             return _KeyboardItemChange(changeType);
949 
950             // TODO: Left/Right across multi-column menus, if they ever work.
951         case VK_LEFT:
952             changeType = MPOS_SELECTLEFT;
953             break;
954         case VK_RIGHT:
955             changeType = MPOS_SELECTRIGHT;
956             break;
957         }
958     }
959     else
960     {
961         // In horizontal menubars, left/right are equivalent to vertical's up/down
962         switch (changeType)
963         {
964         case VK_LEFT:
965             hr = _KeyboardItemChange(VK_UP);
966             if (hr != S_FALSE)
967                 return hr;
968         case VK_RIGHT:
969             hr = _KeyboardItemChange(VK_DOWN);
970             if (hr != S_FALSE)
971                 return hr;
972         }
973     }
974 
975     // In this context, the parent is the CMenuDeskBar, so when it bubbles upward,
976     // it is notifying the deskbar, and not the the higher-level menu.
977     // Same for the child: since it points to a CMenuDeskBar, it's not just recursing.
978     switch (changeType)
979     {
980     case MPOS_EXECUTE:
981     {
982         CMenuToolbarBase * tb = m_hotBar;
983         int item = m_hotItem;
984         tb->PrepareExecuteItem(item);
985         if (m_subMenuParent)
986         {
987             m_subMenuParent->OnSelect(changeType);
988         }
989         TRACE("Menu closed, executing item...\n");
990         tb->ExecuteItem();
991         break;
992     }
993     case MPOS_SELECTLEFT:
994         if (m_parentBand && m_parentBand->_IsPopup()==S_FALSE)
995             return m_parentBand->_MenuItemSelect(VK_LEFT);
996         if (m_subMenuChild)
997             return m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
998         if (!m_subMenuParent)
999             return S_OK;
1000         return m_subMenuParent->OnSelect(MPOS_CANCELLEVEL);
1001 
1002     case MPOS_SELECTRIGHT:
1003         if (m_hotBar && m_hotItem >= 0 && m_hotBar->PopupItem(m_hotItem, TRUE) == S_OK)
1004             return S_FALSE;
1005         if (m_parentBand)
1006             return m_parentBand->_MenuItemSelect(VK_RIGHT);
1007         if (!m_subMenuParent)
1008             return S_OK;
1009         return m_subMenuParent->OnSelect(MPOS_SELECTRIGHT);
1010 
1011     default:
1012         if (!m_subMenuParent)
1013             return S_OK;
1014         return m_subMenuParent->OnSelect(changeType);
1015     }
1016 
1017     return S_OK;
1018 }
1019 
1020 HRESULT CMenuBand::_CancelCurrentPopup()
1021 {
1022     if (m_subMenuChild)
1023     {
1024         HRESULT hr = m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
1025         return hr;
1026     }
1027 
1028     if (m_trackedPopup)
1029     {
1030         ::SendMessage(m_trackedHwnd, WM_CANCELMODE, 0, 0);
1031         return S_OK;
1032     }
1033 
1034     return S_FALSE;
1035 }
1036 
1037 HRESULT CMenuBand::_OnPopupSubMenu(IShellMenu * childShellMenu, POINTL * pAt, RECTL * pExclude, BOOL keyInitiated)
1038 {
1039     HRESULT hr = 0;
1040     CComPtr<IBandSite> pBandSite;
1041     CComPtr<IDeskBar> pDeskBar;
1042 
1043     // Create the necessary objects
1044 #if USE_SYSTEM_MENUSITE
1045     hr = CoCreateInstance(CLSID_MenuBandSite,
1046         NULL,
1047         CLSCTX_INPROC_SERVER,
1048         IID_PPV_ARG(IBandSite, &pBandSite));
1049 #else
1050     hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
1051 #endif
1052     if (FAILED_UNEXPECTEDLY(hr))
1053         return hr;
1054 
1055 #if USE_SYSTEM_MENUDESKBAR
1056     hr = CoCreateInstance(CLSID_MenuDeskBar,
1057         NULL,
1058         CLSCTX_INPROC_SERVER,
1059         IID_PPV_ARG(IDeskBar, &pDeskBar));
1060 #else
1061     hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
1062 #endif
1063     if (FAILED_UNEXPECTEDLY(hr))
1064         return hr;
1065 
1066     hr = pDeskBar->SetClient(pBandSite);
1067     if (FAILED_UNEXPECTEDLY(hr))
1068         return hr;
1069 
1070     hr = pBandSite->AddBand(childShellMenu);
1071     if (FAILED_UNEXPECTEDLY(hr))
1072         return hr;
1073 
1074     //
1075     CComPtr<IMenuPopup> popup;
1076     hr = pDeskBar->QueryInterface(IID_PPV_ARG(IMenuPopup, &popup));
1077     if (FAILED_UNEXPECTEDLY(hr))
1078         return hr;
1079 
1080     m_subMenuChild = popup;
1081 
1082     if (m_subMenuParent)
1083         IUnknown_SetSite(popup, m_subMenuParent);
1084     else
1085         IUnknown_SetSite(popup, m_site);
1086 
1087     DWORD flags = MPPF_RIGHT;
1088 
1089     if (keyInitiated && m_dwFlags & SMINIT_VERTICAL)
1090         flags |= MPPF_INITIALSELECT;
1091 
1092     popup->Popup(pAt, pExclude, flags);
1093 
1094     return S_OK;
1095 }
1096 
1097 HRESULT CMenuBand::_BeforeCancelPopup()
1098 {
1099     if (m_staticToolbar)
1100         m_staticToolbar->BeforeCancelPopup();
1101     if (m_SFToolbar)
1102         m_SFToolbar->BeforeCancelPopup();
1103     return S_OK;
1104 }
1105 
1106 HRESULT CMenuBand::_DisableMouseTrack(BOOL bDisable)
1107 {
1108     if (m_staticToolbar)
1109         m_staticToolbar->DisableMouseTrack(bDisable);
1110     if (m_SFToolbar)
1111         m_SFToolbar->DisableMouseTrack(bDisable);
1112     return S_OK;
1113 }
1114 
1115 HRESULT CMenuBand::_KillPopupTimers()
1116 {
1117     HRESULT hr = S_OK;
1118     if (m_staticToolbar)
1119         hr = m_staticToolbar->KillPopupTimer();
1120     if (FAILED(hr))
1121         return hr;
1122 
1123     if (m_SFToolbar)
1124         hr = m_SFToolbar->KillPopupTimer();
1125 
1126     return hr;
1127 }
1128 
1129 HRESULT CMenuBand::_MenuBarMouseDown(HWND hwnd, INT item, BOOL isLButton)
1130 {
1131     if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK)
1132         m_staticToolbar->MenuBarMouseDown(item, isLButton);
1133     if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK)
1134         m_SFToolbar->MenuBarMouseDown(item, isLButton);
1135     return S_OK;
1136 }
1137 
1138 HRESULT CMenuBand::_MenuBarMouseUp(HWND hwnd, INT item)
1139 {
1140     if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK)
1141         m_staticToolbar->MenuBarMouseUp(item);
1142     if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK)
1143         m_SFToolbar->MenuBarMouseUp(item);
1144     return S_OK;
1145 }
1146 
1147 HRESULT CMenuBand::_HasSubMenu()
1148 {
1149     return m_popupBar ? S_OK : S_FALSE;
1150 }
1151 
1152 HRESULT CMenuBand::AdjustForTheme(BOOL bFlatStyle)
1153 {
1154     return IUnknown_QueryServiceExec(m_site, SID_SMenuPopup, &CGID_MenuDeskBar, 4, bFlatStyle, NULL, NULL);
1155 }
1156 
1157 HRESULT STDMETHODCALLTYPE CMenuBand::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
1158 {
1159     UNIMPLEMENTED;
1160     return S_OK;
1161 }
1162 
1163 HRESULT STDMETHODCALLTYPE CMenuBand::GetState(LPSMDATA psmd)
1164 {
1165     UNIMPLEMENTED;
1166     return S_OK;
1167 }
1168 
1169 HRESULT STDMETHODCALLTYPE CMenuBand::SetMenuToolbar(IUnknown *punk, DWORD dwFlags)
1170 {
1171     UNIMPLEMENTED;
1172     return S_OK;
1173 }
1174 
1175 HRESULT STDMETHODCALLTYPE CMenuBand::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
1176 {
1177     UNIMPLEMENTED;
1178     return S_OK;
1179 }
1180 
1181 HRESULT STDMETHODCALLTYPE CMenuBand::ContextSensitiveHelp(BOOL fEnterMode)
1182 {
1183     UNIMPLEMENTED;
1184     return S_OK;
1185 }
1186 
1187 HRESULT STDMETHODCALLTYPE CMenuBand::GetSubMenu(THIS)
1188 {
1189     UNIMPLEMENTED;
1190     return S_OK;
1191 }
1192 
1193 HRESULT STDMETHODCALLTYPE CMenuBand::SetToolbar(THIS)
1194 {
1195     UNIMPLEMENTED;
1196     return S_OK;
1197 }
1198 
1199 HRESULT STDMETHODCALLTYPE CMenuBand::SetMinWidth(THIS)
1200 {
1201     UNIMPLEMENTED;
1202     return S_OK;
1203 }
1204 
1205 HRESULT STDMETHODCALLTYPE CMenuBand::SetNoBorder(THIS)
1206 {
1207     UNIMPLEMENTED;
1208     return S_OK;
1209 }
1210 
1211 HRESULT STDMETHODCALLTYPE CMenuBand::SetTheme(THIS)
1212 {
1213     UNIMPLEMENTED;
1214     return S_OK;
1215 }
1216 
1217 HRESULT STDMETHODCALLTYPE CMenuBand::GetTop(THIS)
1218 {
1219     UNIMPLEMENTED;
1220     return S_OK;
1221 }
1222 
1223 HRESULT STDMETHODCALLTYPE CMenuBand::GetBottom(THIS)
1224 {
1225     UNIMPLEMENTED;
1226     return S_OK;
1227 }
1228 
1229 HRESULT STDMETHODCALLTYPE CMenuBand::GetTracked(THIS)
1230 {
1231     UNIMPLEMENTED;
1232     return S_OK;
1233 }
1234 
1235 HRESULT STDMETHODCALLTYPE CMenuBand::GetParentSite(THIS)
1236 {
1237     UNIMPLEMENTED;
1238     return S_OK;
1239 }
1240 
1241 HRESULT STDMETHODCALLTYPE CMenuBand::GetState(THIS)
1242 {
1243     UNIMPLEMENTED;
1244     return S_OK;
1245 }
1246 
1247 HRESULT STDMETHODCALLTYPE CMenuBand::DoDefaultAction(THIS)
1248 {
1249     UNIMPLEMENTED;
1250     return S_OK;
1251 }
1252 
1253 HRESULT STDMETHODCALLTYPE CMenuBand::IsEmpty(THIS)
1254 {
1255     UNIMPLEMENTED;
1256     return S_OK;
1257 }
1258 
1259 HRESULT STDMETHODCALLTYPE CMenuBand::HasFocusIO()
1260 {
1261     if (m_popupBar)
1262         return S_OK;
1263     return S_FALSE;
1264 }
1265 
1266 HRESULT STDMETHODCALLTYPE CMenuBand::TranslateAcceleratorIO(LPMSG lpMsg)
1267 {
1268     // TODO: Alt down -> toggle menu focus
1269     return S_FALSE;
1270 }
1271 
1272 HRESULT STDMETHODCALLTYPE CMenuBand::IsDirty()
1273 {
1274     UNIMPLEMENTED;
1275     return S_OK;
1276 }
1277 
1278 HRESULT STDMETHODCALLTYPE CMenuBand::Load(IStream *pStm)
1279 {
1280     UNIMPLEMENTED;
1281     return S_OK;
1282 }
1283 
1284 HRESULT STDMETHODCALLTYPE CMenuBand::Save(IStream *pStm, BOOL fClearDirty)
1285 {
1286     UNIMPLEMENTED;
1287     return S_OK;
1288 }
1289 
1290 HRESULT STDMETHODCALLTYPE CMenuBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
1291 {
1292     UNIMPLEMENTED;
1293     return S_OK;
1294 }
1295 
1296 HRESULT STDMETHODCALLTYPE CMenuBand::GetClassID(CLSID *pClassID)
1297 {
1298     UNIMPLEMENTED;
1299     return S_OK;
1300 }
1301 
1302 HRESULT STDMETHODCALLTYPE CMenuBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
1303 {
1304     UNIMPLEMENTED;
1305     return S_OK;
1306 }
1307