[RSHELL]
[reactos.git] / base / shell / rshell / CMenuFocusManager.cpp
index e066835..36ef3d0 100644 (file)
 #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;
@@ -76,60 +111,62 @@ LRESULT CALLBACK CMenuFocusManager::s_GetMsgHook(INT nCode, WPARAM wParam, LPARA
     return GetManager()->GetMsgHook(nCode, wParam, lParam);
 }
 
-HRESULT CMenuFocusManager::PushToArray(CMenuBand * item)
+HRESULT CMenuFocusManager::PushToArray(StackEntryType type, CMenuBand * mb, HMENU hmenu)
 {
     if (m_bandCount >= MAX_RECURSE)
         return E_OUTOFMEMORY;
 
-    m_bandStack[m_bandCount++] = item;
-    return S_OK;
-}
-
-HRESULT CMenuFocusManager::PopFromArray(CMenuBand ** pItem)
-{
-    if (pItem)
-        *pItem = NULL;
-
-    if (m_bandCount <= 0)
-        return S_FALSE;
-
-    m_bandCount--;
-
-    if (pItem)
-        *pItem = m_bandStack[m_bandCount];
-
-    m_bandStack[m_bandCount] = NULL;
+    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::PeekArray(CMenuBand ** pItem)
+HRESULT CMenuFocusManager::PopFromArray(StackEntryType * pType, CMenuBand ** pMb, HMENU * pHmenu)
 {
-    if (!pItem)
-        return E_FAIL;
-
-    *pItem = NULL;
+    if (pType)  *pType = NoEntry;
+    if (pMb)    *pMb = NULL;
+    if (pHmenu) *pHmenu = NULL;
 
     if (m_bandCount <= 0)
         return S_FALSE;
 
-    *pItem = m_bandStack[m_bandCount - 1];
+    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_currentBand(NULL),
-    m_currentFocus(NULL),
-    m_currentMenu(NULL),
-    m_parentToolbar(NULL),
+    m_current(NULL),
+    m_parent(NULL),
     m_hMsgFilterHook(NULL),
     m_hGetMsgHook(NULL),
     m_mouseTrackDisabled(FALSE),
-    m_lastMoveFlags(0),
-    m_lastMovePos(0),
+    m_captureHwnd(0),
+    m_hwndUnderMouse(NULL),
+    m_entryUnderMouse(NULL),
+    m_selectedMenu(NULL),
+    m_selectedItem(0),
+    m_selectedItemFlags(0),
+    m_isLButtonDown(FALSE),
+    m_movedSinceDown(FALSE),
+    m_windowAtDown(NULL),
     m_bandCount(0)
 {
+    m_ptPrev.x = 0;
+    m_ptPrev.y = 0;
     m_threadId = GetCurrentThreadId();
 }
 
@@ -137,58 +174,114 @@ CMenuFocusManager::~CMenuFocusManager()
 {
 }
 
-void CMenuFocusManager::DisableMouseTrack(HWND enableTo, BOOL disableThis)
+void CMenuFocusManager::DisableMouseTrack(HWND parent, BOOL disableThis)
 {
     BOOL bDisable = FALSE;
+    BOOL lastDisable = FALSE;
 
     int i = m_bandCount;
     while (--i >= 0)
     {
-        CMenuBand * band = m_bandStack[i];
-        
-        HWND hwnd;
-        HRESULT hr = band->_GetTopLevelWindow(&hwnd);
-        if (FAILED_UNEXPECTEDLY(hr))
-            break;
+        StackEntry& entry = m_bandStack[i];
 
-        if (hwnd == enableTo)
+        if (entry.type != TrackedMenuEntry)
         {
-            band->_DisableMouseTrack(disableThis);
-            bDisable = TRUE;
-        }
-        else
-        {
-            band->_DisableMouseTrack(bDisable);
+            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;
+}
 
-    if (m_mouseTrackDisabled == bDisable)
+void CMenuFocusManager::SetCapture(HWND child)
+{
+    if (m_captureHwnd != child)
     {
-        if (bDisable)
+        if (child)
         {
-            SetCapture(m_currentFocus);
+            ::SetCapture(child);
+            m_captureHwnd = child;
+            TRACE("MouseTrack is now capturing %p\n", child);
         }
         else
-            ReleaseCapture();
+        {
+            ::ReleaseCapture();
+            m_captureHwnd = NULL;
+            TRACE("MouseTrack is now off\n");
+        }
 
-        m_mouseTrackDisabled = bDisable;
     }
 }
 
-HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd)
+HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd, StackEntry ** pentry)
 {
-    int i = m_bandCount;
-    while (--i >= 0)
+    if (pentry)
+        *pentry = NULL;
+
+    for (int i = m_bandCount; --i >= 0;)
     {
-        CMenuBand * band = m_bandStack[i];
+        StackEntry& entry = m_bandStack[i];
 
-        HWND hwnd;
-        HRESULT hr = band->_GetTopLevelWindow(&hwnd);
-        if (FAILED_UNEXPECTEDLY(hr))
-            return hr;
+        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 (hwnd == hWnd)
-            return S_OK;
+        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;
@@ -196,180 +289,372 @@ HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd)
 
 LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg)
 {
-    HWND parent;
     HWND child;
-    POINT pt;
-    int iHitTestResult;
+    int iHitTestResult = -1;
+
+    POINT pt2 = { GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam) };
+    ClientToScreen(msg->hwnd, &pt2);
+
+    // Don't do anything if the mouse has not been moved
+    POINT pt = msg->pt;
+    if (pt.x == m_ptPrev.x && pt.y == m_ptPrev.y)
+        return TRUE;
+
+    // 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 %d\n", m_isLButtonDown);
+    }
+
+    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 (SendMessage(child, WM_USER_ISTRACKEDITEM, iHitTestResult, 0) == S_FALSE)
+        {
+            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 (cCapture == m_captureHwnd)
+            SetCapture(NULL);
+    }
+
+    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)
+{
+    HWND child;
+    int iHitTestResult = -1;
+
+    TRACE("ProcessMouseDown %d %d %d\n", msg->message, msg->wParam, msg->lParam);
 
-    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;
 
-    parent = WindowFromPoint(pt);
 
-    ScreenToClient(parent, &pt);
+    POINT pt = msg->pt;
 
-    child = ChildWindowFromPoint(parent, pt);
+    child = WindowFromPoint(pt);
 
-    if (child != m_parentToolbar)
+    StackEntry * entry = NULL;
+    if (IsTrackedWindow(child, &entry) != S_OK)
         return TRUE;
 
-    ScreenToClient(m_parentToolbar, &msg->pt);
+    TRACE("MouseDown %d\n", m_isLButtonDown);
+
+    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);
+        }
+    }
+
+    msg->message = WM_NULL;
+
+    m_isLButtonDown = TRUE;
+    m_movedSinceDown = FALSE;
+    m_windowAtDown = child;
+
+    TRACE("MouseDown end %d\n", m_isLButtonDown);
+
+    return TRUE;
+}
+
+LRESULT CMenuFocusManager::ProcessMouseUp(MSG* msg)
+{
+    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;
 
-    /* Don't do anything if the mouse has not been moved */
-    if (msg->pt.x == m_ptPrev.x && msg->pt.y == m_ptPrev.y)
+    if (!m_isLButtonDown)
         return TRUE;
 
-    m_ptPrev = msg->pt;
+    m_isLButtonDown = FALSE;
 
-    iHitTestResult = SendMessageW(m_parentToolbar, TB_HITTEST, 0, (LPARAM) &msg->pt);
+    POINT pt = msg->pt;
 
-    /* Make sure that iHitTestResult is one of the menu items and that it is not the current menu item */
-    if (iHitTestResult >= 0)
+    child = WindowFromPoint(pt);
+
+    StackEntry * entry = NULL;
+    if (IsTrackedWindow(child, &entry) != S_OK)
+        return TRUE;
+
+    TRACE("MouseUp %d\n", m_isLButtonDown);
+
+    if (entry)
     {
-        HWND hwndToolbar = m_parentToolbar;
-        if (SendMessage(hwndToolbar, WM_USER_ISTRACKEDITEM, iHitTestResult, 0))
+        ScreenToClient(child, &pt);
+        iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
+
+        if (iHitTestResult >= 0)
         {
-            DbgPrint("Hot item tracking detected a change...\n");
-            if (m_currentMenu)
-                SendMessage(m_currentFocus, WM_CANCELMODE, 0, 0);
-            else
-                m_currentBand->_MenuItemHotTrack(MPOS_SELECTLEFT);
-            DbgPrint("Active popup cancelled, notifying of change...\n");
-            PostMessage(hwndToolbar, WM_USER_CHANGETRACKEDITEM, iHitTestResult, iHitTestResult);
-            return TRUE;
+            TRACE("MouseUp send %d\n", iHitTestResult);
+            entry->mb->_MenuBarMouseUp(child, iHitTestResult);
         }
     }
 
     return TRUE;
 }
 
-LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam)
+LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam)
 {
     if (nCode < 0)
-        return CallNextHookEx(m_hMsgFilterHook, nCode, wParam, lParam);
+        return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
 
     if (nCode == MSGF_MENU)
     {
         BOOL callNext = TRUE;
-        MSG* msg = reinterpret_cast<MSG*>(lParam);
-
-        // Do whatever is necessary here
+        MSG* msg = reinterpret_cast<MSG*>(hookLParam);
 
         switch (msg->message)
         {
+        case WM_NCLBUTTONDOWN:
+        case WM_LBUTTONDOWN:
+        case WM_NCRBUTTONDOWN:
+        case WM_RBUTTONDOWN:
+            if (m_menuBar)
+            {
+                POINT pt = msg->pt;
+                HWND child = WindowFromPoint(pt);
+                BOOL hoveringMenuBar = m_menuBar->mb->IsWindowOwner(child) == S_OK;
+                if (hoveringMenuBar)
+                {
+                    m_menuBar->mb->_DisableMouseTrack(TRUE);
+                    if (m_current->type == TrackedMenuEntry)
+                    {
+                        SendMessage(m_parent->hwnd, WM_CANCELMODE, 0, 0);
+                        msg->message = WM_NULL;
+                    }
+                }
+            }
+            break;
+        case WM_NCLBUTTONUP:
+        case WM_LBUTTONUP:
+        case WM_NCRBUTTONUP:
+        case WM_RBUTTONUP:
+            if (m_current && m_current->type != TrackedMenuEntry)
+            {
+                msg->message = WM_NULL;
+            }
+            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->_MenuItemHotTrack(VK_LEFT);
+                }
+                break;
+            case VK_RIGHT:
+                if (m_selectedItem < 0 || !(m_selectedItemFlags & MF_POPUP))
+                {
+                    m_parent->mb->_MenuItemHotTrack(VK_RIGHT);
+                }
+                break;
+            }
+            break;
         }
 
         if (!callNext)
