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