[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / shellmenu / CMenuBand.cpp
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 }