Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061/* * Shell Menu Band * * Copyright 2014 David Quintana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
/*This file implements the CMenuFocusManager class.
This class manages the shell menus, by overriding the hot-tracking behaviour.
For the shell menus, it uses a GetMessage hook,where it intercepts messages directed to the menu windows.
In order to show submenus using system popups, it also has a MessageFilter hook.
The menu is tracked using a stack structure. When a CMenuBand wants to open a submenu,it pushes the submenu band, or HMENU to track in case of system popups,and when the menu has closed, it pops the same pointer or handle.
While a shell menu is open, it overrides the menu toolbar's hottracking behaviour,using its own logic to track both the active menu item, and the opened submenu's parent item.
While a system popup is open, it tracks the mouse movements so that it can cancel the popup,and switch to another submenu when the mouse goes over another item from the parent.
*/#include "shellmenu.h"#include <windowsx.h>#include <commoncontrols.h>#include <shlwapi_undoc.h>
#include "CMenuFocusManager.h"#include "CMenuToolbars.h"#include "CMenuBand.h"
#if DBG# undef _ASSERT# define _ASSERT(x) DbgAssert(!!(x), __FILE__, __LINE__, #x)
bool DbgAssert(bool x, const char * filename, int line, const char * expr){ if (!x) { char szMsg[512]; const char *fname;
fname = strrchr(filename, '\\'); if (fname == NULL) { fname = strrchr(filename, '/'); }
if (fname == NULL) fname = filename; else fname++;
sprintf(szMsg, "%s:%d: Assertion failed: %s\n", fname, line, expr);
OutputDebugStringA(szMsg);
__debugbreak(); } return x;}#else# undef _ASSERT# define _ASSERT(x) (!!(x))#endif
WINE_DEFAULT_DEBUG_CHANNEL(CMenuFocus);
DWORD CMenuFocusManager::TlsIndex = 0;
// Gets the thread's assigned manager without refcountingCMenuFocusManager * CMenuFocusManager::GetManager(){ return reinterpret_cast<CMenuFocusManager *>(TlsGetValue(TlsIndex));}
// Obtains a manager for the thread, with refcountingCMenuFocusManager * CMenuFocusManager::AcquireManager(){ CMenuFocusManager * obj = NULL;
if (!TlsIndex) { if ((TlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) return NULL; }
obj = GetManager();
if (!obj) { obj = new CComObject<CMenuFocusManager>(); TlsSetValue(TlsIndex, obj); }
obj->AddRef();
return obj;}
// Releases a previously acquired manager, and deletes it if the refcount reaches 0void CMenuFocusManager::ReleaseManager(CMenuFocusManager * obj){ if (!obj->Release()) { TlsSetValue(TlsIndex, NULL); }}
LRESULT CALLBACK CMenuFocusManager::s_MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam){ return GetManager()->MsgFilterHook(nCode, wParam, lParam);}
LRESULT CALLBACK CMenuFocusManager::s_GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam){ return GetManager()->GetMsgHook(nCode, wParam, lParam);}
HRESULT CMenuFocusManager::PushToArray(StackEntryType type, CMenuBand * mb, HMENU hmenu){ if (m_bandCount >= MAX_RECURSE) return E_OUTOFMEMORY;
m_bandStack[m_bandCount].type = type; m_bandStack[m_bandCount].mb = mb; m_bandStack[m_bandCount].hmenu = hmenu; m_bandCount++;
return S_OK;}
HRESULT CMenuFocusManager::PopFromArray(StackEntryType * pType, CMenuBand ** pMb, HMENU * pHmenu){ if (pType) *pType = NoEntry; if (pMb) *pMb = NULL; if (pHmenu) *pHmenu = NULL;
if (m_bandCount <= 0) return S_FALSE;
m_bandCount--;
if (pType) *pType = m_bandStack[m_bandCount].type; if (*pType == TrackedMenuEntry) { if (pHmenu) *pHmenu = m_bandStack[m_bandCount].hmenu; } else { if (pMb) *pMb = m_bandStack[m_bandCount].mb; }
return S_OK;}
CMenuFocusManager::CMenuFocusManager() : m_current(NULL), m_parent(NULL), m_hMsgFilterHook(NULL), m_hGetMsgHook(NULL), m_mouseTrackDisabled(FALSE), m_captureHwnd(0), m_hwndUnderMouse(NULL), m_entryUnderMouse(NULL), m_selectedMenu(NULL), m_selectedItem(0), m_selectedItemFlags(0), m_movedSinceDown(FALSE), m_windowAtDown(NULL), m_PreviousForeground(NULL), m_bandCount(0), m_menuDepth(0){ m_ptPrev.x = 0; m_ptPrev.y = 0; m_threadId = GetCurrentThreadId();}
CMenuFocusManager::~CMenuFocusManager(){}
// Used so that the toolbar can properly ignore mouse events, when the menu is being used with the keyboardvoid CMenuFocusManager::DisableMouseTrack(HWND parent, BOOL disableThis){ BOOL bDisable = FALSE; BOOL lastDisable = FALSE;
int i = m_bandCount; while (--i >= 0) { StackEntry& entry = m_bandStack[i];
if (entry.type != TrackedMenuEntry) { HWND hwnd; HRESULT hr = entry.mb->_GetTopLevelWindow(&hwnd); if (FAILED_UNEXPECTEDLY(hr)) break;
if (hwnd == parent) { lastDisable = disableThis; entry.mb->_DisableMouseTrack(disableThis); bDisable = TRUE; } else { lastDisable = bDisable; entry.mb->_DisableMouseTrack(bDisable); } } } m_mouseTrackDisabled = lastDisable;}
void CMenuFocusManager::SetMenuCapture(HWND child){ if (m_captureHwnd != child) { if (child) { ::SetCapture(child); m_captureHwnd = child; TRACE("Capturing %p\n", child); } else { ::ReleaseCapture(); m_captureHwnd = NULL; TRACE("Capture is now off\n"); }
}}
HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd, StackEntry ** pentry){ if (pentry) *pentry = NULL;
for (int i = m_bandCount; --i >= 0;) { StackEntry& entry = m_bandStack[i];
if (entry.type != TrackedMenuEntry) { HRESULT hr = entry.mb->IsWindowOwner(hWnd); if (FAILED_UNEXPECTEDLY(hr)) return hr; if (hr == S_OK) { if (pentry) *pentry = &entry; return S_OK; } } }
return S_FALSE;}
HRESULT CMenuFocusManager::IsTrackedWindowOrParent(HWND hWnd){ for (int i = m_bandCount; --i >= 0;) { StackEntry& entry = m_bandStack[i];
if (entry.type != TrackedMenuEntry) { HRESULT hr = entry.mb->IsWindowOwner(hWnd); if (FAILED_UNEXPECTEDLY(hr)) return hr; if (hr == S_OK) return S_OK; if (entry.mb->_IsPopup() == S_OK) { CComPtr<IUnknown> site; CComPtr<IOleWindow> pw; hr = entry.mb->GetSite(IID_PPV_ARG(IUnknown, &site)); if (FAILED_UNEXPECTEDLY(hr)) continue; hr = IUnknown_QueryService(site, SID_SMenuBandParent, IID_PPV_ARG(IOleWindow, &pw)); if (FAILED_UNEXPECTEDLY(hr)) continue;
HWND hParent; if (pw->GetWindow(&hParent) == S_OK && hParent == hWnd) return S_OK; } } }
return S_FALSE;}
LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg){ HWND child; int iHitTestResult = -1;
POINT pt2 = { GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam) }; ClientToScreen(msg->hwnd, &pt2);
POINT pt = msg->pt;
// Don't do anything if another window is capturing the mouse. HWND cCapture = ::GetCapture(); if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry) return TRUE;
m_movedSinceDown = TRUE;
m_ptPrev = pt;
child = WindowFromPoint(pt);
StackEntry * entry = NULL; if (IsTrackedWindow(child, &entry) == S_OK) { TRACE("MouseMove\n"); }
BOOL isTracking = FALSE; if (entry && (entry->type == MenuBarEntry || m_current->type != TrackedMenuEntry)) { ScreenToClient(child, &pt); iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt); isTracking = entry->mb->_IsTracking();
if (iHitTestResult < -1) { // TB_HITTEST would return negative numbers for separators iHitTestResult = -iHitTestResult; } else if (iHitTestResult == -1) { // TB_HITTEST would return -1 in two cases: // 1. the mouse is outside the toolbar; // 2. the mouse is over the first item, and that item is a separator. // Confirm the second scenario by checking first item's rect. RECT rc; SendMessageW(child, TB_GETITEMRECT, 1, (LPARAM)&rc); if (PtInRect(&rc, pt)) iHitTestResult = 1; }
if (SendMessage(child, WM_USER_ISTRACKEDITEM, iHitTestResult, 0) == S_FALSE) { // The current tracked item has changed, notify the toolbar
TRACE("Hot item tracking detected a change (capture=%p / cCapture=%p)...\n", m_captureHwnd, cCapture); DisableMouseTrack(NULL, FALSE); if (isTracking && iHitTestResult >= 0 && m_current->type == TrackedMenuEntry) SendMessage(entry->hwnd, WM_CANCELMODE, 0, 0); PostMessage(child, WM_USER_CHANGETRACKEDITEM, iHitTestResult, MAKELPARAM(isTracking, TRUE)); if (m_current->type == TrackedMenuEntry) return FALSE; } }
if (m_entryUnderMouse != entry) { // Mouse moved away from a tracked window if (m_entryUnderMouse) { m_entryUnderMouse->mb->_ChangeHotItem(NULL, -1, HICF_MOUSE); } }
if (m_hwndUnderMouse != child) { if (entry) { // Mouse moved to a tracked window if (m_current->type == MenuPopupEntry) { ScreenToClient(child, &pt2); SendMessage(child, WM_MOUSEMOVE, msg->wParam, MAKELPARAM(pt2.x, pt2.y)); } }
m_hwndUnderMouse = child; m_entryUnderMouse = entry; }
if (m_current->type == MenuPopupEntry) { HWND parent = GetAncestor(child, GA_ROOT); DisableMouseTrack(parent, FALSE); }
return TRUE;}
LRESULT CMenuFocusManager::ProcessMouseDown(MSG* msg, BOOL isLButton){ HWND child; int iHitTestResult = -1;
TRACE("ProcessMouseDown %d %d %d\n", msg->message, msg->wParam, msg->lParam);
// Don't do anything if another window is capturing the mouse. HWND cCapture = ::GetCapture(); if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry) { TRACE("Foreign capture active.\n"); return TRUE; }
POINT pt = msg->pt;
child = WindowFromPoint(pt);
StackEntry * entry = NULL; if (IsTrackedWindow(child, &entry) != S_OK) { TRACE("Foreign window detected.\n"); return TRUE; }
if (entry->type == MenuBarEntry) { if (entry != m_current) { TRACE("Menubar with popup active.\n"); return TRUE; } }
if (entry) { ScreenToClient(child, &pt); iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
if (iHitTestResult >= 0) { TRACE("MouseDown send %d\n", iHitTestResult); entry->mb->_MenuBarMouseDown(child, iHitTestResult, isLButton); } }
msg->message = WM_NULL;
m_movedSinceDown = FALSE; m_windowAtDown = child;
TRACE("MouseDown end\n");
return TRUE;}
LRESULT CMenuFocusManager::ProcessMouseUp(MSG* msg, BOOL isLButton){ HWND child; int iHitTestResult = -1;
TRACE("ProcessMouseUp %d %d %d\n", msg->message, msg->wParam, msg->lParam);
// Don't do anything if another window is capturing the mouse. HWND cCapture = ::GetCapture(); if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry) return TRUE;
POINT pt = msg->pt;
child = WindowFromPoint(pt);
StackEntry * entry = NULL; if (IsTrackedWindow(child, &entry) != S_OK) return TRUE;
if (entry) { ScreenToClient(child, &pt); iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
if (iHitTestResult >= 0) { TRACE("MouseUp send %d\n", iHitTestResult); entry->mb->_MenuBarMouseUp(child, iHitTestResult, isLButton); } }
return TRUE;}
LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam){ if (nCode < 0) return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
if (nCode == MSGF_MENU) { BOOL callNext = TRUE; MSG* msg = reinterpret_cast<MSG*>(hookLParam);
switch (msg->message) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: if (m_menuBar && m_current->type == TrackedMenuEntry) { POINT pt = msg->pt; HWND child = WindowFromPoint(pt); BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK; if (hoveringMenuBar) { m_menuBar->mb->_BeforeCancelPopup(); } } break; case WM_MOUSEMOVE: callNext = ProcessMouseMove(msg); break; case WM_INITMENUPOPUP: TRACE("WM_INITMENUPOPUP %p %p\n", msg->wParam, msg->lParam); m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam); m_selectedItem = -1; m_selectedItemFlags = 0; break; case WM_MENUSELECT: TRACE("WM_MENUSELECT %p %p\n", msg->wParam, msg->lParam); m_selectedMenu = reinterpret_cast<HMENU>(msg->lParam); m_selectedItem = GET_X_LPARAM(msg->wParam); m_selectedItemFlags = HIWORD(msg->wParam); break; case WM_KEYDOWN: switch (msg->wParam) { case VK_LEFT: if (m_current->hmenu == m_selectedMenu) { m_parent->mb->_MenuItemSelect(VK_LEFT); } break; case VK_RIGHT: if (m_selectedItem < 0 || !(m_selectedItemFlags & MF_POPUP)) { m_parent->mb->_MenuItemSelect(VK_RIGHT); } break; } break; }
if (!callNext) return 1; }
return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);}
LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam){ BOOL isLButton = FALSE; if (nCode < 0) return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
if (nCode == HC_ACTION) { BOOL callNext = TRUE; MSG* msg = reinterpret_cast<MSG*>(hookLParam); POINT pt = msg->pt;
switch (msg->message) { case WM_CAPTURECHANGED: if (m_captureHwnd) { TRACE("Capture lost.\n"); m_captureHwnd = NULL; } break;
case WM_NCLBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: isLButton = TRUE; TRACE("LB\n");
if (m_menuBar && m_current->type == MenuPopupEntry) { POINT pt = msg->pt; HWND child = WindowFromPoint(pt); BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK; if (hoveringMenuBar) { m_current->mb->_MenuItemSelect(MPOS_FULLCANCEL); break; } }
if (m_current->type == MenuPopupEntry) { HWND child = WindowFromPoint(pt);
if (IsTrackedWindowOrParent(child) != S_OK) { m_current->mb->_MenuItemSelect(MPOS_FULLCANCEL); break; } }
ProcessMouseDown(msg, isLButton);
break; case WM_NCRBUTTONUP: case WM_RBUTTONUP: ProcessMouseUp(msg, isLButton); break; case WM_NCLBUTTONUP: case WM_LBUTTONUP: isLButton = TRUE; ProcessMouseUp(msg, isLButton); break; case WM_MOUSEMOVE: callNext = ProcessMouseMove(msg); break; case WM_MOUSELEAVE: callNext = ProcessMouseMove(msg); //callNext = ProcessMouseLeave(msg); break; case WM_SYSKEYDOWN: case WM_KEYDOWN: if (m_current->type == MenuPopupEntry) { DisableMouseTrack(m_current->hwnd, TRUE); switch (msg->wParam) { case VK_ESCAPE: case VK_MENU: case VK_LMENU: case VK_RMENU: m_current->mb->_MenuItemSelect(MPOS_FULLCANCEL); break; case VK_RETURN: m_current->mb->_MenuItemSelect(MPOS_EXECUTE); break; case VK_LEFT: m_current->mb->_MenuItemSelect(VK_LEFT); break; case VK_RIGHT: m_current->mb->_MenuItemSelect(VK_RIGHT); break; case VK_UP: m_current->mb->_MenuItemSelect(VK_UP); break; case VK_DOWN: m_current->mb->_MenuItemSelect(VK_DOWN); break; } msg->message = WM_NULL; msg->lParam = 0; msg->wParam = 0; } break; }
if (!callNext) return 1; }
return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);}
HRESULT CMenuFocusManager::PlaceHooks(){ if (m_hGetMsgHook) { WARN("GETMESSAGE hook already placed!\n"); return S_OK; } if (m_hMsgFilterHook) { WARN("MSGFILTER hook already placed!\n"); return S_OK; } if (m_current->type == TrackedMenuEntry) { TRACE("Entering MSGFILTER hook...\n"); m_hMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, s_MsgFilterHook, NULL, m_threadId); } else { TRACE("Entering GETMESSAGE hook...\n"); m_hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId); } return S_OK;}
HRESULT CMenuFocusManager::RemoveHooks(){ if (m_hMsgFilterHook) { TRACE("Removing MSGFILTER hook...\n"); UnhookWindowsHookEx(m_hMsgFilterHook); m_hMsgFilterHook = NULL; } if (m_hGetMsgHook) { TRACE("Removing GETMESSAGE hook...\n"); UnhookWindowsHookEx(m_hGetMsgHook); m_hGetMsgHook = NULL; } return S_OK;}
// Used to update the tracking info to account for a change in the top-level menuHRESULT CMenuFocusManager::UpdateFocus(){ HRESULT hr; StackEntry * old = m_current;
TRACE("UpdateFocus\n");
// Assign the new current item if (m_bandCount > 0) m_current = &(m_bandStack[m_bandCount - 1]); else m_current = NULL;
// Remove the menu capture if necesary if (!m_current || m_current->type != MenuPopupEntry) { SetMenuCapture(NULL); if (old && old->type == MenuPopupEntry && m_PreviousForeground) { ::SetForegroundWindow(m_PreviousForeground); m_PreviousForeground = NULL; } }
// Obtain the top-level window for the new active menu if (m_current && m_current->type != TrackedMenuEntry) { hr = m_current->mb->_GetTopLevelWindow(&(m_current->hwnd)); if (FAILED_UNEXPECTEDLY(hr)) return hr; }
// Refresh the parent pointer if (m_bandCount >= 2) { m_parent = &(m_bandStack[m_bandCount - 2]); _ASSERT(m_parent->type != TrackedMenuEntry); } else { m_parent = NULL; }
// Refresh the menubar pointer, if applicable if (m_bandCount >= 1 && m_bandStack[0].type == MenuBarEntry) { m_menuBar = &(m_bandStack[0]); } else { m_menuBar = NULL; }
// Remove the old hooks if the menu type changed, or we don't have a menu anymore if (old && (!m_current || old->type != m_current->type)) { if (m_current && m_current->type != TrackedMenuEntry) { DisableMouseTrack(m_current->hwnd, FALSE); }
hr = RemoveHooks(); if (FAILED_UNEXPECTEDLY(hr)) return hr; }
// And place new ones if necessary if (m_current && (!old || old->type != m_current->type)) { hr = PlaceHooks(); if (FAILED_UNEXPECTEDLY(hr)) return hr; }
// Give the user a chance to move the mouse to the new menu if (m_parent) { DisableMouseTrack(m_parent->hwnd, TRUE); }
if (m_current && m_current->type == MenuPopupEntry) { if (m_captureHwnd == NULL) { // We need to restore the capture after a non-shell submenu or context menu is shown StackEntry * topMenu = m_bandStack; if (topMenu->type == MenuBarEntry) topMenu++;
// Get the top-level window from the top popup CComPtr<IServiceProvider> bandSite; CComPtr<IOleWindow> deskBar; hr = topMenu->mb->GetSite(IID_PPV_ARG(IServiceProvider, &bandSite)); if (FAILED(hr)) goto NoCapture; hr = bandSite->QueryService(SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &deskBar)); if (FAILED(hr)) goto NoCapture;
CComPtr<IOleWindow> deskBarSite; hr = IUnknown_GetSite(deskBar, IID_PPV_ARG(IOleWindow, &deskBarSite)); if (FAILED(hr)) goto NoCapture;
// FIXME: Find the correct place for this HWND hWndOwner; hr = deskBarSite->GetWindow(&hWndOwner); if (FAILED(hr)) goto NoCapture;
m_PreviousForeground = ::GetForegroundWindow(); if (m_PreviousForeground != hWndOwner) ::SetForegroundWindow(hWndOwner); else m_PreviousForeground = NULL;
// Get the HWND of the top-level window HWND hWndSite; hr = deskBar->GetWindow(&hWndSite); if (FAILED(hr)) goto NoCapture; SetMenuCapture(hWndSite); }NoCapture:
if (!m_parent || m_parent->type == MenuBarEntry) { if (old && old->type == TrackedMenuEntry) { // FIXME: Debugging code, probably not right POINT pt2; RECT rc2; GetCursorPos(&pt2); ScreenToClient(m_current->hwnd, &pt2); GetClientRect(m_current->hwnd, &rc2); if (PtInRect(&rc2, pt2)) SendMessage(m_current->hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(pt2.x, pt2.y)); else SendMessage(m_current->hwnd, WM_MOUSELEAVE, 0, 0); } } }
_ASSERT(!m_parent || m_parent->type != TrackedMenuEntry);
return S_OK;}
// Begin tracking top-level menu bar (for file browser windows)HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb){ TRACE("PushMenuBar %p\n", mb);
mb->AddRef();
_ASSERT(m_bandCount == 0);
HRESULT hr = PushToArray(MenuBarEntry, mb, NULL); if (FAILED_UNEXPECTEDLY(hr)) return hr;
return UpdateFocus();}
// Begin tracking a shell menu popup (start menu or submenus)HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb){ TRACE("PushTrackedPopup %p\n", mb);
mb->AddRef();
_ASSERT(!m_current || m_current->type != TrackedMenuEntry);
HRESULT hr = PushToArray(MenuPopupEntry, mb, NULL); if (FAILED_UNEXPECTEDLY(hr)) return hr;
hr = UpdateFocus();
m_menuDepth++;
if (m_parent && m_parent->type != TrackedMenuEntry) { m_parent->mb->_SetChildBand(mb); mb->_SetParentBand(m_parent->mb); }
return hr;}
// Begin tracking a system popup submenu (submenu of the file browser windows)HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup){ TRACE("PushTrackedPopup %p\n", popup);
_ASSERT(m_bandCount > 0); _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
HRESULT hr = PushToArray(TrackedMenuEntry, NULL, popup); if (FAILED_UNEXPECTEDLY(hr)) return hr;
TRACE("PushTrackedPopup %p\n", popup); m_selectedMenu = popup; m_selectedItem = -1; m_selectedItemFlags = 0;
return UpdateFocus();}
// Stop tracking the menubarHRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb){ StackEntryType type; CMenuBand * mbc; HRESULT hr;
TRACE("PopMenuBar %p\n", mb);
if (m_current == m_entryUnderMouse) { m_entryUnderMouse = NULL; }
hr = PopFromArray(&type, &mbc, NULL); if (FAILED_UNEXPECTEDLY(hr)) { UpdateFocus(); return hr; }
_ASSERT(type == MenuBarEntry); if (type != MenuBarEntry) return E_FAIL;
if (!mbc) return E_FAIL;
mbc->_SetParentBand(NULL);
mbc->Release();
hr = UpdateFocus(); if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (m_current) { _ASSERT(m_current->type != TrackedMenuEntry); m_current->mb->_SetChildBand(NULL); }
return S_OK;}
// Stop tracking a shell menuHRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb){ StackEntryType type; CMenuBand * mbc; HRESULT hr;
TRACE("PopMenuPopup %p\n", mb);
if (m_current == m_entryUnderMouse) { m_entryUnderMouse = NULL; }
m_menuDepth--;
hr = PopFromArray(&type, &mbc, NULL); if (FAILED_UNEXPECTEDLY(hr)) { UpdateFocus(); return hr; }
_ASSERT(type == MenuPopupEntry); if (type != MenuPopupEntry) return E_FAIL;
if (!mbc) return E_FAIL;
mbc->_SetParentBand(NULL);
mbc->Release();
hr = UpdateFocus(); if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (m_current) { _ASSERT(m_current->type != TrackedMenuEntry); m_current->mb->_SetChildBand(NULL); }
return S_OK;}
// Stop tracking a system popup submenuHRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup){ StackEntryType type; HMENU hmenu; HRESULT hr;
TRACE("PopTrackedPopup %p\n", popup);
hr = PopFromArray(&type, NULL, &hmenu); if (FAILED_UNEXPECTEDLY(hr)) { UpdateFocus(); return hr; }
_ASSERT(type == TrackedMenuEntry); if (type != TrackedMenuEntry) return E_FAIL;
if (hmenu != popup) return E_FAIL;
hr = UpdateFocus(); if (FAILED_UNEXPECTEDLY(hr)) return hr;
return S_OK;}