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