[RSHELL]
[reactos.git] / base / shell / rshell / CMenuFocusManager.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 "CMenuFocusManager.h"
26 #include "CMenuToolbars.h"
27 #include "CMenuBand.h"
28
29 #if DBG
30 # undef _ASSERT
31 # define _ASSERT(x) DbgAssert(!!(x), __FILE__, __LINE__, #x)
32
33 bool DbgAssert(bool x, const char * filename, int line, const char * expr)
34 {
35 if (!x)
36 {
37 char szMsg[512];
38 const char *fname;
39
40 fname = strrchr(filename, '\\');
41 if (fname == NULL)
42 {
43 fname = strrchr(filename, '/');
44 }
45
46 if (fname == NULL)
47 fname = filename;
48 else
49 fname++;
50
51 sprintf(szMsg, "%s:%d: Assertion failed: %s\n", fname, line, expr);
52
53 OutputDebugStringA(szMsg);
54
55 __debugbreak();
56 }
57 return x;
58 }
59 #else
60 # undef _ASSERT
61 # define _ASSERT(x) (!!(x))
62 #endif
63
64 WINE_DEFAULT_DEBUG_CHANNEL(CMenuFocus);
65
66 DWORD CMenuFocusManager::TlsIndex = 0;
67
68 CMenuFocusManager * CMenuFocusManager::GetManager()
69 {
70 return reinterpret_cast<CMenuFocusManager *>(TlsGetValue(TlsIndex));
71 }
72
73 CMenuFocusManager * CMenuFocusManager::AcquireManager()
74 {
75 CMenuFocusManager * obj = NULL;
76
77 if (!TlsIndex)
78 {
79 if ((TlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
80 return NULL;
81 }
82
83 obj = GetManager();
84
85 if (!obj)
86 {
87 obj = new CComObject<CMenuFocusManager>();
88 TlsSetValue(TlsIndex, obj);
89 }
90
91 obj->AddRef();
92
93 return obj;
94 }
95
96 void CMenuFocusManager::ReleaseManager(CMenuFocusManager * obj)
97 {
98 if (!obj->Release())
99 {
100 TlsSetValue(TlsIndex, NULL);
101 }
102 }
103
104 LRESULT CALLBACK CMenuFocusManager::s_MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam)
105 {
106 return GetManager()->MsgFilterHook(nCode, wParam, lParam);
107 }
108
109 LRESULT CALLBACK CMenuFocusManager::s_GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
110 {
111 return GetManager()->GetMsgHook(nCode, wParam, lParam);
112 }
113
114 HRESULT CMenuFocusManager::PushToArray(StackEntryType type, CMenuBand * mb, HMENU hmenu)
115 {
116 if (m_bandCount >= MAX_RECURSE)
117 return E_OUTOFMEMORY;
118
119 m_bandStack[m_bandCount].type = type;
120 m_bandStack[m_bandCount].mb = mb;
121 m_bandStack[m_bandCount].hmenu = hmenu;
122 m_bandCount++;
123
124 return S_OK;
125 }
126
127 HRESULT CMenuFocusManager::PopFromArray(StackEntryType * pType, CMenuBand ** pMb, HMENU * pHmenu)
128 {
129 if (pType) *pType = NoEntry;
130 if (pMb) *pMb = NULL;
131 if (pHmenu) *pHmenu = NULL;
132
133 if (m_bandCount <= 0)
134 return S_FALSE;
135
136 m_bandCount--;
137
138 if (pType) *pType = m_bandStack[m_bandCount].type;
139 if (*pType == TrackedMenuEntry)
140 {
141 if (pHmenu) *pHmenu = m_bandStack[m_bandCount].hmenu;
142 }
143 else
144 {
145 if (pMb) *pMb = m_bandStack[m_bandCount].mb;
146 }
147
148 return S_OK;
149 }
150
151 CMenuFocusManager::CMenuFocusManager() :
152 m_current(NULL),
153 m_parent(NULL),
154 m_hMsgFilterHook(NULL),
155 m_hGetMsgHook(NULL),
156 m_mouseTrackDisabled(FALSE),
157 m_captureHwnd(0),
158 m_hwndUnderMouse(NULL),
159 m_entryUnderMouse(NULL),
160 m_selectedMenu(NULL),
161 m_selectedItem(0),
162 m_selectedItemFlags(0),
163 m_isLButtonDown(FALSE),
164 m_movedSinceDown(FALSE),
165 m_windowAtDown(NULL),
166 m_bandCount(0)
167 {
168 m_ptPrev.x = 0;
169 m_ptPrev.y = 0;
170 m_threadId = GetCurrentThreadId();
171 }
172
173 CMenuFocusManager::~CMenuFocusManager()
174 {
175 }
176
177 void CMenuFocusManager::DisableMouseTrack(HWND parent, BOOL disableThis)
178 {
179 BOOL bDisable = FALSE;
180 BOOL lastDisable = FALSE;
181
182 int i = m_bandCount;
183 while (--i >= 0)
184 {
185 StackEntry& entry = m_bandStack[i];
186
187 if (entry.type != TrackedMenuEntry)
188 {
189 HWND hwnd;
190 HRESULT hr = entry.mb->_GetTopLevelWindow(&hwnd);
191 if (FAILED_UNEXPECTEDLY(hr))
192 break;
193
194 if (hwnd == parent)
195 {
196 lastDisable = disableThis;
197 entry.mb->_DisableMouseTrack(disableThis);
198 bDisable = TRUE;
199 }
200 else
201 {
202 lastDisable = bDisable;
203 entry.mb->_DisableMouseTrack(bDisable);
204 }
205 }
206 }
207 m_mouseTrackDisabled = lastDisable;
208 }
209
210 void CMenuFocusManager::SetCapture(HWND child)
211 {
212 if (m_captureHwnd != child)
213 {
214 if (child)
215 {
216 ::SetCapture(child);
217 m_captureHwnd = child;
218 TRACE("MouseTrack is now capturing %p\n", child);
219 }
220 else
221 {
222 ::ReleaseCapture();
223 m_captureHwnd = NULL;
224 TRACE("MouseTrack is now off\n");
225 }
226
227 }
228 }
229
230 HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd, StackEntry ** pentry)
231 {
232 if (pentry)
233 *pentry = NULL;
234
235 for (int i = m_bandCount; --i >= 0;)
236 {
237 StackEntry& entry = m_bandStack[i];
238
239 if (entry.type != TrackedMenuEntry)
240 {
241 HRESULT hr = entry.mb->IsWindowOwner(hWnd);
242 if (FAILED_UNEXPECTEDLY(hr))
243 return hr;
244 if (hr == S_OK)
245 {
246 if (pentry)
247 *pentry = &entry;
248 return S_OK;
249 }
250 }
251 }
252
253 return S_FALSE;
254 }
255
256 HRESULT CMenuFocusManager::IsTrackedWindowOrParent(HWND hWnd)
257 {
258 for (int i = m_bandCount; --i >= 0;)
259 {
260 StackEntry& entry = m_bandStack[i];
261
262 if (entry.type != TrackedMenuEntry)
263 {
264 HRESULT hr = entry.mb->IsWindowOwner(hWnd);
265 if (FAILED_UNEXPECTEDLY(hr))
266 return hr;
267 if (hr == S_OK)
268 return S_OK;
269 if (entry.mb->_IsPopup() == S_OK)
270 {
271 CComPtr<IUnknown> site;
272 CComPtr<IOleWindow> pw;
273 hr = entry.mb->GetSite(IID_PPV_ARG(IUnknown, &site));
274 if (FAILED_UNEXPECTEDLY(hr))
275 continue;
276 hr = IUnknown_QueryService(site, SID_SMenuBandParent, IID_PPV_ARG(IOleWindow, &pw));
277 if (FAILED_UNEXPECTEDLY(hr))
278 continue;
279
280 HWND hParent;
281 if (pw->GetWindow(&hParent) == S_OK && hParent == hWnd)
282 return S_OK;
283 }
284 }
285 }
286
287 return S_FALSE;
288 }
289
290 LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg)
291 {
292 HWND child;
293 int iHitTestResult = -1;
294
295 POINT pt2 = { GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam) };
296 ClientToScreen(msg->hwnd, &pt2);
297
298 // Don't do anything if the mouse has not been moved
299 POINT pt = msg->pt;
300 if (pt.x == m_ptPrev.x && pt.y == m_ptPrev.y)
301 return TRUE;
302
303 // Don't do anything if another window is capturing the mouse.
304 HWND cCapture = ::GetCapture();
305 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry)
306 return TRUE;
307
308
309 m_movedSinceDown = TRUE;
310
311 m_ptPrev = pt;
312
313 child = WindowFromPoint(pt);
314
315 StackEntry * entry = NULL;
316 if (IsTrackedWindow(child, &entry) == S_OK)
317 {
318 TRACE("MouseMove %d\n", m_isLButtonDown);
319 }
320
321 BOOL isTracking = FALSE;
322 if (entry)
323 {
324 ScreenToClient(child, &pt);
325 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
326 isTracking = entry->mb->_IsTracking();
327
328 if (SendMessage(child, WM_USER_ISTRACKEDITEM, iHitTestResult, 0) == S_FALSE)
329 {
330 TRACE("Hot item tracking detected a change (capture=%p / cCapture=%p)...\n", m_captureHwnd, cCapture);
331 DisableMouseTrack(NULL, FALSE);
332 if (isTracking && iHitTestResult >= 0 && m_current->type == TrackedMenuEntry)
333 SendMessage(entry->hwnd, WM_CANCELMODE, 0, 0);
334 PostMessage(child, WM_USER_CHANGETRACKEDITEM, iHitTestResult, MAKELPARAM(isTracking, TRUE));
335 if (m_current->type == TrackedMenuEntry)
336 return FALSE;
337 }
338 }
339
340 if (m_entryUnderMouse != entry)
341 {
342 // Mouse moved away from a tracked window
343 if (m_entryUnderMouse)
344 {
345 m_entryUnderMouse->mb->_ChangeHotItem(NULL, -1, HICF_MOUSE);
346 }
347 if (cCapture == m_captureHwnd)
348 SetCapture(NULL);
349 }
350
351 if (m_hwndUnderMouse != child)
352 {
353 if (entry)
354 {
355 // Mouse moved to a tracked window
356 if (m_current->type == MenuPopupEntry)
357 {
358 ScreenToClient(child, &pt2);
359 SendMessage(child, WM_MOUSEMOVE, msg->wParam, MAKELPARAM(pt2.x, pt2.y));
360 }
361 }
362
363 m_hwndUnderMouse = child;
364 m_entryUnderMouse = entry;
365 }
366
367 if (m_current->type == MenuPopupEntry)
368 {
369 HWND parent = GetAncestor(child, GA_ROOT);
370 DisableMouseTrack(parent, FALSE);
371 }
372
373 return TRUE;
374 }
375
376 LRESULT CMenuFocusManager::ProcessMouseDown(MSG* msg)
377 {
378 HWND child;
379 int iHitTestResult = -1;
380
381 // Don't do anything if another window is capturing the mouse.
382 HWND cCapture = ::GetCapture();
383 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry)
384 return TRUE;
385
386
387 POINT pt = msg->pt;
388
389 child = WindowFromPoint(pt);
390
391 StackEntry * entry = NULL;
392 if (IsTrackedWindow(child, &entry) != S_OK)
393 return TRUE;
394
395 TRACE("MouseDown %d\n", m_isLButtonDown);
396
397 BOOL isTracking = FALSE;
398 if (entry)
399 {
400 ScreenToClient(child, &pt);
401 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
402 isTracking = entry->mb->_IsTracking();
403
404 if (iHitTestResult >= 0)
405 {
406 TRACE("MouseDown send %d\n", iHitTestResult);
407 entry->mb->_MenuBarMouseDown(child, iHitTestResult);
408 }
409 }
410
411 msg->message = WM_NULL;
412
413 m_isLButtonDown = TRUE;
414 m_movedSinceDown = FALSE;
415 m_windowAtDown = child;
416
417 TRACE("MouseDown end %d\n", m_isLButtonDown);
418
419 return TRUE;
420 }
421
422 LRESULT CMenuFocusManager::ProcessMouseUp(MSG* msg)
423 {
424 HWND child;
425 int iHitTestResult = -1;
426
427 // Don't do anything if another window is capturing the mouse.
428 HWND cCapture = ::GetCapture();
429 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry)
430 return TRUE;
431
432 if (!m_isLButtonDown)
433 return TRUE;
434
435 m_isLButtonDown = FALSE;
436
437 POINT pt = msg->pt;
438
439 child = WindowFromPoint(pt);
440
441 StackEntry * entry = NULL;
442 if (IsTrackedWindow(child, &entry) != S_OK)
443 return TRUE;
444
445 TRACE("MouseUp %d\n", m_isLButtonDown);
446
447 BOOL isTracking = FALSE;
448 if (entry)
449 {
450 ScreenToClient(child, &pt);
451 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
452 isTracking = entry->mb->_IsTracking();
453
454 if (iHitTestResult >= 0)
455 {
456 TRACE("MouseUp send %d\n", iHitTestResult);
457 entry->mb->_MenuBarMouseUp(child, iHitTestResult);
458 }
459 }
460
461 return TRUE;
462 }
463
464 LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam)
465 {
466 if (nCode < 0)
467 return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
468
469 if (nCode == MSGF_MENU)
470 {
471 BOOL callNext = TRUE;
472 MSG* msg = reinterpret_cast<MSG*>(hookLParam);
473
474 switch (msg->message)
475 {
476 case WM_NCLBUTTONDOWN:
477 case WM_LBUTTONDOWN:
478 case WM_NCRBUTTONDOWN:
479 case WM_RBUTTONDOWN:
480 if (m_menuBar)
481 {
482 POINT pt = msg->pt;
483 HWND child = WindowFromPoint(pt);
484 BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK;
485 if (hoveringMenuBar)
486 {
487 m_menuBar->mb->_DisableMouseTrack(TRUE);
488 }
489 }
490 break;
491 case WM_MOUSEMOVE:
492 callNext = ProcessMouseMove(msg);
493 break;
494 case WM_INITMENUPOPUP:
495 TRACE("WM_INITMENUPOPUP %p %p\n", msg->wParam, msg->lParam);
496 m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam);
497 m_selectedItem = -1;
498 m_selectedItemFlags = 0;
499 break;
500 case WM_MENUSELECT:
501 TRACE("WM_MENUSELECT %p %p\n", msg->wParam, msg->lParam);
502 m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam);
503 m_selectedItem = GET_X_LPARAM(msg->wParam);
504 m_selectedItemFlags = HIWORD(msg->wParam);
505 break;
506 case WM_KEYDOWN:
507 switch (msg->wParam)
508 {
509 case VK_LEFT:
510 if (m_current->hmenu == m_selectedMenu)
511 {
512 m_parent->mb->_MenuItemHotTrack(VK_LEFT);
513 }
514 break;
515 case VK_RIGHT:
516 if (m_selectedItem < 0 || !(m_selectedItemFlags & MF_POPUP))
517 {
518 m_parent->mb->_MenuItemHotTrack(VK_RIGHT);
519 }
520 break;
521 }
522 break;
523 }
524
525 if (!callNext)
526 return 1;
527 }
528
529 return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
530 }
531
532 LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam)
533 {
534 BOOL isLButton = FALSE;
535 if (nCode < 0)
536 return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
537
538 if (nCode == HC_ACTION)
539 {
540 BOOL callNext = TRUE;
541 MSG* msg = reinterpret_cast<MSG*>(hookLParam);
542 POINT pt = msg->pt;
543
544 switch (msg->message)
545 {
546 case WM_NCLBUTTONDOWN:
547 case WM_LBUTTONDOWN:
548 isLButton = TRUE;
549
550 // fallthrough;
551 case WM_NCRBUTTONDOWN:
552 case WM_RBUTTONDOWN:
553 if (m_current->type == MenuPopupEntry)
554 {
555 HWND child = WindowFromPoint(pt);
556
557 if (IsTrackedWindowOrParent(child) != S_OK)
558 {
559 SetCapture(NULL);
560 m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
561 break;
562 }
563 }
564
565 if (isLButton)
566 {
567 ProcessMouseDown(msg);
568 }
569 break;
570 case WM_NCLBUTTONUP:
571 case WM_LBUTTONUP:
572 ProcessMouseUp(msg);
573 break;
574 case WM_MOUSEMOVE:
575 callNext = ProcessMouseMove(msg);
576 break;
577 case WM_MOUSELEAVE:
578 callNext = ProcessMouseMove(msg);
579 //callNext = ProcessMouseLeave(msg);
580 break;
581 case WM_SYSKEYDOWN:
582 case WM_KEYDOWN:
583 if (m_current->type == MenuPopupEntry)
584 {
585 DisableMouseTrack(m_current->hwnd, TRUE);
586 switch (msg->wParam)
587 {
588 case VK_ESCAPE:
589 case VK_MENU:
590 case VK_LMENU:
591 case VK_RMENU:
592 m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
593 break;
594 case VK_RETURN:
595 m_current->mb->_MenuItemHotTrack(MPOS_EXECUTE);
596 break;
597 case VK_LEFT:
598 m_current->mb->_MenuItemHotTrack(VK_LEFT);
599 break;
600 case VK_RIGHT:
601 m_current->mb->_MenuItemHotTrack(VK_RIGHT);
602 break;
603 case VK_UP:
604 m_current->mb->_MenuItemHotTrack(VK_UP);
605 break;
606 case VK_DOWN:
607 m_current->mb->_MenuItemHotTrack(VK_DOWN);
608 break;
609 }
610 msg->message = WM_NULL;
611 msg->lParam = 0;
612 msg->wParam = 0;
613 }
614 break;
615 }
616
617 if (!callNext)
618 return 1;
619 }
620
621 return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
622 }
623
624 HRESULT CMenuFocusManager::PlaceHooks()
625 {
626 if (m_current->type == TrackedMenuEntry)
627 {
628 TRACE("Entering MSGFILTER hook...\n");
629 m_hMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, s_MsgFilterHook, NULL, m_threadId);
630 }
631 else
632 {
633 TRACE("Entering GETMESSAGE hook...\n");
634 m_hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId);
635 }
636 return S_OK;
637 }
638
639 HRESULT CMenuFocusManager::RemoveHooks()
640 {
641 TRACE("Removing all hooks...\n");
642 if (m_hMsgFilterHook)
643 UnhookWindowsHookEx(m_hMsgFilterHook);
644 if (m_hGetMsgHook)
645 UnhookWindowsHookEx(m_hGetMsgHook);
646 m_hMsgFilterHook = NULL;
647 m_hGetMsgHook = NULL;
648 return S_OK;
649 }
650
651 HRESULT CMenuFocusManager::UpdateFocus()
652 {
653 HRESULT hr;
654 StackEntry * old = m_current;
655
656 if (old)
657 SetCapture(NULL);
658
659 if (m_bandCount > 0)
660 m_current = &(m_bandStack[m_bandCount - 1]);
661 else
662 m_current = NULL;
663
664 if (m_current && m_current->type != TrackedMenuEntry)
665 {
666 hr = m_current->mb->_GetTopLevelWindow(&(m_current->hwnd));
667 if (FAILED_UNEXPECTEDLY(hr))
668 return hr;
669 }
670
671 if (m_bandCount >= 2)
672 {
673 m_parent = &(m_bandStack[m_bandCount - 2]);
674 _ASSERT(m_parent->type != TrackedMenuEntry);
675 }
676 else
677 {
678 m_parent = NULL;
679 }
680
681 if (m_bandCount >= 1 && m_bandStack[0].type == MenuBarEntry)
682 {
683 m_menuBar = &(m_bandStack[0]);
684 }
685 else
686 {
687 m_menuBar = NULL;
688 }
689
690 if (old && (!m_current || old->type != m_current->type))
691 {
692 if (m_current && m_current->type != TrackedMenuEntry)
693 {
694 DisableMouseTrack(m_current->hwnd, FALSE);
695 }
696
697 hr = RemoveHooks();
698 if (FAILED_UNEXPECTEDLY(hr))
699 return hr;
700 }
701
702 if (m_current && (!old || old->type != m_current->type))
703 {
704 hr = PlaceHooks();
705 if (FAILED_UNEXPECTEDLY(hr))
706 return hr;
707 }
708
709 if (m_parent)
710 {
711 DisableMouseTrack(m_parent->hwnd, TRUE);
712 }
713
714 if ((m_current && m_current->type == MenuPopupEntry) &&
715 (!m_parent || m_parent->type == MenuBarEntry))
716 {
717 // When the mouse moves, it should set itself to the proper band
718 SetCapture(m_current->hwnd);
719
720 if (old && old->type == TrackedMenuEntry)
721 {
722 // FIXME: Debugging code, probably not right
723 POINT pt2;
724 RECT rc2;
725 GetCursorPos(&pt2);
726 ScreenToClient(m_current->hwnd, &pt2);
727 GetClientRect(m_current->hwnd, &rc2);
728 if (PtInRect(&rc2, pt2))
729 SendMessage(m_current->hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(pt2.x, pt2.y));
730 else
731 SendMessage(m_current->hwnd, WM_MOUSELEAVE, 0, 0);
732 }
733 }
734
735 _ASSERT(!m_parent || m_parent->type != TrackedMenuEntry);
736
737 return S_OK;
738 }
739
740 HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb)
741 {
742 _ASSERT(m_bandCount == 0);
743
744 HRESULT hr = PushToArray(MenuBarEntry, mb, NULL);
745 if (FAILED_UNEXPECTEDLY(hr))
746 return hr;
747
748 return UpdateFocus();
749 }
750
751 HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb)
752 {
753 _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
754
755 HRESULT hr = PushToArray(MenuPopupEntry, mb, NULL);
756 if (FAILED_UNEXPECTEDLY(hr))
757 return hr;
758
759 hr = UpdateFocus();
760
761 if (m_parent && m_parent->type != TrackedMenuEntry)
762 {
763 m_parent->mb->_SetChildBand(mb);
764 mb->_SetParentBand(m_parent->mb);
765 }
766
767 return hr;
768 }
769
770 HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup)
771 {
772 _ASSERT(m_bandCount > 0);
773 _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
774
775 HRESULT hr = PushToArray(TrackedMenuEntry, NULL, popup);
776 if (FAILED_UNEXPECTEDLY(hr))
777 return hr;
778
779 TRACE("PushTrackedPopup %p\n", popup);
780 m_selectedMenu = popup;
781 m_selectedItem = -1;
782 m_selectedItemFlags = 0;
783
784 return UpdateFocus();
785 }
786
787 HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb)
788 {
789 StackEntryType type;
790 CMenuBand * mbc;
791 HRESULT hr;
792
793 hr = PopFromArray(&type, &mbc, NULL);
794 if (FAILED_UNEXPECTEDLY(hr))
795 {
796 UpdateFocus();
797 return hr;
798 }
799
800 _ASSERT(type == MenuBarEntry);
801 if (type != MenuBarEntry)
802 return E_FAIL;
803
804 if (!mbc)
805 return E_FAIL;
806
807 mbc->_SetParentBand(NULL);
808
809 hr = UpdateFocus();
810 if (FAILED_UNEXPECTEDLY(hr))
811 return hr;
812
813 if (m_current)
814 {
815 _ASSERT(m_current->type != TrackedMenuEntry);
816 m_current->mb->_SetChildBand(NULL);
817 }
818
819 return S_OK;
820 }
821
822 HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb)
823 {
824 StackEntryType type;
825 CMenuBand * mbc;
826 HRESULT hr;
827
828 hr = PopFromArray(&type, &mbc, NULL);
829 if (FAILED_UNEXPECTEDLY(hr))
830 {
831 UpdateFocus();
832 return hr;
833 }
834
835 _ASSERT(type == MenuPopupEntry);
836 if (type != MenuPopupEntry)
837 return E_FAIL;
838
839 if (!mbc)
840 return E_FAIL;
841
842 mbc->_SetParentBand(NULL);
843
844 hr = UpdateFocus();
845 if (FAILED_UNEXPECTEDLY(hr))
846 return hr;
847
848 if (m_current)
849 {
850 _ASSERT(m_current->type != TrackedMenuEntry);
851 m_current->mb->_SetChildBand(NULL);
852 }
853
854 return S_OK;
855 }
856
857 HRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup)
858 {
859 StackEntryType type;
860 HMENU hmenu;
861 HRESULT hr;
862
863 hr = PopFromArray(&type, NULL, &hmenu);
864 if (FAILED_UNEXPECTEDLY(hr))
865 {
866 UpdateFocus();
867 return hr;
868 }
869
870 _ASSERT(type == TrackedMenuEntry);
871 if (type != TrackedMenuEntry)
872 return E_FAIL;
873
874 if (hmenu != popup)
875 return E_FAIL;
876
877 hr = UpdateFocus();
878 if (FAILED_UNEXPECTEDLY(hr))
879 return hr;
880
881 return S_OK;
882 }