-            return 0;
+            return 1;
     }
 
-    return CallNextHookEx(m_hMsgFilterHook, nCode, wParam, lParam);
+    return CallNextHookEx(m_hMsgFilterHook, nCode, hookWParam, hookLParam);
 }
 
-LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
+LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM hookWParam, LPARAM hookLParam)
 {
+    BOOL isLButton = FALSE;
     if (nCode < 0)
-        return CallNextHookEx(m_hGetMsgHook, nCode, wParam, lParam);
-
-    LPARAM pos = (LPARAM) GetMessagePos();
-
+        return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
+    
     if (nCode == HC_ACTION)
     {
         BOOL callNext = TRUE;
-        MSG* msg = reinterpret_cast<MSG*>(lParam);
-
-        // Do whatever is necessary here
+        MSG* msg = reinterpret_cast<MSG*>(hookLParam);
+        POINT pt = msg->pt;
 
         switch (msg->message)
         {
-        case WM_CLOSE:
-            break;
-
         case WM_NCLBUTTONDOWN:
         case WM_LBUTTONDOWN:
-        {
-            POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
-
-            HWND window = GetAncestor(WindowFromPoint(pt), GA_ROOT);
+            isLButton = TRUE;
 
-            if (IsTrackedWindow(window) != S_OK)
+            // fallthrough;
+        case WM_NCRBUTTONDOWN:
+        case WM_RBUTTONDOWN:
+            if (m_current->type == MenuPopupEntry)
             {
-                DisableMouseTrack(NULL, FALSE);
-                m_currentBand->_MenuItemHotTrack(MPOS_FULLCANCEL);
-            }
-
-            break;
-        }
-        case WM_MOUSEMOVE:
-            if (m_lastMoveFlags != wParam || m_lastMovePos != pos)
-            {
-                m_lastMoveFlags = wParam;
-                m_lastMovePos = pos;
-
-                POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
+                HWND child = WindowFromPoint(pt);
 
-                HWND window = WindowFromPoint(pt);
-
-                if (IsTrackedWindow(window) == S_OK)
-                {
-                    DisableMouseTrack(window, FALSE);
-                }
-                else
+                if (IsTrackedWindowOrParent(child) != S_OK)
                 {
-                    DisableMouseTrack(NULL, FALSE);
+                    SetCapture(NULL);
+                    m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
+                    break;
                 }
             }
+            
+            if (isLButton)
+            {
+                ProcessMouseDown(msg);
+            }
+            break;
+        case WM_NCLBUTTONUP:
+        case WM_LBUTTONUP:
+            ProcessMouseUp(msg);
+            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_currentMenu)
+            if (m_current->type == MenuPopupEntry)
             {
-                DisableMouseTrack(m_currentFocus, TRUE);
+                DisableMouseTrack(m_current->hwnd, TRUE);
                 switch (msg->wParam)
                 {
+                case VK_ESCAPE:
                 case VK_MENU:
                 case VK_LMENU:
                 case VK_RMENU:
-                    m_currentBand->_MenuItemHotTrack(MPOS_FULLCANCEL);
+                    m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
+                    break;
+                case VK_RETURN:
+                    m_current->mb->_MenuItemHotTrack(MPOS_EXECUTE);
                     break;
                 case VK_LEFT:
-                    m_currentBand->_MenuItemHotTrack(MPOS_SELECTLEFT);
+                    m_current->mb->_MenuItemHotTrack(VK_LEFT);
                     break;
                 case VK_RIGHT:
-                    m_currentBand->_MenuItemHotTrack(MPOS_SELECTRIGHT);
+                    m_current->mb->_MenuItemHotTrack(VK_RIGHT);
                     break;
                 case VK_UP:
-                    m_currentBand->_MenuItemHotTrack(VK_UP);
+                    m_current->mb->_MenuItemHotTrack(VK_UP);
                     break;
                 case VK_DOWN:
-                    m_currentBand->_MenuItemHotTrack(VK_DOWN);
+                    m_current->mb->_MenuItemHotTrack(VK_DOWN);
                     break;
                 }
+                msg->message = WM_NULL;
+                msg->lParam = 0;
+                msg->wParam = 0;
             }
             break;
         }
 
         if (!callNext)
-            return 0;
+            return 1;
     }
 
