Reactos
at listview 1060 lines 28 kB view raw
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 21/* 22This file implements the CMenuFocusManager class. 23 24This class manages the shell menus, by overriding the hot-tracking behaviour. 25 26For the shell menus, it uses a GetMessage hook, 27where it intercepts messages directed to the menu windows. 28 29In order to show submenus using system popups, it also has a MessageFilter hook. 30 31The menu is tracked using a stack structure. When a CMenuBand wants to open a submenu, 32it pushes the submenu band, or HMENU to track in case of system popups, 33and when the menu has closed, it pops the same pointer or handle. 34 35While a shell menu is open, it overrides the menu toolbar's hottracking behaviour, 36using its own logic to track both the active menu item, and the opened submenu's parent item. 37 38While a system popup is open, it tracks the mouse movements so that it can cancel the popup, 39and switch to another submenu when the mouse goes over another item from the parent. 40 41*/ 42#include "shellmenu.h" 43#include <windowsx.h> 44#include <commoncontrols.h> 45#include <shlwapi_undoc.h> 46 47#include "CMenuFocusManager.h" 48#include "CMenuToolbars.h" 49#include "CMenuBand.h" 50 51#if DBG 52# undef _ASSERT 53# define _ASSERT(x) DbgAssert(!!(x), __FILE__, __LINE__, #x) 54 55bool DbgAssert(bool x, const char * filename, int line, const char * expr) 56{ 57 if (!x) 58 { 59 char szMsg[512]; 60 const char *fname; 61 62 fname = strrchr(filename, '\\'); 63 if (fname == NULL) 64 { 65 fname = strrchr(filename, '/'); 66 } 67 68 if (fname == NULL) 69 fname = filename; 70 else 71 fname++; 72 73 sprintf(szMsg, "%s:%d: Assertion failed: %s\n", fname, line, expr); 74 75 OutputDebugStringA(szMsg); 76 77 __debugbreak(); 78 } 79 return x; 80} 81#else 82# undef _ASSERT 83# define _ASSERT(x) (!!(x)) 84#endif 85 86WINE_DEFAULT_DEBUG_CHANNEL(CMenuFocus); 87 88DWORD CMenuFocusManager::TlsIndex = 0; 89 90// Gets the thread's assigned manager without refcounting 91CMenuFocusManager * CMenuFocusManager::GetManager() 92{ 93 return reinterpret_cast<CMenuFocusManager *>(TlsGetValue(TlsIndex)); 94} 95 96// Obtains a manager for the thread, with refcounting 97CMenuFocusManager * CMenuFocusManager::AcquireManager() 98{ 99 CMenuFocusManager * obj = NULL; 100 101 if (!TlsIndex) 102 { 103 if ((TlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 104 return NULL; 105 } 106 107 obj = GetManager(); 108 109 if (!obj) 110 { 111 obj = new CComObject<CMenuFocusManager>(); 112 TlsSetValue(TlsIndex, obj); 113 } 114 115 obj->AddRef(); 116 117 return obj; 118} 119 120// Releases a previously acquired manager, and deletes it if the refcount reaches 0 121void CMenuFocusManager::ReleaseManager(CMenuFocusManager * obj) 122{ 123 if (!obj->Release()) 124 { 125 TlsSetValue(TlsIndex, NULL); 126 } 127} 128 129LRESULT CALLBACK CMenuFocusManager::s_MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam) 130{ 131 return GetManager()->MsgFilterHook(nCode, wParam, lParam); 132} 133 134LRESULT CALLBACK CMenuFocusManager::s_GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam) 135{ 136 return GetManager()->GetMsgHook(nCode, wParam, lParam); 137} 138 139HRESULT CMenuFocusManager::PushToArray(StackEntryType type, CMenuBand * mb, HMENU hmenu) 140{ 141 if (m_bandCount >= MAX_RECURSE) 142 return E_OUTOFMEMORY; 143 144 m_bandStack[m_bandCount].type = type; 145 m_bandStack[m_bandCount].mb = mb; 146 m_bandStack[m_bandCount].hmenu = hmenu; 147 m_bandCount++; 148 149 return S_OK; 150} 151 152HRESULT CMenuFocusManager::PopFromArray(StackEntryType * pType, CMenuBand ** pMb, HMENU * pHmenu) 153{ 154 if (pType) *pType = NoEntry; 155 if (pMb) *pMb = NULL; 156 if (pHmenu) *pHmenu = NULL; 157 158 if (m_bandCount <= 0) 159 return S_FALSE; 160 161 m_bandCount--; 162 163 if (pType) *pType = m_bandStack[m_bandCount].type; 164 if (*pType == TrackedMenuEntry) 165 { 166 if (pHmenu) *pHmenu = m_bandStack[m_bandCount].hmenu; 167 } 168 else 169 { 170 if (pMb) *pMb = m_bandStack[m_bandCount].mb; 171 } 172 173 return S_OK; 174} 175 176CMenuFocusManager::CMenuFocusManager() : 177 m_current(NULL), 178 m_parent(NULL), 179 m_hMsgFilterHook(NULL), 180 m_hGetMsgHook(NULL), 181 m_mouseTrackDisabled(FALSE), 182 m_captureHwnd(0), 183 m_hwndUnderMouse(NULL), 184 m_entryUnderMouse(NULL), 185 m_selectedMenu(NULL), 186 m_selectedItem(0), 187 m_selectedItemFlags(0), 188 m_movedSinceDown(FALSE), 189 m_windowAtDown(NULL), 190 m_PreviousForeground(NULL), 191 m_bandCount(0), 192 m_menuDepth(0) 193{ 194 m_ptPrev.x = 0; 195 m_ptPrev.y = 0; 196 m_threadId = GetCurrentThreadId(); 197} 198 199CMenuFocusManager::~CMenuFocusManager() 200{ 201} 202 203// Used so that the toolbar can properly ignore mouse events, when the menu is being used with the keyboard 204void CMenuFocusManager::DisableMouseTrack(HWND parent, BOOL disableThis) 205{ 206 BOOL bDisable = FALSE; 207 BOOL lastDisable = FALSE; 208 209 int i = m_bandCount; 210 while (--i >= 0) 211 { 212 StackEntry& entry = m_bandStack[i]; 213 214 if (entry.type != TrackedMenuEntry) 215 { 216 HWND hwnd; 217 HRESULT hr = entry.mb->_GetTopLevelWindow(&hwnd); 218 if (FAILED_UNEXPECTEDLY(hr)) 219 break; 220 221 if (hwnd == parent) 222 { 223 lastDisable = disableThis; 224 entry.mb->_DisableMouseTrack(disableThis); 225 bDisable = TRUE; 226 } 227 else 228 { 229 lastDisable = bDisable; 230 entry.mb->_DisableMouseTrack(bDisable); 231 } 232 } 233 } 234 m_mouseTrackDisabled = lastDisable; 235} 236 237void CMenuFocusManager::SetMenuCapture(HWND child) 238{ 239 if (m_captureHwnd != child) 240 { 241 if (child) 242 { 243 ::SetCapture(child); 244 m_captureHwnd = child; 245 TRACE("Capturing %p\n", child); 246 } 247 else 248 { 249 ::ReleaseCapture(); 250 m_captureHwnd = NULL; 251 TRACE("Capture is now off\n"); 252 } 253 254 } 255} 256 257HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd, StackEntry ** pentry) 258{ 259 if (pentry) 260 *pentry = NULL; 261 262 for (int i = m_bandCount; --i >= 0;) 263 { 264 StackEntry& entry = m_bandStack[i]; 265 266 if (entry.type != TrackedMenuEntry) 267 { 268 HRESULT hr = entry.mb->IsWindowOwner(hWnd); 269 if (FAILED_UNEXPECTEDLY(hr)) 270 return hr; 271 if (hr == S_OK) 272 { 273 if (pentry) 274 *pentry = &entry; 275 return S_OK; 276 } 277 } 278 } 279 280 return S_FALSE; 281} 282 283HRESULT CMenuFocusManager::IsTrackedWindowOrParent(HWND hWnd) 284{ 285 for (int i = m_bandCount; --i >= 0;) 286 { 287 StackEntry& entry = m_bandStack[i]; 288 289 if (entry.type != TrackedMenuEntry) 290 { 291 HRESULT hr = entry.mb->IsWindowOwner(hWnd); 292 if (FAILED_UNEXPECTEDLY(hr)) 293 return hr; 294 if (hr == S_OK) 295 return S_OK; 296 if (entry.mb->_IsPopup() == S_OK) 297 { 298 CComPtr<IUnknown> site; 299 CComPtr<IOleWindow> pw; 300 hr = entry.mb->GetSite(IID_PPV_ARG(IUnknown, &site)); 301 if (FAILED_UNEXPECTEDLY(hr)) 302 continue; 303 hr = IUnknown_QueryService(site, SID_SMenuBandParent, IID_PPV_ARG(IOleWindow, &pw)); 304 if (FAILED_UNEXPECTEDLY(hr)) 305 continue; 306 307 HWND hParent; 308 if (pw->GetWindow(&hParent) == S_OK && hParent == hWnd) 309 return S_OK; 310 } 311 } 312 } 313 314 return S_FALSE; 315} 316 317LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg) 318{ 319 HWND child; 320 int iHitTestResult = -1; 321 322 POINT pt2 = { GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam) }; 323 ClientToScreen(msg->hwnd, &pt2); 324 325 POINT pt = msg->pt; 326 327 // Don't do anything if another window is capturing the mouse. 328 HWND cCapture = ::GetCapture(); 329 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry) 330 return TRUE; 331 332 m_movedSinceDown = TRUE; 333 334 m_ptPrev = pt; 335 336 child = WindowFromPoint(pt); 337 338 StackEntry * entry = NULL; 339 if (IsTrackedWindow(child, &entry) == S_OK) 340 { 341 TRACE("MouseMove\n"); 342 } 343 344 BOOL isTracking = FALSE; 345 if (entry && (entry->type == MenuBarEntry || m_current->type != TrackedMenuEntry)) 346 { 347 ScreenToClient(child, &pt); 348 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt); 349 isTracking = entry->mb->_IsTracking(); 350 351 if (iHitTestResult < -1) 352 { 353 // TB_HITTEST would return negative numbers for separators 354 iHitTestResult = -iHitTestResult; 355 } 356 else if (iHitTestResult == -1) 357 { 358 // TB_HITTEST would return -1 in two cases: 359 // 1. the mouse is outside the toolbar; 360 // 2. the mouse is over the first item, and that item is a separator. 361 // Confirm the second scenario by checking first item's rect. 362 RECT rc; 363 SendMessageW(child, TB_GETITEMRECT, 1, (LPARAM)&rc); 364 if (PtInRect(&rc, pt)) 365 iHitTestResult = 1; 366 } 367 368 if (SendMessage(child, WM_USER_ISTRACKEDITEM, iHitTestResult, 0) == S_FALSE) 369 { 370 // The current tracked item has changed, notify the toolbar 371 372 TRACE("Hot item tracking detected a change (capture=%p / cCapture=%p)...\n", m_captureHwnd, cCapture); 373 DisableMouseTrack(NULL, FALSE); 374 if (isTracking && iHitTestResult >= 0 && m_current->type == TrackedMenuEntry) 375 SendMessage(entry->hwnd, WM_CANCELMODE, 0, 0); 376 PostMessage(child, WM_USER_CHANGETRACKEDITEM, iHitTestResult, MAKELPARAM(isTracking, TRUE)); 377 if (m_current->type == TrackedMenuEntry) 378 return FALSE; 379 } 380 } 381 382 if (m_entryUnderMouse != entry) 383 { 384 // Mouse moved away from a tracked window 385 if (m_entryUnderMouse) 386 { 387 m_entryUnderMouse->mb->_ChangeHotItem(NULL, -1, HICF_MOUSE); 388 } 389 } 390 391 if (m_hwndUnderMouse != child) 392 { 393 if (entry) 394 { 395 // Mouse moved to a tracked window 396 if (m_current->type == MenuPopupEntry) 397 { 398 ScreenToClient(child, &pt2); 399 SendMessage(child, WM_MOUSEMOVE, msg->wParam, MAKELPARAM(pt2.x, pt2.y)); 400 } 401 } 402 403 m_hwndUnderMouse = child; 404 m_entryUnderMouse = entry; 405 } 406 407 if (m_current->type == MenuPopupEntry) 408 { 409 HWND parent = GetAncestor(child, GA_ROOT); 410 DisableMouseTrack(parent, FALSE); 411 } 412 413 return TRUE; 414} 415 416LRESULT CMenuFocusManager::ProcessMouseDown(MSG* msg, BOOL isLButton) 417{ 418 HWND child; 419 int iHitTestResult = -1; 420 421 TRACE("ProcessMouseDown %d %d %d\n", msg->message, msg->wParam, msg->lParam); 422 423 // Don't do anything if another window is capturing the mouse. 424 HWND cCapture = ::GetCapture(); 425 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry) 426 { 427 TRACE("Foreign capture active.\n"); 428 return TRUE; 429 } 430 431 POINT pt = msg->pt; 432 433 child = WindowFromPoint(pt); 434 435 StackEntry * entry = NULL; 436 if (IsTrackedWindow(child, &entry) != S_OK) 437 { 438 TRACE("Foreign window detected.\n"); 439 return TRUE; 440 } 441 442 if (entry->type == MenuBarEntry) 443 { 444 if (entry != m_current) 445 { 446 TRACE("Menubar with popup active.\n"); 447 return TRUE; 448 } 449 } 450 451 if (entry) 452 { 453 ScreenToClient(child, &pt); 454 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt); 455 456 if (iHitTestResult >= 0) 457 { 458 TRACE("MouseDown send %d\n", iHitTestResult); 459 entry->mb->_MenuBarMouseDown(child, iHitTestResult, isLButton); 460 } 461 } 462 463 msg->message = WM_NULL; 464 465 m_movedSinceDown = FALSE; 466 m_windowAtDown = child; 467 468 TRACE("MouseDown end\n"); 469 470 return TRUE; 471} 472 473LRESULT CMenuFocusManager::ProcessMouseUp(MSG* msg, BOOL isLButton) 474{ 475 HWND child; 476 int iHitTestResult = -1; 477 478 TRACE("ProcessMouseUp %d %d %d\n", msg->message, msg->wParam, msg->lParam); 479 480 // Don't do anything if another window is capturing the mouse. 481 HWND cCapture = ::GetCapture(); 482 if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry) 483 return TRUE; 484 485 POINT pt = msg->pt; 486 487 child = WindowFromPoint(pt); 488 489 StackEntry * entry = NULL; 490 if (IsTrackedWindow(child, &entry) != S_OK) 491 return TRUE; 492 493 if (entry) 494 { 495 ScreenToClient(child, &pt); 496 iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt); 497 498 if (iHitTestResult >= 0) 499 { 500 TRACE("MouseUp send %d\n", iHitTestResult); 501 entry->mb->_MenuBarMouseUp(child, iHitTestResult, isLButton); 502 } 503 } 504 505 return TRUE; 506} 507 508LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam) 509{ 510 if (nCode < 0) 511 return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam); 512 513 if (nCode == MSGF_MENU) 514 { 515 BOOL callNext = TRUE; 516 MSG* msg = reinterpret_cast<MSG*>(hookLParam); 517 518 switch (msg->message) 519 { 520 case WM_LBUTTONDOWN: 521 case WM_RBUTTONDOWN: 522 if (m_menuBar && m_current->type == TrackedMenuEntry) 523 { 524 POINT pt = msg->pt; 525 HWND child = WindowFromPoint(pt); 526 BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK; 527 if (hoveringMenuBar) 528 { 529 m_menuBar->mb->_BeforeCancelPopup(); 530 } 531 } 532 break; 533 case WM_MOUSEMOVE: 534 callNext = ProcessMouseMove(msg); 535 break; 536 case WM_INITMENUPOPUP: 537 TRACE("WM_INITMENUPOPUP %p %p\n", msg->wParam, msg->lParam); 538 m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam); 539 m_selectedItem = -1; 540 m_selectedItemFlags = 0; 541 break; 542 case WM_MENUSELECT: 543 TRACE("WM_MENUSELECT %p %p\n", msg->wParam, msg->lParam); 544 m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam); 545 m_selectedItem = GET_X_LPARAM(msg->wParam); 546 m_selectedItemFlags = HIWORD(msg->wParam); 547 break; 548 case WM_KEYDOWN: 549 switch (msg->wParam) 550 { 551 case VK_LEFT: 552 if (m_current->hmenu == m_selectedMenu) 553 { 554 m_parent->mb->_MenuItemSelect(VK_LEFT); 555 } 556 break; 557 case VK_RIGHT: 558 if (m_selectedItem < 0 || !(m_selectedItemFlags & MF_POPUP)) 559 { 560 m_parent->mb->_MenuItemSelect(VK_RIGHT); 561 } 562 break; 563 } 564 break; 565 } 566 567 if (!callNext) 568 return 1; 569 } 570 571 return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam); 572} 573 574LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam) 575{ 576 BOOL isLButton = FALSE; 577 if (nCode < 0) 578 return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam); 579 580 if (nCode == HC_ACTION) 581 { 582 BOOL callNext = TRUE; 583 MSG* msg = reinterpret_cast<MSG*>(hookLParam); 584 POINT pt = msg->pt; 585 586 switch (msg->message) 587 { 588 case WM_CAPTURECHANGED: 589 if (m_captureHwnd) 590 { 591 TRACE("Capture lost.\n"); 592 m_captureHwnd = NULL; 593 } 594 break; 595 596 case WM_NCLBUTTONDOWN: 597 case WM_LBUTTONDBLCLK: 598 case WM_LBUTTONDOWN: 599 isLButton = TRUE; 600 TRACE("LB\n"); 601 602 if (m_menuBar && m_current->type == MenuPopupEntry) 603 { 604 POINT pt = msg->pt; 605 HWND child = WindowFromPoint(pt); 606 BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK; 607 if (hoveringMenuBar) 608 { 609 m_current->mb->_MenuItemSelect(MPOS_FULLCANCEL); 610 break; 611 } 612 } 613 614 if (m_current->type == MenuPopupEntry) 615 { 616 HWND child = WindowFromPoint(pt); 617 618 if (IsTrackedWindowOrParent(child) != S_OK) 619 { 620 m_current->mb->_MenuItemSelect(MPOS_FULLCANCEL); 621 break; 622 } 623 } 624 625 ProcessMouseDown(msg, isLButton); 626 627 break; 628 case WM_NCRBUTTONUP: 629 case WM_RBUTTONUP: 630 ProcessMouseUp(msg, isLButton); 631 break; 632 case WM_NCLBUTTONUP: 633 case WM_LBUTTONUP: 634 isLButton = TRUE; 635 ProcessMouseUp(msg, isLButton); 636 break; 637 case WM_MOUSEMOVE: 638 callNext = ProcessMouseMove(msg); 639 break; 640 case WM_MOUSELEAVE: 641 callNext = ProcessMouseMove(msg); 642 //callNext = ProcessMouseLeave(msg); 643 break; 644 case WM_SYSKEYDOWN: 645 case WM_KEYDOWN: 646 if (m_current->type == MenuPopupEntry) 647 { 648 DisableMouseTrack(m_current->hwnd, TRUE); 649 switch (msg->wParam) 650 { 651 case VK_ESCAPE: 652 case VK_MENU: 653 case VK_LMENU: 654 case VK_RMENU: 655 m_current->mb->_MenuItemSelect(MPOS_FULLCANCEL); 656 break; 657 case VK_RETURN: 658 m_current->mb->_MenuItemSelect(MPOS_EXECUTE); 659 break; 660 case VK_LEFT: 661 m_current->mb->_MenuItemSelect(VK_LEFT); 662 break; 663 case VK_RIGHT: 664 m_current->mb->_MenuItemSelect(VK_RIGHT); 665 break; 666 case VK_UP: 667 m_current->mb->_MenuItemSelect(VK_UP); 668 break; 669 case VK_DOWN: 670 m_current->mb->_MenuItemSelect(VK_DOWN); 671 break; 672 } 673 msg->message = WM_NULL; 674 msg->lParam = 0; 675 msg->wParam = 0; 676 } 677 break; 678 } 679 680 if (!callNext) 681 return 1; 682 } 683 684 return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam); 685} 686 687HRESULT CMenuFocusManager::PlaceHooks() 688{ 689 if (m_hGetMsgHook) 690 { 691 WARN("GETMESSAGE hook already placed!\n"); 692 return S_OK; 693 } 694 if (m_hMsgFilterHook) 695 { 696 WARN("MSGFILTER hook already placed!\n"); 697 return S_OK; 698 } 699 if (m_current->type == TrackedMenuEntry) 700 { 701 TRACE("Entering MSGFILTER hook...\n"); 702 m_hMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, s_MsgFilterHook, NULL, m_threadId); 703 } 704 else 705 { 706 TRACE("Entering GETMESSAGE hook...\n"); 707 m_hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId); 708 } 709 return S_OK; 710} 711 712HRESULT CMenuFocusManager::RemoveHooks() 713{ 714 if (m_hMsgFilterHook) 715 { 716 TRACE("Removing MSGFILTER hook...\n"); 717 UnhookWindowsHookEx(m_hMsgFilterHook); 718 m_hMsgFilterHook = NULL; 719 } 720 if (m_hGetMsgHook) 721 { 722 TRACE("Removing GETMESSAGE hook...\n"); 723 UnhookWindowsHookEx(m_hGetMsgHook); 724 m_hGetMsgHook = NULL; 725 } 726 return S_OK; 727} 728 729// Used to update the tracking info to account for a change in the top-level menu 730HRESULT CMenuFocusManager::UpdateFocus() 731{ 732 HRESULT hr; 733 StackEntry * old = m_current; 734 735 TRACE("UpdateFocus\n"); 736 737 // Assign the new current item 738 if (m_bandCount > 0) 739 m_current = &(m_bandStack[m_bandCount - 1]); 740 else 741 m_current = NULL; 742 743 // Remove the menu capture if necesary 744 if (!m_current || m_current->type != MenuPopupEntry) 745 { 746 SetMenuCapture(NULL); 747 if (old && old->type == MenuPopupEntry && m_PreviousForeground) 748 { 749 ::SetForegroundWindow(m_PreviousForeground); 750 m_PreviousForeground = NULL; 751 } 752 } 753 754 // Obtain the top-level window for the new active menu 755 if (m_current && m_current->type != TrackedMenuEntry) 756 { 757 hr = m_current->mb->_GetTopLevelWindow(&(m_current->hwnd)); 758 if (FAILED_UNEXPECTEDLY(hr)) 759 return hr; 760 } 761 762 // Refresh the parent pointer 763 if (m_bandCount >= 2) 764 { 765 m_parent = &(m_bandStack[m_bandCount - 2]); 766 _ASSERT(m_parent->type != TrackedMenuEntry); 767 } 768 else 769 { 770 m_parent = NULL; 771 } 772 773 // Refresh the menubar pointer, if applicable 774 if (m_bandCount >= 1 && m_bandStack[0].type == MenuBarEntry) 775 { 776 m_menuBar = &(m_bandStack[0]); 777 } 778 else 779 { 780 m_menuBar = NULL; 781 } 782 783 // Remove the old hooks if the menu type changed, or we don't have a menu anymore 784 if (old && (!m_current || old->type != m_current->type)) 785 { 786 if (m_current && m_current->type != TrackedMenuEntry) 787 { 788 DisableMouseTrack(m_current->hwnd, FALSE); 789 } 790 791 hr = RemoveHooks(); 792 if (FAILED_UNEXPECTEDLY(hr)) 793 return hr; 794 } 795 796 // And place new ones if necessary 797 if (m_current && (!old || old->type != m_current->type)) 798 { 799 hr = PlaceHooks(); 800 if (FAILED_UNEXPECTEDLY(hr)) 801 return hr; 802 } 803 804 // Give the user a chance to move the mouse to the new menu 805 if (m_parent) 806 { 807 DisableMouseTrack(m_parent->hwnd, TRUE); 808 } 809 810 if (m_current && m_current->type == MenuPopupEntry) 811 { 812 if (m_captureHwnd == NULL) 813 { 814 // We need to restore the capture after a non-shell submenu or context menu is shown 815 StackEntry * topMenu = m_bandStack; 816 if (topMenu->type == MenuBarEntry) 817 topMenu++; 818 819 // Get the top-level window from the top popup 820 CComPtr<IServiceProvider> bandSite; 821 CComPtr<IOleWindow> deskBar; 822 hr = topMenu->mb->GetSite(IID_PPV_ARG(IServiceProvider, &bandSite)); 823 if (FAILED(hr)) 824 goto NoCapture; 825 hr = bandSite->QueryService(SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &deskBar)); 826 if (FAILED(hr)) 827 goto NoCapture; 828 829 CComPtr<IOleWindow> deskBarSite; 830 hr = IUnknown_GetSite(deskBar, IID_PPV_ARG(IOleWindow, &deskBarSite)); 831 if (FAILED(hr)) 832 goto NoCapture; 833 834 // FIXME: Find the correct place for this 835 HWND hWndOwner; 836 hr = deskBarSite->GetWindow(&hWndOwner); 837 if (FAILED(hr)) 838 goto NoCapture; 839 840 m_PreviousForeground = ::GetForegroundWindow(); 841 if (m_PreviousForeground != hWndOwner) 842 ::SetForegroundWindow(hWndOwner); 843 else 844 m_PreviousForeground = NULL; 845 846 // Get the HWND of the top-level window 847 HWND hWndSite; 848 hr = deskBar->GetWindow(&hWndSite); 849 if (FAILED(hr)) 850 goto NoCapture; 851 SetMenuCapture(hWndSite); 852 } 853NoCapture: 854 855 if (!m_parent || m_parent->type == MenuBarEntry) 856 { 857 if (old && old->type == TrackedMenuEntry) 858 { 859 // FIXME: Debugging code, probably not right 860 POINT pt2; 861 RECT rc2; 862 GetCursorPos(&pt2); 863 ScreenToClient(m_current->hwnd, &pt2); 864 GetClientRect(m_current->hwnd, &rc2); 865 if (PtInRect(&rc2, pt2)) 866 SendMessage(m_current->hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(pt2.x, pt2.y)); 867 else 868 SendMessage(m_current->hwnd, WM_MOUSELEAVE, 0, 0); 869 } 870 } 871 } 872 873 _ASSERT(!m_parent || m_parent->type != TrackedMenuEntry); 874 875 return S_OK; 876} 877 878// Begin tracking top-level menu bar (for file browser windows) 879HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb) 880{ 881 TRACE("PushMenuBar %p\n", mb); 882 883 mb->AddRef(); 884 885 _ASSERT(m_bandCount == 0); 886 887 HRESULT hr = PushToArray(MenuBarEntry, mb, NULL); 888 if (FAILED_UNEXPECTEDLY(hr)) 889 return hr; 890 891 return UpdateFocus(); 892} 893 894// Begin tracking a shell menu popup (start menu or submenus) 895HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb) 896{ 897 TRACE("PushTrackedPopup %p\n", mb); 898 899 mb->AddRef(); 900 901 _ASSERT(!m_current || m_current->type != TrackedMenuEntry); 902 903 HRESULT hr = PushToArray(MenuPopupEntry, mb, NULL); 904 if (FAILED_UNEXPECTEDLY(hr)) 905 return hr; 906 907 hr = UpdateFocus(); 908 909 m_menuDepth++; 910 911 if (m_parent && m_parent->type != TrackedMenuEntry) 912 { 913 m_parent->mb->_SetChildBand(mb); 914 mb->_SetParentBand(m_parent->mb); 915 } 916 917 return hr; 918} 919 920// Begin tracking a system popup submenu (submenu of the file browser windows) 921HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup) 922{ 923 TRACE("PushTrackedPopup %p\n", popup); 924 925 _ASSERT(m_bandCount > 0); 926 _ASSERT(!m_current || m_current->type != TrackedMenuEntry); 927 928 HRESULT hr = PushToArray(TrackedMenuEntry, NULL, popup); 929 if (FAILED_UNEXPECTEDLY(hr)) 930 return hr; 931 932 TRACE("PushTrackedPopup %p\n", popup); 933 m_selectedMenu = popup; 934 m_selectedItem = -1; 935 m_selectedItemFlags = 0; 936 937 return UpdateFocus(); 938} 939 940// Stop tracking the menubar 941HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb) 942{ 943 StackEntryType type; 944 CMenuBand * mbc; 945 HRESULT hr; 946 947 TRACE("PopMenuBar %p\n", mb); 948 949 if (m_current == m_entryUnderMouse) 950 { 951 m_entryUnderMouse = NULL; 952 } 953 954 hr = PopFromArray(&type, &mbc, NULL); 955 if (FAILED_UNEXPECTEDLY(hr)) 956 { 957 UpdateFocus(); 958 return hr; 959 } 960 961 _ASSERT(type == MenuBarEntry); 962 if (type != MenuBarEntry) 963 return E_FAIL; 964 965 if (!mbc) 966 return E_FAIL; 967 968 mbc->_SetParentBand(NULL); 969 970 mbc->Release(); 971 972 hr = UpdateFocus(); 973 if (FAILED_UNEXPECTEDLY(hr)) 974 return hr; 975 976 if (m_current) 977 { 978 _ASSERT(m_current->type != TrackedMenuEntry); 979 m_current->mb->_SetChildBand(NULL); 980 } 981 982 return S_OK; 983} 984 985// Stop tracking a shell menu 986HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb) 987{ 988 StackEntryType type; 989 CMenuBand * mbc; 990 HRESULT hr; 991 992 TRACE("PopMenuPopup %p\n", mb); 993 994 if (m_current == m_entryUnderMouse) 995 { 996 m_entryUnderMouse = NULL; 997 } 998 999 m_menuDepth--; 1000 1001 hr = PopFromArray(&type, &mbc, NULL); 1002 if (FAILED_UNEXPECTEDLY(hr)) 1003 { 1004 UpdateFocus(); 1005 return hr; 1006 } 1007 1008 _ASSERT(type == MenuPopupEntry); 1009 if (type != MenuPopupEntry) 1010 return E_FAIL; 1011 1012 if (!mbc) 1013 return E_FAIL; 1014 1015 mbc->_SetParentBand(NULL); 1016 1017 mbc->Release(); 1018 1019 hr = UpdateFocus(); 1020 if (FAILED_UNEXPECTEDLY(hr)) 1021 return hr; 1022 1023 if (m_current) 1024 { 1025 _ASSERT(m_current->type != TrackedMenuEntry); 1026 m_current->mb->_SetChildBand(NULL); 1027 } 1028 1029 return S_OK; 1030} 1031 1032// Stop tracking a system popup submenu 1033HRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup) 1034{ 1035 StackEntryType type; 1036 HMENU hmenu; 1037 HRESULT hr; 1038 1039 TRACE("PopTrackedPopup %p\n", popup); 1040 1041 hr = PopFromArray(&type, NULL, &hmenu); 1042 if (FAILED_UNEXPECTEDLY(hr)) 1043 { 1044 UpdateFocus(); 1045 return hr; 1046 } 1047 1048 _ASSERT(type == TrackedMenuEntry); 1049 if (type != TrackedMenuEntry) 1050 return E_FAIL; 1051 1052 if (hmenu != popup) 1053 return E_FAIL; 1054 1055 hr = UpdateFocus(); 1056 if (FAILED_UNEXPECTEDLY(hr)) 1057 return hr; 1058 1059 return S_OK; 1060}