* Sync up to trunk head (r65074).
[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_hMsgFilterHook)
645 {
646 WARN("GETMESSAGE hook already placed!\n");
647 return S_OK;
648 }
649 if (m_hMsgFilterHook)
650 {
651 WARN("MSGFILTER hook already placed!\n");
652 return S_OK;
653 }
654 if (m_current->type == TrackedMenuEntry)
655 {
656 TRACE("Entering MSGFILTER hook...\n");
657 m_hMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, s_MsgFilterHook, NULL, m_threadId);
658 }
659 else
660 {
661 TRACE("Entering GETMESSAGE hook...\n");
662 m_hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId);
663 }
664 return S_OK;
665 }
666
667 HRESULT CMenuFocusManager::RemoveHooks()
668 {
669 TRACE("Removing all hooks...\n");
670 if (m_hMsgFilterHook)
671 UnhookWindowsHookEx(m_hMsgFilterHook);
672 if (m_hGetMsgHook)
673 UnhookWindowsHookEx(m_hGetMsgHook);
674 m_hMsgFilterHook = NULL;
675 m_hGetMsgHook = NULL;
676 return S_OK;
677 }
678
679 HRESULT CMenuFocusManager::UpdateFocus()
680 {
681 HRESULT hr;
682 StackEntry * old = m_current;
683
684 TRACE("UpdateFocus\n");
685
686 if (old)
687 SetCapture(NULL);
688
689 if (m_bandCount > 0)
690 m_current = &(m_bandStack[m_bandCount - 1]);
691 else
692 m_current = NULL;
693
694 if (m_current && m_current->type != TrackedMenuEntry)
695 {
696 hr = m_current->mb->_GetTopLevelWindow(&(m_current->hwnd));
697 if (FAILED_UNEXPECTEDLY(hr))
698 return hr;
699 }
700
701 if (m_bandCount >= 2)
702 {
703 m_parent = &(m_bandStack[m_bandCount - 2]);
704 _ASSERT(m_parent->type != TrackedMenuEntry);
705 }
706 else
707 {
708 m_parent = NULL;
709 }
710
711 if (m_bandCount >= 1 && m_bandStack[0].type == MenuBarEntry)
712 {
713 m_menuBar = &(m_bandStack[0]);
714 }
715 else
716 {
717 m_menuBar = NULL;
718 }
719
720 if (old && (!m_current || old->type != m_current->type))
721 {
722 if (m_current && m_current->type != TrackedMenuEntry)
723 {
724 DisableMouseTrack(m_current->hwnd, FALSE);
725 }
726
727 hr = RemoveHooks();
728 if (FAILED_UNEXPECTEDLY(hr))
729 return hr;
730 }
731
732 if (m_current && (!old || old->type != m_current->type))
733 {
734 hr = PlaceHooks();
735 if (FAILED_UNEXPECTEDLY(hr))
736 return hr;
737 }
738
739 if (m_parent)
740 {
741 DisableMouseTrack(m_parent->hwnd, TRUE);
742 }
743
744 if ((m_current && m_current->type == MenuPopupEntry) &&
745 (!m_parent || m_parent->type == MenuBarEntry))
746 {
747 // When the mouse moves, it should set itself to the proper band
748 SetCapture(m_current->hwnd);
749
750 if (old && old->type == TrackedMenuEntry)
751 {
752 // FIXME: Debugging code, probably not right
753 POINT pt2;
754 RECT rc2;
755 GetCursorPos(&pt2);
756 ScreenToClient(m_current->hwnd, &pt2);
757 GetClientRect(m_current->hwnd, &rc2);
758 if (PtInRect(&rc2, pt2))
759 SendMessage(m_current->hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(pt2.x, pt2.y));
760 else
761 SendMessage(m_current->hwnd, WM_MOUSELEAVE, 0, 0);
762 }
763 }
764
765 _ASSERT(!m_parent || m_parent->type != TrackedMenuEntry);
766
767 return S_OK;
768 }
769
770 HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb)
771 {
772 DbgPrint("PushMenuBar %p\n", mb);
773
774 mb->AddRef();
775
776 _ASSERT(m_bandCount == 0);
777
778 HRESULT hr = PushToArray(MenuBarEntry, mb, NULL);
779 if (FAILED_UNEXPECTEDLY(hr))
780 return hr;
781
782 return UpdateFocus();
783 }
784
785 HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb)
786 {
787 DbgPrint("PushTrackedPopup %p\n", mb);
788
789 mb->AddRef();
790
791 _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
792
793 HRESULT hr = PushToArray(MenuPopupEntry, mb, NULL);
794 if (FAILED_UNEXPECTEDLY(hr))
795 return hr;
796
797 hr = UpdateFocus();
798
799 if (m_parent && m_parent->type != TrackedMenuEntry)
800 {
801 m_parent->mb->_SetChildBand(mb);
802 mb->_SetParentBand(m_parent->mb);
803 }
804
805 return hr;
806 }
807
808 HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup)
809 {
810 DbgPrint("PushTrackedPopup %p\n", popup);
811
812 _ASSERT(m_bandCount > 0);
813 _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
814
815 HRESULT hr = PushToArray(TrackedMenuEntry, NULL, popup);
816 if (FAILED_UNEXPECTEDLY(hr))
817 return hr;
818
819 DbgPrint("PushTrackedPopup %p\n", popup);
820 m_selectedMenu = popup;
821 m_selectedItem = -1;
822 m_selectedItemFlags = 0;
823
824 return UpdateFocus();
825 }
826
827 HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb)
828 {
829 StackEntryType type;
830 CMenuBand * mbc;
831 HRESULT hr;
832
833 DbgPrint("PopMenuBar %p\n", mb);
834
835 if (m_current == m_entryUnderMouse)
836 {
837 m_entryUnderMouse = NULL;
838 }
839
840 hr = PopFromArray(&type, &mbc, NULL);
841 if (FAILED_UNEXPECTEDLY(hr))
842 {
843 UpdateFocus();
844 return hr;
845 }
846
847 _ASSERT(type == MenuBarEntry);
848 if (type != MenuBarEntry)
849 return E_FAIL;
850
851 if (!mbc)
852 return E_FAIL;
853
854 mbc->_SetParentBand(NULL);
855
856 mbc->Release();
857
858 hr = UpdateFocus();
859 if (FAILED_UNEXPECTEDLY(hr))
860 return hr;
861
862 if (m_current)
863 {
864 _ASSERT(m_current->type != TrackedMenuEntry);
865 m_current->mb->_SetChildBand(NULL);
866 }
867
868 return S_OK;
869 }
870
871 HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb)
872 {
873 StackEntryType type;
874 CMenuBand * mbc;
875 HRESULT hr;
876
877 DbgPrint("PopMenuPopup %p\n", mb);
878
879 if (m_current == m_entryUnderMouse)
880 {
881 m_entryUnderMouse = NULL;
882 }
883
884 hr = PopFromArray(&type, &mbc, NULL);
885 if (FAILED_UNEXPECTEDLY(hr))
886 {
887 UpdateFocus();
888 return hr;
889 }
890
891 _ASSERT(type == MenuPopupEntry);
892 if (type != MenuPopupEntry)
893 return E_FAIL;
894
895 if (!mbc)
896 return E_FAIL;
897
898 mbc->_SetParentBand(NULL);
899
900 mbc->Release();
901
902 hr = UpdateFocus();
903 if (FAILED_UNEXPECTEDLY(hr))
904 return hr;
905
906 if (m_current)
907 {
908 _ASSERT(m_current->type != TrackedMenuEntry);
909 m_current->mb->_SetChildBand(NULL);
910 }
911
912 return S_OK;
913 }
914
915 HRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup)
916 {
917 StackEntryType type;
918 HMENU hmenu;
919 HRESULT hr;
920
921 DbgPrint("PopTrackedPopup %p\n", popup);
922
923 hr = PopFromArray(&type, NULL, &hmenu);
924 if (FAILED_UNEXPECTEDLY(hr))
925 {
926 UpdateFocus();
927 return hr;
928 }
929
930 _ASSERT(type == TrackedMenuEntry);
931 if (type != TrackedMenuEntry)
932 return E_FAIL;
933
934 if (hmenu != popup)
935 return E_FAIL;
936
937 hr = UpdateFocus();
938 if (FAILED_UNEXPECTEDLY(hr))
939 return hr;
940
941 return S_OK;
942 }