-    return CallNextHookEx(m_hGetMsgHook, nCode, wParam, lParam);
+    return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
 }
 
 HRESULT CMenuFocusManager::PlaceHooks()
 {
-    //SetCapture(window);
-    if (m_currentMenu)
+    if (m_hMsgFilterHook)
+    {
+        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)
     {
-        DbgPrint("Entering MSGFILTER hook...\n");
+        TRACE("Entering MSGFILTER hook...\n");
         m_hMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, s_MsgFilterHook, NULL, m_threadId);
     }
     else
     {
-        DbgPrint("Entering GETMESSAGE hook...\n");
+        TRACE("Entering GETMESSAGE hook...\n");
         m_hGetMsgHook = SetWindowsHookEx(WH_GETMESSAGE, s_GetMsgHook, NULL, m_threadId);
     }
     return S_OK;
@@ -377,7 +662,7 @@ HRESULT CMenuFocusManager::PlaceHooks()
 
 HRESULT CMenuFocusManager::RemoveHooks()
 {
-    DbgPrint("Removing all hooks...\n");
+    TRACE("Removing all hooks...\n");
     if (m_hMsgFilterHook)
         UnhookWindowsHookEx(m_hMsgFilterHook);
     if (m_hGetMsgHook)
@@ -387,152 +672,265 @@ HRESULT CMenuFocusManager::RemoveHooks()
     return S_OK;
 }
 
