[RSHELL]
[reactos.git] / base / shell / rshell / CMenuFocusManager.cpp
index ec4a33e..36ef3d0 100644 (file)
@@ -160,6 +160,9 @@ CMenuFocusManager::CMenuFocusManager() :
     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;
@@ -250,6 +253,40 @@ HRESULT CMenuFocusManager::IsTrackedWindow(HWND hWnd, StackEntry ** pentry)
     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;
@@ -268,15 +305,21 @@ LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg)
     if (cCapture && cCapture != m_captureHwnd && m_current->type != TrackedMenuEntry)
         return TRUE;
 
+
+    m_movedSinceDown = TRUE;
+
     m_ptPrev = pt;
 
     child = WindowFromPoint(pt);
 
     StackEntry * entry = NULL;
-    IsTrackedWindow(child, &entry);
+    if (IsTrackedWindow(child, &entry) == S_OK)
+    {
+        TRACE("MouseMove %d\n", m_isLButtonDown);
+    }
 
     BOOL isTracking = FALSE;
-    if (entry)
+    if (entry && (entry->type == MenuBarEntry || m_current->type != TrackedMenuEntry))
     {
         ScreenToClient(child, &pt);
         iHitTestResult = SendMessageW(child, TB_HITTEST, 0, (LPARAM) &pt);
@@ -284,11 +327,11 @@ LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg)
 
         if (SendMessage(child, WM_USER_ISTRACKEDITEM, iHitTestResult, 0) == S_FALSE)
         {
-            TRACE("Hot item tracking detected a change (capture=%p)...\n", m_captureHwnd);
+            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)
+            if (isTracking && iHitTestResult >= 0 && m_current->type == TrackedMenuEntry)
                 SendMessage(entry->hwnd, WM_CANCELMODE, 0, 0);
-            PostMessage(child, WM_USER_CHANGETRACKEDITEM, iHitTestResult, isTracking);
+            PostMessage(child, WM_USER_CHANGETRACKEDITEM, iHitTestResult, MAKELPARAM(isTracking, TRUE));
             if (m_current->type == TrackedMenuEntry)
                 return FALSE;
         }
@@ -330,20 +373,110 @@ LRESULT CMenuFocusManager::ProcessMouseMove(MSG* msg)
     return TRUE;
 }
 
-LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam)
+LRESULT CMenuFocusManager::ProcessMouseDown(MSG* msg)
+{
+    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)
+        return TRUE;
+
+
+    POINT pt = msg->pt;
+
+    child = WindowFromPoint(pt);
+
+    StackEntry * entry = NULL;
+    if (IsTrackedWindow(child, &entry) != S_OK)
+        return TRUE;
+
+    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;
+
+    if (!m_isLButtonDown)
+        return TRUE;
+
+    m_isLButtonDown = FALSE;
+
+    POINT pt = msg->pt;
+
+    child = WindowFromPoint(pt);
+
+    StackEntry * entry = NULL;
+    if (IsTrackedWindow(child, &entry) != S_OK)
+        return TRUE;
+
+    TRACE("MouseUp %d\n", m_isLButtonDown);
+
+    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);
+        }
+    }
+
+    return TRUE;
+}
+
+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);
+        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;
@@ -352,23 +485,37 @@ LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam
                 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:
-            DbgPrint("WM_INITMENUPOPUP %p %p\n", wParam, lParam);
-            m_selectedMenu = reinterpret_cast<HMENU>(lParam);
+            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:
-            DbgPrint("WM_MENUSELECT %p %p\n", wParam, lParam);
-            m_selectedMenu = reinterpret_cast<HMENU>(lParam);
-            m_selectedItem = LOWORD(wParam);
-            m_selectedItemFlags = HIWORD(wParam);
+            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)
@@ -380,7 +527,7 @@ LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam
                 }
                 break;
             case VK_RIGHT:
-                if (!(m_selectedItemFlags & MF_POPUP))
+                if (m_selectedItem < 0 || !(m_selectedItemFlags & MF_POPUP))
                 {
                     m_parent->mb->_MenuItemHotTrack(VK_RIGHT);
                 }
@@ -393,34 +540,50 @@ LRESULT CMenuFocusManager::MsgFilterHook(INT nCode, WPARAM wParam, LPARAM lParam
             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);
