[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 && (entry->type == MenuBarEntry || m_current->type != TrackedMenuEntry))
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 TRACE("ProcessMouseDown %d %d %d\n", msg->message, msg->wParam, msg->lParam);
382
383 // Don't do anything if another window is capturing the mouse.
384 HWND cCapture = ::GetCapture();
385 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry)
386 return TRUE;
387
388
389 POINT pt = msg->pt;
390
391 child = WindowFromPoint(pt);
392
393 StackEntry * entry = NULL;
394 if (IsTrackedWindow(child, &entry) != S_OK)
395 return TRUE;
396
397 TRACE("MouseDown %d\n", m_isLButtonDown);
398
399 BOOL isTracking = FALSE;
400 if (entry)
401 {
402 ScreenToClient(child, &pt);
403 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
404 isTracking = entry->mb->_IsTracking();
405
406 if (iHitTestResult >= 0)
407 {
408 TRACE("MouseDown send %d\n", iHitTestResult);
409 entry->mb->_MenuBarMouseDown(child, iHitTestResult);
410 }
411 }
412
413 msg->message = WM_NULL;
414
415 m_isLButtonDown = TRUE;
416 m_movedSinceDown = FALSE;
417 m_windowAtDown = child;
418
419 TRACE("MouseDown end %d\n", m_isLButtonDown);
420
421 return TRUE;
422 }
423
424 LRESULT CMenuFocusManager::ProcessMouseUp(MSG* msg)
425 {
426 HWND child;
427 int iHitTestResult = -1;
428
429 TRACE("ProcessMouseUp %d %d %d\n", msg->message, msg->wParam, msg->lParam);
430
431 // Don't do anything if another window is capturing the mouse.
432 HWND cCapture = ::GetCapture();
433 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry)
434 return TRUE;
435
436 if (!m_isLButtonDown)
437 return TRUE;
438
439 m_isLButtonDown = FALSE;
440
441 POINT pt = msg->pt;
442
443 child = WindowFromPoint(pt);
444
445 StackEntry * entry = NULL;
446 if (IsTrackedWindow(child, &entry) != S_OK)
447 return TRUE;
448
449 TRACE("MouseUp %d\n", m_isLButtonDown);
450
451 BOOL isTracking = FALSE;
452 if (entry)
453 {
454 ScreenToClient(child, &pt);
455 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
456 isTracking = entry->mb->_IsTracking();
457
458 if (iHitTestResult >= 0)
459 {
460 TRACE("MouseUp send %d\n", iHitTestResult);
461 entry->mb->_MenuBarMouseUp(child, iHitTestResult);
462 }
463 }
464
465 return TRUE;
466 }
467
468 LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam)
469 {
470 if (nCode < 0)
471 return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
472
473 if (nCode == MSGF_MENU)
474 {
475 BOOL callNext = TRUE;
476 MSG* msg = reinterpret_cast<MSG*>(hookLParam);
477
478 switch (msg->message)
479 {
480 case WM_NCLBUTTONDOWN:
481 case WM_LBUTTONDOWN:
482 case WM_NCRBUTTONDOWN:
483 case WM_RBUTTONDOWN:
484 if (m_menuBar)
485 {
486 POINT pt = msg->pt;
487 HWND child = WindowFromPoint(pt);
488 BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK;
489 if (hoveringMenuBar)
490 {
491 m_menuBar->mb->_DisableMouseTrack(TRUE);
492 if (m_current->type == TrackedMenuEntry)
493 {
494 SendMessage(m_parent->hwnd, WM_CANCELMODE, 0, 0);
495 msg->message = WM_NULL;
496 }
497 }
498 }
499 break;
500 case WM_NCLBUTTONUP:
501 case WM_LBUTTONUP:
502 case WM_NCRBUTTONUP:
503 case WM_RBUTTONUP:
504 if (m_current && m_current->type != TrackedMenuEntry)
505 {
506 msg->message = WM_NULL;
507 }
508 break;
509 case WM_MOUSEMOVE:
510 callNext = ProcessMouseMove(msg);
511 break;
512 case WM_INITMENUPOPUP:
513 TRACE("WM_INITMENUPOPUP %p %p\n", msg->wParam, msg->lParam);
514 m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam);
515 m_selectedItem = -1;
516 m_selectedItemFlags = 0;
517 break;
518 case WM_MENUSELECT:
519 TRACE("WM_MENUSELECT %p %p\n", msg->wParam, msg->lParam);
520 m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam);
521 m_selectedItem = GET_X_LPARAM(msg->wParam);
522 m_selectedItemFlags = HIWORD(msg->wParam);
523 break;
524 case WM_KEYDOWN:
525 switch (msg->wParam)
526 {
527 case VK_LEFT:
528 if (m_current->hmenu == m_selectedMenu)
529 {
530 m_parent->mb->_MenuItemHotTrack(VK_LEFT);
531 }
532 break;
533 case VK_RIGHT:
534 if (m_selectedItem < 0 || !(m_selectedItemFlags & MF_POPUP))
535 {
536 m_parent->mb->_MenuItemHotTrack(VK_RIGHT);
537 }
538 break;
539 }
540 break;
541 }
542
543 if (!callNext)
544 return 1;
545 }
546
547 return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
548 }
549
550 LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam)
551 {
552 BOOL isLButton = FALSE;
553 if (nCode < 0)
554 return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
555
556 if (nCode == HC_ACTION)
557 {
558 BOOL callNext = TRUE;
559 MSG* msg = reinterpret_cast<MSG*>(hookLParam);
560 POINT pt = msg->pt;
561
562 switch (msg->message)
563 {
564 case WM_NCLBUTTONDOWN:
565 case WM_LBUTTONDOWN:
566 isLButton = TRUE;
567
568 // fallthrough;
569 case WM_NCRBUTTONDOWN:
570 case WM_RBUTTONDOWN:
571 if (m_current->type == MenuPopupEntry)
572 {
573 HWND child = WindowFromPoint(pt);
574
575 if (IsTrackedWindowOrParent(child) != S_OK)
576 {
577 SetCapture(NULL);
578 m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
579 break;
580 }
581 }
582
583 if (isLButton)
584 {
585 ProcessMouseDown(msg);
586 }
587 break;
588 case WM_NCLBUTTONUP:
589 case WM_LBUTTONUP:
590 ProcessMouseUp(msg);
591 break;
592 case WM_MOUSEMOVE:
593 callNext = ProcessMouseMove(msg);
594 break;
595 case WM_MOUSELEAVE:
596 callNext = ProcessMouseMove(msg);
597 //callNext = ProcessMouseLeave(msg);
598 break;
599 case WM_SYSKEYDOWN:
600 case WM_KEYDOWN:
601 if (m_current->type == MenuPopupEntry)
602 {
603 DisableMouseTrack(m_current->hwnd, TRUE);
604 switch (msg->wParam)
605 {
606 case VK_ESCAPE:
607 case VK_MENU:
608 case VK_LMENU:
609 case VK_RMENU:
610 m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
611 break;
612 case VK_RETURN:
613 m_current->mb->_MenuItemHotTrack(MPOS_EXECUTE);
614 break;
615 case VK_LEFT:
616 m_current->mb->_MenuItemHotTrack(VK_LEFT);
617 break;
618 case VK_RIGHT:
619 m_current->mb->_MenuItemHotTrack(VK_RIGHT);
620 break;
621 case VK_UP:
622 m_current->mb->_MenuItemHotTrack(VK_UP);
623 break;
624 case VK_DOWN:
625 m_current->mb->_MenuItemHotTrack(VK_DOWN);
626 break;
627 }
628 msg->message = WM_NULL;
629 msg->lParam = 0;
630 msg->wParam = 0;
631 }
632 break;
633 }
634
635 if (!callNext)
636 return 1;
637 }
638
639 return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
640 }
641
642 HRESULT CMenuFocusManager::PlaceHooks()
643 {
644 if (m_current->type == TrackedMenuEntry)
645 {
646 TRACE("Entering MSGFILTER hook...\n");
647 m_hMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, s_MsgFilterHook, NULL, m_threadId);
648 }
649 else
650 {
651 TRACE("Entering GETMESSAGE hook...\n");
652 m_hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId);
653 }
654 return S_OK;
655 }
656
657 HRESULT CMenuFocusManager::RemoveHooks()
658 {
659 TRACE("Removing all hooks...\n");
660 if (m_hMsgFilterHook)
661 UnhookWindowsHookEx(m_hMsgFilterHook);
662 if (m_hGetMsgHook)
663 UnhookWindowsHookEx(m_hGetMsgHook);
664 m_hMsgFilterHook = NULL;
665 m_hGetMsgHook = NULL;
666 return S_OK;
667 }
668
669 HRESULT CMenuFocusManager::UpdateFocus()
670 {
671 HRESULT hr;
672 StackEntry * old = m_current;
673
674 TRACE("UpdateFocus\n");
675
676 if (old)
677 SetCapture(NULL);
678
679 if (m_bandCount > 0)
680 m_current = &(m_bandStack[m_bandCount - 1]);
681 else
682 m_current = NULL;
683
684 if (m_current && m_current->type != TrackedMenuEntry)
685 {
686 hr = m_current->mb->_GetTopLevelWindow(&(m_current->hwnd));
687 if (FAILED_UNEXPECTEDLY(hr))
688 return hr;
689 }
690
691 if (m_bandCount >= 2)
692 {
693 m_parent = &(m_bandStack[m_bandCount - 2]);
694 _ASSERT(m_parent->type != TrackedMenuEntry);
695 }
696 else
697 {
698 m_parent = NULL;
699 }
700
701 if (m_bandCount >= 1 && m_bandStack[0].type == MenuBarEntry)
702 {
703 m_menuBar = &(m_bandStack[0]);
704 }
705 else
706 {
707 m_menuBar = NULL;
708 }
709
710 if (old && (!m_current || old->type != m_current->type))
711 {
712 if (m_current && m_current->type != TrackedMenuEntry)
713 {
714 DisableMouseTrack(m_current->hwnd, FALSE);
715 }
716
717 hr = RemoveHooks();
718 if (FAILED_UNEXPECTEDLY(hr))
719 return hr;
720 }
721
722 if (m_current && (!old || old->type != m_current->type))
723 {
724 hr = PlaceHooks();
725 if (FAILED_UNEXPECTEDLY(hr))
726 return hr;
727 }
728
729 if (m_parent)
730 {
731 DisableMouseTrack(m_parent->hwnd, TRUE);
732 }
733
734 if ((m_current && m_current->type == MenuPopupEntry) &&
735 (!m_parent || m_parent->type == MenuBarEntry))
736 {
737 // When the mouse moves, it should set itself to the proper band
738 SetCapture(m_current->hwnd);
739
740 if (old && old->type == TrackedMenuEntry)
741 {
742 // FIXME: Debugging code, probably not right
743 POINT pt2;
744 RECT rc2;
745 GetCursorPos(&pt2);
746 ScreenToClient(m_current->hwnd, &pt2);
747 GetClientRect(m_current->hwnd, &rc2);
748 if (PtInRect(&rc2, pt2))
749 SendMessage(m_current->hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(pt2.x, pt2.y));
750 else
751 SendMessage(m_current->hwnd, WM_MOUSELEAVE, 0, 0);
752 }
753 }
754
755 _ASSERT(!m_parent || m_parent->type != TrackedMenuEntry);
756
757 return S_OK;
758 }
759
760 HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb)
761 {
762 DbgPrint("PushMenuBar %p\n", mb);
763
764 mb->AddRef();
765
766 _ASSERT(m_bandCount == 0);
767
768 HRESULT hr = PushToArray(MenuBarEntry, mb, NULL);
769 if (FAILED_UNEXPECTEDLY(hr))
770 return hr;
771
772 return UpdateFocus();
773 }
774
775 HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb)
776 {
777 DbgPrint("PushTrackedPopup %p\n", mb);
778
779 mb->AddRef();
780
781 _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
782
783 HRESULT hr = PushToArray(MenuPopupEntry, mb, NULL);
784 if (FAILED_UNEXPECTEDLY(hr))
785 return hr;
786
787 hr = UpdateFocus();
788
789 if (m_parent && m_parent->type != TrackedMenuEntry)
790 {
791 m_parent->mb->_SetChildBand(mb);
792 mb->_SetParentBand(m_parent->mb);
793 }
794
795 return hr;
796 }
797
798 HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup)
799 {
800 DbgPrint("PushTrackedPopup %p\n", popup);
801
802 _ASSERT(m_bandCount > 0);
803 _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
804
805 HRESULT hr = PushToArray(TrackedMenuEntry, NULL, popup);
806 if (FAILED_UNEXPECTEDLY(hr))
807 return hr;
808
809 DbgPrint("PushTrackedPopup %p\n", popup);
810 m_selectedMenu = popup;
811 m_selectedItem = -1;
812 m_selectedItemFlags = 0;
813
814 return UpdateFocus();
815 }
816
817 HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb)
818 {
819 StackEntryType type;
820 CMenuBand * mbc;
821 HRESULT hr;
822
823 DbgPrint("PopMenuBar %p\n", mb);
824
825 hr = PopFromArray(&type, &mbc, NULL);
826 if (FAILED_UNEXPECTEDLY(hr))
827 {
828 UpdateFocus();
829 return hr;
830 }
831
832 _ASSERT(type == MenuBarEntry);
833 if (type != MenuBarEntry)
834 return E_FAIL;
835
836 if (!mbc)
837 return E_FAIL;
838
839 mbc->_SetParentBand(NULL);
840
841 mbc->Release();
842
843 hr = UpdateFocus();
844 if (FAILED_UNEXPECTEDLY(hr))
845 return hr;
846
847 if (m_current)
848 {
849 _ASSERT(m_current->type != TrackedMenuEntry);
850 m_current->mb->_SetChildBand(NULL);
851 }
852
853 return S_OK;
854 }
855
856 HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb)
857 {
858 StackEntryType type;
859 CMenuBand * mbc;
860 HRESULT hr;
861
862 DbgPrint("PopMenuPopup %p\n", mb);
863
864 hr = PopFromArray(&type, &mbc, NULL);
865 if (FAILED_UNEXPECTEDLY(hr))
866 {
867 UpdateFocus();
868 return hr;
869 }
870
871 _ASSERT(type == MenuPopupEntry);
872 if (type != MenuPopupEntry)
873 return E_FAIL;
874
875 if (!mbc)
876 return E_FAIL;
877
878 mbc->_SetParentBand(NULL);
879
880 mbc->Release();
881
882 hr = UpdateFocus();
883 if (FAILED_UNEXPECTEDLY(hr))
884 return hr;
885
886 if (m_current)
887 {
888 _ASSERT(m_current->type != TrackedMenuEntry);
889 m_current->mb->_SetChildBand(NULL);
890 }
891
892 return S_OK;
893 }
894
895 HRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup)
896 {
897 StackEntryType type;
898 HMENU hmenu;
899 HRESULT hr;
900
901 DbgPrint("PopTrackedPopup %p\n", popup);
902
903 hr = PopFromArray(&type, NULL, &hmenu);
904 if (FAILED_UNEXPECTEDLY(hr))
905 {
906 UpdateFocus();
907 return hr;
908 }
909
910 _ASSERT(type == TrackedMenuEntry);
911 if (type != TrackedMenuEntry)
912 return E_FAIL;
913
914 if (hmenu != popup)
915 return E_FAIL;
916
917 hr = UpdateFocus();
918 if (FAILED_UNEXPECTEDLY(hr))
919 return hr;
920
921 return S_OK;
922 }