-HRESULT CMenuFocusManager::UpdateFocus(CMenuBand * newBand, HMENU popupToTrack)
+HRESULT CMenuFocusManager::UpdateFocus()
 {
     HRESULT hr;
-    HWND newFocus = NULL;
-    HWND oldFocus = m_currentFocus;
-    HMENU oldMenu = m_currentMenu;
+    StackEntry * old = m_current;
 
-    if (newBand)
+    TRACE("UpdateFocus\n");
+
+    if (old)
+        SetCapture(NULL);
+
+    if (m_bandCount > 0)
+        m_current = &(m_bandStack[m_bandCount - 1]);
+    else
+        m_current = NULL;
+
+    if (m_current && m_current->type != TrackedMenuEntry)
     {
-        hr = newBand->_GetTopLevelWindow(&newFocus);
+        hr = m_current->mb->_GetTopLevelWindow(&(m_current->hwnd));
         if (FAILED_UNEXPECTEDLY(hr))
             return hr;
     }
 
-    m_currentBand = newBand;
-    m_currentMenu = popupToTrack;
-    m_currentFocus = newFocus;
-    m_parentToolbar = NULL;
-    if (popupToTrack)
+    if (m_bandCount >= 2)
+    {
+        m_parent = &(m_bandStack[m_bandCount - 2]);
+        _ASSERT(m_parent->type != TrackedMenuEntry);
+    }
+    else
+    {
+        m_parent = NULL;
+    }
+
+    if (m_bandCount >= 1 && m_bandStack[0].type == MenuBarEntry)
     {
-        m_currentBand->GetWindow(&m_parentToolbar);
+        m_menuBar = &(m_bandStack[0]);
     }
-    else if (m_bandCount >= 2)
+    else
     {
-        m_bandStack[m_bandCount - 2]->GetWindow(&m_parentToolbar);
+        m_menuBar = NULL;
     }
 
-    if (oldFocus && (!newFocus || (oldMenu != popupToTrack)))
+    if (old && (!m_current || old->type != m_current->type))
     {
-        DisableMouseTrack(NULL, FALSE);
+        if (m_current && m_current->type != TrackedMenuEntry)
+        {
+            DisableMouseTrack(m_current->hwnd, FALSE);
+        }
 
         hr = RemoveHooks();
         if (FAILED_UNEXPECTEDLY(hr))
             return hr;
     }
-    
-    if (newFocus && (!oldFocus || (oldMenu != popupToTrack)))
+
+    if (m_current && (!old || old->type != m_current->type))
     {
         hr = PlaceHooks();
         if (FAILED_UNEXPECTEDLY(hr))
             return hr;
     }
 
+    if (m_parent)
+    {
+        DisableMouseTrack(m_parent->hwnd, TRUE);
+    }
+
+    if ((m_current && m_current->type == MenuPopupEntry) &&
+        (!m_parent || m_parent->type == MenuBarEntry))
+    {
+        // When the mouse moves, it should set itself to the proper band
+        SetCapture(m_current->hwnd);
+
+        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;
 }
 
-HRESULT CMenuFocusManager::PushMenu(CMenuBand * mb)
+HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb)
 {
-    HRESULT hr;
+    DbgPrint("PushMenuBar %p\n", mb);
+
+    mb->AddRef();
 
-    CMenuBand * mbParent = m_currentBand;
+    _ASSERT(m_bandCount == 0);
 
-    hr = PushToArray(mb);
+    HRESULT hr = PushToArray(MenuBarEntry, mb, NULL);
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
-    if (mbParent)
+    return UpdateFocus();
+}
+
+HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb)
+{
+    DbgPrint("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();
+
+    if (m_parent && m_parent->type != TrackedMenuEntry)
     {
-        mbParent->_SetChildBand(mb);
+        m_parent->mb->_SetChildBand(mb);
+        mb->_SetParentBand(m_parent->mb);
     }
 
-    return UpdateFocus(mb);
+    return hr;
 }
 
-HRESULT CMenuFocusManager::PopMenu(CMenuBand * mb)
+HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup)
 {
-    CMenuBand * mbc;
-    HRESULT hr;
+    DbgPrint("PushTrackedPopup %p\n", popup);
 
-    HWND newFocus;
-    hr = mb->_GetTopLevelWindow(&newFocus);
+    _ASSERT(m_bandCount > 0);
+    _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
+
+    HRESULT hr = PushToArray(TrackedMenuEntry, NULL, popup);
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
-    DbgPrint("Trying to pop %08p, hwnd=%08x\n", mb, newFocus);
+    DbgPrint("PushTrackedPopup %p\n", popup);
+    m_selectedMenu = popup;
+    m_selectedItem = -1;
+    m_selectedItemFlags = 0;
 
-    do {
-        hr = PopFromArray(&mbc);
-        if (FAILED_UNEXPECTEDLY(hr))
-        {
-            UpdateFocus(NULL);
-            return hr;
-        }
+    return UpdateFocus();
+}
+
+HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb)
+{
+    StackEntryType type;
+    CMenuBand * mbc;
+    HRESULT hr;
+
+    DbgPrint("PopMenuBar %p\n", mb);
+
+    if (m_current == m_entryUnderMouse)
+    {
+        m_entryUnderMouse = NULL;
     }
-    while (mbc && mb != mbc);
 
-    if (!mbc)
-        return E_FAIL;
-    
-    hr = PeekArray(&mb);
+    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);
 
