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