+        return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
     
     if (nCode == HC_ACTION)
     {
         BOOL callNext = TRUE;
-        MSG* msg = reinterpret_cast<MSG*>(lParam);
+        MSG* msg = reinterpret_cast<MSG*>(hookLParam);
         POINT pt = msg->pt;
 
         switch (msg->message)
         {
         case WM_NCLBUTTONDOWN:
         case WM_LBUTTONDOWN:
+            isLButton = TRUE;
+
+            // fallthrough;
+        case WM_NCRBUTTONDOWN:
+        case WM_RBUTTONDOWN:
             if (m_current->type == MenuPopupEntry)
             {
                 HWND child = WindowFromPoint(pt);
 
-                if (IsTrackedWindow(child) != S_OK)
+                if (IsTrackedWindowOrParent(child) != S_OK)
                 {
                     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);
@@ -442,6 +605,9 @@ LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
                 case VK_RMENU:
                     m_current->mb->_MenuItemHotTrack(MPOS_FULLCANCEL);
                     break;
+                case VK_RETURN:
+                    m_current->mb->_MenuItemHotTrack(MPOS_EXECUTE);
+                    break;
                 case VK_LEFT:
                     m_current->mb->_MenuItemHotTrack(VK_LEFT);
                     break;
@@ -455,6 +621,9 @@ LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
                     m_current->mb->_MenuItemHotTrack(VK_DOWN);
                     break;
                 }
+                msg->message = WM_NULL;
+                msg->lParam = 0;
+                msg->wParam = 0;
             }
             break;
         }
@@ -463,11 +632,21 @@ LRESULT CMenuFocusManager::GetMsgHook(INT nCode, WPARAM wParam, LPARAM lParam)
             return 1;
     }
 
-    return CallNextHookEx(m_hGetMsgHook, nCode, wParam, lParam);
+    return CallNextHookEx(m_hGetMsgHook, nCode, hookWParam, hookLParam);
 }
 
 HRESULT CMenuFocusManager::PlaceHooks()
 {
+    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)
     {
         TRACE("Entering MSGFILTER hook...\n");
@@ -498,6 +677,8 @@ HRESULT CMenuFocusManager::UpdateFocus()
     HRESULT hr;
     StackEntry * old = m_current;
 
+    TRACE("UpdateFocus\n");
+
     if (old)
         SetCapture(NULL);
 
@@ -584,6 +765,10 @@ HRESULT CMenuFocusManager::UpdateFocus()
 
 HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb)
 {
+    DbgPrint("PushMenuBar %p\n", mb);
+
+    mb->AddRef();
+
     _ASSERT(m_bandCount == 0);
 
     HRESULT hr = PushToArray(MenuBarEntry, mb, NULL);
@@ -595,6 +780,10 @@ HRESULT CMenuFocusManager::PushMenuBar(CMenuBand * mb)
 
 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);
@@ -614,6 +803,8 @@ HRESULT CMenuFocusManager::PushMenuPopup(CMenuBand * mb)
 
 HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup)
 {
+    DbgPrint("PushTrackedPopup %p\n", popup);
+
     _ASSERT(m_bandCount > 0);
     _ASSERT(!m_current || m_current->type != TrackedMenuEntry);
 
@@ -621,6 +812,11 @@ HRESULT CMenuFocusManager::PushTrackedPopup(HMENU popup)
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
+    DbgPrint("PushTrackedPopup %p\n", popup);
+    m_selectedMenu = popup;
+    m_selectedItem = -1;
+    m_selectedItemFlags = 0;
+
     return UpdateFocus();
 }
 
@@ -630,6 +826,13 @@ HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb)
     CMenuBand * mbc;
     HRESULT hr;
 
+    DbgPrint("PopMenuBar %p\n", mb);
+
+    if (m_current == m_entryUnderMouse)
+    {
+        m_entryUnderMouse = NULL;
+    }
+
     hr = PopFromArray(&type, &mbc, NULL);
     if (FAILED_UNEXPECTEDLY(hr))
     {
@@ -646,6 +849,8 @@ HRESULT CMenuFocusManager::PopMenuBar(CMenuBand * mb)
 
     mbc->_SetParentBand(NULL);
 
+    mbc->Release();
+
     hr = UpdateFocus();
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
@@ -665,6 +870,13 @@ HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb)
     CMenuBand * mbc;
     HRESULT hr;
 
+    DbgPrint("PopMenuPopup %p\n", mb);
+
+    if (m_current == m_entryUnderMouse)
+    {
+        m_entryUnderMouse = NULL;
+    }
+
     hr = PopFromArray(&type, &mbc, NULL);
     if (FAILED_UNEXPECTEDLY(hr))
     {
@@ -681,6 +893,8 @@ HRESULT CMenuFocusManager::PopMenuPopup(CMenuBand * mb)
 
     mbc->_SetParentBand(NULL);
 
+    mbc->Release();
+
     hr = UpdateFocus();
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
@@ -700,6 +914,8 @@ HRESULT CMenuFocusManager::PopTrackedPopup(HMENU popup)
     HMENU hmenu;
     HRESULT hr;
 
+    DbgPrint("PopTrackedPopup %p\n", popup);
+
     hr = PopFromArray(&type, NULL, &hmenu);
     if (FAILED_UNEXPECTEDLY(hr))
     {