-    hr = UpdateFocus(mb);
+    mbc->Release();
+
+    hr = UpdateFocus();
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
-    if (mb)
+    if (m_current)
     {
-        mb->_SetChildBand(NULL);
+        _ASSERT(m_current->type != TrackedMenuEntry);
+        m_current->mb->_SetChildBand(NULL);
     }
 
     return S_OK;
 }
 
-HRESULT CMenuFocusManager::PushTrackedPopup(CMenuBand * mb, HMENU popup)
+HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb)
 {
+    StackEntryType type;
+    CMenuBand * mbc;
     HRESULT hr;
 
-    hr = PushToArray(mb);
+    DbgPrint("PopMenuPopup %p\n", mb);
+
+    if (m_current == m_entryUnderMouse)
+    {
+        m_entryUnderMouse = NULL;
+    }
+
+    hr = PopFromArray(&type, &mbc, NULL);
     if (FAILED_UNEXPECTEDLY(hr))
+    {
+        UpdateFocus();
         return hr;
+    }
 
-    return UpdateFocus(mb, popup);
-}
+    _ASSERT(type == MenuPopupEntry);
+    if (type != MenuPopupEntry)
+        return E_FAIL;
 
-HRESULT CMenuFocusManager::PopTrackedPopup(CMenuBand * mb, HMENU popup)
-{
-    CMenuBand * mbc;
-    HRESULT hr;
+    if (!mbc)
+        return E_FAIL;
+
+    mbc->_SetParentBand(NULL);
 
-    HWND newFocus;
-    hr = mb->_GetTopLevelWindow(&newFocus);
+    mbc->Release();
+
+    hr = UpdateFocus();
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
-    DbgPrint("Trying to pop %08p, hwnd=%08x\n", mb, newFocus);
+    if (m_current)
+    {
+        _ASSERT(m_current->type != TrackedMenuEntry);
+        m_current->mb->_SetChildBand(NULL);
+    }
+
+    return S_OK;
+}
 
-    do {
-        hr = PopFromArray(&mbc);
-        if (FAILED_UNEXPECTEDLY(hr))
-        {
-            UpdateFocus(NULL);
-            return hr;
-        }
-    } while (mbc && mb != mbc);
+HRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup)
+{
+    StackEntryType type;
+    HMENU hmenu;
+    HRESULT hr;
 
-    if (!mbc)
-        return E_FAIL;
+    DbgPrint("PopTrackedPopup %p\n", popup);
 
-    hr = PeekArray(&mb);
+    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(mb);
+    hr = UpdateFocus();
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;