[RSHELL]
[reactos.git] / base / shell / rshell / CMenuBand.cpp
index 8c5a816..a9979b9 100644 (file)
@@ -35,19 +35,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(CMenuBand);
 extern "C"
 HRESULT WINAPI CMenuBand_Constructor(REFIID riid, LPVOID *ppv)
 {
-    *ppv = NULL;
-
-    CMenuBand * site = new CComObject<CMenuBand>();
-
-    if (!site)
-        return E_OUTOFMEMORY;
-
-    HRESULT hr = site->QueryInterface(riid, ppv);
-
-    if (FAILED_UNEXPECTEDLY(hr))
-        site->Release();
-
-    return hr;
+    return ShellObjectCreator<CMenuBand>(riid, ppv);
 }
 
 CMenuBand::CMenuBand() :
@@ -66,7 +54,11 @@ CMenuBand::CMenuBand() :
     m_hotBar(NULL),
     m_hotItem(-1),
     m_popupBar(NULL),
-    m_popupItem(-1)
+    m_popupItem(-1),
+    m_Show(FALSE),
+    m_shellBottom(FALSE),
+    m_trackedPopup(NULL),
+    m_trackedHwnd(NULL)
 {
     m_focusManager = CMenuFocusManager::AcquireManager();
 }
@@ -80,6 +72,9 @@ CMenuBand::~CMenuBand()
 
     if (m_SFToolbar)
         delete m_SFToolbar;
+
+    if (m_hmenu)
+        DestroyMenu(m_hmenu);
 }
 
 HRESULT STDMETHODCALLTYPE  CMenuBand::Initialize(
@@ -112,7 +107,10 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::GetMenuInfo(
         return E_INVALIDARG;
 
     if (ppsmc)
+    {
+        m_psmc->AddRef();
         *ppsmc = m_psmc;
+    }
 
     if (puId)
         *puId = m_uId;
@@ -134,6 +132,12 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::SetMenu(
 
     BOOL created = FALSE;
 
+    if (m_hmenu)
+    {
+        DestroyMenu(m_hmenu);
+        m_hmenu = NULL;
+    }
+
     if (m_staticToolbar == NULL)
     {
         m_staticToolbar = new CMenuStaticToolbar(this);
@@ -246,8 +250,7 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::GetSite(REFIID riid, PVOID *ppvSite)
     return m_site->QueryInterface(riid, ppvSite);
 }
 
-HRESULT STDMETHODCALLTYPE CMenuBand::GetWindow(
-    HWND *phwnd)
+HRESULT STDMETHODCALLTYPE CMenuBand::GetWindow(HWND *phwnd)
 {
     if (m_SFToolbar != NULL)
         return m_SFToolbar->GetWindow(phwnd);
@@ -255,6 +258,8 @@ HRESULT STDMETHODCALLTYPE CMenuBand::GetWindow(
     if (m_staticToolbar != NULL)
         return m_staticToolbar->GetWindow(phwnd);
 
+    if (phwnd) *phwnd = NULL;
+
     return E_FAIL;
 }
 
@@ -282,25 +287,48 @@ HRESULT STDMETHODCALLTYPE CMenuBand::OnPosRectChangeDB(RECT *prc)
     int syStatic = maxStatic.cy;
     int syShlFld = sy - syStatic;
 
-    if (m_SFToolbar)
+    if (m_shellBottom)
     {
-        m_SFToolbar->SetPosSize(
-            prc->left,
-            prc->top,
-            prc->right - prc->left,
-            syShlFld);
+        if (m_SFToolbar)
+        {
+            m_SFToolbar->SetPosSize(
+                prc->left,
+                prc->top + syStatic,
+                prc->right - prc->left,
+                syShlFld);
+        }
+        if (m_staticToolbar)
+        {
+            m_staticToolbar->SetPosSize(
+                prc->left,
+                prc->top,
+                prc->right - prc->left,
+                syStatic);
+        }
     }
-    if (m_staticToolbar)
+    else // shell menu on top
     {
-        m_staticToolbar->SetPosSize(
-            prc->left,
-            prc->top + syShlFld,
-            prc->right - prc->left,
-            syStatic);
+        if (m_SFToolbar)
+        {
+            m_SFToolbar->SetPosSize(
+                prc->left,
+                prc->top,
+                prc->right - prc->left,
+                syShlFld);
+        }
+        if (m_staticToolbar)
+        {
+            m_staticToolbar->SetPosSize(
+                prc->left,
+                prc->top + syShlFld,
+                prc->right - prc->left,
+                syStatic);
+        }
     }
 
     return S_OK;
 }
+
 HRESULT STDMETHODCALLTYPE  CMenuBand::GetBandInfo(
     DWORD dwBandID,
     DWORD dwViewMode,
@@ -330,20 +358,20 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::GetBandInfo(
 
     if (m_dwFlags & SMINIT_VERTICAL)
     {
-        pdbi->ptMinSize.x = max(minStatic.cx, minStatic.cx) + 20;
-        pdbi->ptMinSize.y = minStatic.cy + minStatic.cy;
+        pdbi->ptMinSize.x = max(minStatic.cx, minShlFld.cx) + 20;
+        pdbi->ptMinSize.y = minStatic.cy + minShlFld.cy;
         pdbi->ptMaxSize.x = max(maxStatic.cx, maxShlFld.cx) + 20;
         pdbi->ptMaxSize.y = maxStatic.cy + maxShlFld.cy;
         pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
     }
     else
     {
-        pdbi->ptMinSize.x = minStatic.cx + minStatic.cx;
-        pdbi->ptMinSize.y = max(minStatic.cy, minStatic.cy);
+        pdbi->ptMinSize.x = minStatic.cx + minShlFld.cx;
+        pdbi->ptMinSize.y = max(minStatic.cy, minShlFld.cy);
         pdbi->ptMaxSize.x = maxStatic.cx + maxShlFld.cx;
         pdbi->ptMaxSize.y = max(maxStatic.cy, maxShlFld.cy);
     }
-    pdbi->ptIntegral.x = max(intStatic.cx, intStatic.cx);
+    pdbi->ptIntegral.x = max(intStatic.cx, intShlFld.cx);
     pdbi->ptIntegral.y = max(intStatic.cy, intShlFld.cy);
     pdbi->ptActual = pdbi->ptMinSize;
 
@@ -354,6 +382,11 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::ShowDW(BOOL fShow)
 {
     HRESULT hr = S_OK;
 
+    if (m_Show == fShow)
+        return S_OK;
+
+    m_Show = fShow;
+
     if (m_staticToolbar != NULL)
     {
         hr = m_staticToolbar->ShowWindow(fShow);
@@ -399,13 +432,33 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::ShowDW(BOOL fShow)
 
 HRESULT STDMETHODCALLTYPE CMenuBand::CloseDW(DWORD dwReserved)
 {
+    if (m_subMenuChild)
+    {
+        m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
+    }
+
+    if (m_subMenuChild)
+    {
+        DbgPrint("Child object should have removed itself.\n");
+    }
+
     ShowDW(FALSE);
 
     if (m_staticToolbar != NULL)
-        return m_staticToolbar->Close();
+    {
+        m_staticToolbar->Close();
+    }
 
     if (m_SFToolbar != NULL)
-        return m_SFToolbar->Close();
+    {
+        m_SFToolbar->Close();
+    }
+
+    if (m_site) m_site.Release();
+    if (m_subMenuChild) m_subMenuChild.Release();
+    if (m_subMenuParent) m_subMenuParent.Release();
+    if (m_childBand) m_childBand.Release();
+    if (m_parentBand) m_parentBand.Release();
 
     return S_OK;
 }
@@ -456,6 +509,18 @@ HRESULT STDMETHODCALLTYPE CMenuBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdI
         {
             return S_FALSE;
         }
+        else if (nCmdID == 5) // select an item
+        {
+            if (nCmdexecopt == 0) // first
+            {
+                _KeyboardItemChange(VK_HOME);
+            }
+            else // last
+            {
+                _KeyboardItemChange(VK_END);
+            }
+            return S_FALSE;
+        }
 
         return S_FALSE;
     }
@@ -538,7 +603,7 @@ HRESULT CMenuBand::_SetParentBand(CMenuBand * parent)
 
 HRESULT CMenuBand::_IsPopup()
 {
-    return m_subMenuParent ? S_OK : S_FALSE;
+    return !(m_dwFlags & SMINIT_VERTICAL);
 }
 
 HRESULT CMenuBand::_IsTracking()
@@ -548,28 +613,37 @@ HRESULT CMenuBand::_IsTracking()
 
 HRESULT STDMETHODCALLTYPE CMenuBand::SetClient(IUnknown *punkClient)
 {
+    CComPtr<IMenuPopup> child = m_subMenuChild;
+
     m_subMenuChild = NULL;
+        
+    if (child)
+    {
+        IUnknown_SetSite(child, NULL);
+        child.Release();
+    }
 
     if (!punkClient)
     {
         return S_OK;
     }
 
-    HRESULT hr = punkClient->QueryInterface(IID_PPV_ARG(IMenuPopup, &m_subMenuChild));
-
-    return hr;
+    return punkClient->QueryInterface(IID_PPV_ARG(IMenuPopup, &m_subMenuChild));
 }
 
 HRESULT STDMETHODCALLTYPE CMenuBand::GetClient(IUnknown **ppunkClient)
 {
     // HACK, so I can test for a submenu in the DeskBar
-    if (ppunkClient)
+    if (!ppunkClient)
+        return E_POINTER;
+    *ppunkClient = NULL;
+
+    if (m_subMenuChild)
     {
-        if (m_subMenuChild)
-            *ppunkClient = m_subMenuChild;
-        else
-            *ppunkClient = NULL;
+        m_subMenuChild->AddRef();
+        *ppunkClient = m_subMenuChild;
     }
+
     return S_OK;
 }
 
@@ -594,6 +668,8 @@ HRESULT STDMETHODCALLTYPE CMenuBand::SetShellFolder(IShellFolder *psf, LPCITEMID
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
+    m_shellBottom = (dwFlags & SMSET_BOTTOM) != 0;
+
     if (m_site)
     {
         HWND hwndParent;
@@ -687,20 +763,19 @@ HRESULT CMenuBand::_CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam, UINT id, LPI
 HRESULT CMenuBand::_TrackSubMenu(HMENU popup, INT x, INT y, RECT& rcExclude)
 {
     TPMPARAMS params = { sizeof(TPMPARAMS), rcExclude };
+    UINT      flags  = TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_LEFTALIGN;
+    HWND      hwnd   = m_menuOwner ? m_menuOwner : m_topLevelWindow;
 
-    UINT flags = TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_LEFTALIGN;
+    m_trackedPopup = popup;
+    m_trackedHwnd = hwnd;
 
     m_focusManager->PushTrackedPopup(popup);
-    if (m_menuOwner)
-    {
-        ::TrackPopupMenuEx(popup, flags, x, y, m_menuOwner, &params);
-    }
-    else
-    {
-        ::TrackPopupMenuEx(popup, flags, x, y, m_topLevelWindow, &params);
-    }
+    ::TrackPopupMenuEx(popup, flags, x, y, hwnd, &params);
     m_focusManager->PopTrackedPopup(popup);
 
+    m_trackedPopup = NULL;
+    m_trackedHwnd = NULL;
+
     _DisableMouseTrack(FALSE);
 
     return S_OK;
@@ -715,9 +790,11 @@ HRESULT CMenuBand::_TrackContextMenu(IContextMenu * contextMenu, INT x, INT y)
     if (popup == NULL)
         return E_FAIL;
 
+    TRACE("Before Query\n");
     hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL);
     if (FAILED_UNEXPECTEDLY(hr))
     {
+        TRACE("Query failed\n");
         DestroyMenu(popup);
         return hr;
     }
@@ -725,17 +802,26 @@ HRESULT CMenuBand::_TrackContextMenu(IContextMenu * contextMenu, INT x, INT y)
     HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow;
 
     m_focusManager->PushTrackedPopup(popup);
-    uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, m_menuOwner, NULL);
-    m_focusManager->PopTrackedPopup(popup);
 
-    if (uCommand == 0)
-        return S_FALSE;
+    TRACE("Before Tracking\n");
+    uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, hwnd, NULL);
+
+    m_focusManager->PopTrackedPopup(popup);
 
-    CMINVOKECOMMANDINFO cmi = { 0 };
-    cmi.cbSize = sizeof(cmi);
-    cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
-    cmi.hwnd = hwnd;
-    hr = contextMenu->InvokeCommand(&cmi);
+    if (uCommand != 0)
+    {
+        TRACE("Before InvokeCommand\n");
+        CMINVOKECOMMANDINFO cmi = { 0 };
+        cmi.cbSize = sizeof(cmi);
+        cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
+        cmi.hwnd = hwnd;
+        hr = contextMenu->InvokeCommand(&cmi);
+    }
+    else
+    {
+        TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError());
+        hr = S_FALSE;
+    }
 
     DestroyMenu(popup);
     return hr;
@@ -786,13 +872,21 @@ HRESULT  CMenuBand::_KeyboardItemChange(DWORD change)
 
     if (!tb)
     {
-        // If no hot item was selected
-        // choose the first toolbar (prefer shell-folder, which will be positionedat the top)
-
-        if (m_SFToolbar)
-            tb = m_SFToolbar;
-        else
-            tb = m_staticToolbar;
+        // If no hot item was selected choose the appropriate toolbar
+        if (change == VK_UP || change == VK_END)
+        {
+            if (m_staticToolbar)
+                tb = m_staticToolbar;
+            else
+                tb = m_SFToolbar;
+        }
+        else if (change == VK_DOWN || change == VK_HOME)
+        {
+            if (m_SFToolbar)
+                tb = m_SFToolbar;
+            else
+                tb = m_staticToolbar;
+        }
     }
 
     // Ask the first toolbar to change
@@ -802,20 +896,21 @@ HRESULT  CMenuBand::_KeyboardItemChange(DWORD change)
         return hr;
 
     // Select the second toolbar based on the first
-    if (tb == m_SFToolbar)
+    if (tb == m_SFToolbar && m_staticToolbar)
         tb = m_staticToolbar;
-    else
+    else if (m_SFToolbar)
         tb = m_SFToolbar;
 
     if (!tb)
         return hr;
 
     // Ask the second toolbar to change
-    return tb->KeyboardItemChange(change == VK_DOWN ? VK_END : VK_HOME);
+    return tb->KeyboardItemChange(change == VK_DOWN ? VK_HOME : VK_END);
 }
 
 HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
 {
+    CComPtr<CMenuBand> safeThis = this;
     HRESULT hr;
 
     if (m_dwFlags & SMINIT_VERTICAL)
@@ -853,21 +948,33 @@ HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
 
     switch (changeType)
     {
+    case MPOS_EXECUTE:
+    {
+        CMenuToolbarBase * tb = m_hotBar;
+        int item = m_hotItem;
+        tb->PrepareExecuteItem(item);
+        if (m_subMenuParent)
+        {
+            m_subMenuParent->OnSelect(changeType);
+        }
+        TRACE("Menu closed, executing item...\n");
+        tb->ExecuteItem();
+        break;
+    }
     case MPOS_SELECTLEFT:
+        if (m_parentBand && m_parentBand->_IsPopup()==S_FALSE)
+            return m_parentBand->_MenuItemHotTrack(VK_LEFT);
+        if (m_subMenuChild)
+            return m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
         if (!m_subMenuParent)
-        {
-            if (m_subMenuChild)
-                return m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
             return S_OK;
-        }
         return m_subMenuParent->OnSelect(MPOS_CANCELLEVEL);
 
     case MPOS_SELECTRIGHT:
-        if (m_hotBar && m_hotItem >= 0)
-        {
-            if (m_hotBar->PopupItem(m_hotItem) == S_OK)
-                return S_FALSE;
-        }
+        if (m_hotBar && m_hotItem >= 0 && m_hotBar->PopupItem(m_hotItem, TRUE) == S_OK)
+            return S_FALSE;
+        if (m_parentBand)
+            return m_parentBand->_MenuItemHotTrack(VK_RIGHT);
         if (!m_subMenuParent)
             return S_OK;
         return m_subMenuParent->OnSelect(MPOS_SELECTRIGHT);
@@ -877,25 +984,34 @@ HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
             return S_OK;
         return m_subMenuParent->OnSelect(changeType);
     }
+
+    return S_OK;
 }
 
 HRESULT CMenuBand::_CancelCurrentPopup()
 {
-    if (!m_subMenuChild)
-        return S_FALSE;
+    if (m_subMenuChild)
+    {
+        HRESULT hr = m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
+        return hr;
+    }
 
-    HRESULT hr = m_subMenuChild->OnSelect(MPOS_CANCELLEVEL);
-    return hr;
+    if (m_trackedPopup)
+    {
+        ::SendMessage(m_trackedHwnd, WM_CANCELMODE, 0, 0);
+        return S_OK;
+    }
+
+    return S_FALSE;
 }
 
-HRESULT CMenuBand::_OnPopupSubMenu(IShellMenu * childShellMenu, POINTL * pAt, RECTL * pExclude)
+HRESULT CMenuBand::_OnPopupSubMenu(IShellMenu * childShellMenu, POINTL * pAt, RECTL * pExclude, BOOL keyInitiated)
 {
     HRESULT hr = 0;
-    IBandSite* pBandSite;
-    IDeskBar* pDeskBar;
+    CComPtr<IBandSite> pBandSite;
+    CComPtr<IDeskBar> pDeskBar;
 
     // Create the necessary objects
-
 #if USE_SYSTEM_MENUSITE
     hr = CoCreateInstance(CLSID_MenuBandSite,
         NULL,
@@ -939,7 +1055,12 @@ HRESULT CMenuBand::_OnPopupSubMenu(IShellMenu * childShellMenu, POINTL * pAt, RE
     else
         IUnknown_SetSite(popup, m_site);
 
-    popup->Popup(pAt, pExclude, MPPF_RIGHT);
+    DWORD flags = MPPF_RIGHT;
+
+    if (keyInitiated && m_dwFlags & SMINIT_VERTICAL)
+        flags |= MPPF_INITIALSELECT;
+
+    popup->Popup(pAt, pExclude, flags);
 
     return S_OK;
 }
@@ -967,6 +1088,29 @@ HRESULT CMenuBand::_KillPopupTimers()
     return hr;
 }
 
+HRESULT CMenuBand::_MenuBarMouseDown(HWND hwnd, INT item)
+{
+    if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK)
+        m_staticToolbar->MenuBarMouseDown(item);
+    if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK)
+        m_SFToolbar->MenuBarMouseDown(item);
+    return S_OK;
+}
+
+HRESULT CMenuBand::_MenuBarMouseUp(HWND hwnd, INT item)
+{
+    if (m_staticToolbar && m_staticToolbar->IsWindowOwner(hwnd) == S_OK)
+        m_staticToolbar->MenuBarMouseUp(item);
+    if (m_SFToolbar && m_SFToolbar->IsWindowOwner(hwnd) == S_OK)
+        m_SFToolbar->MenuBarMouseUp(item);
+    return S_OK;
+}
+
+HRESULT CMenuBand::_HasSubMenu()
+{
+    return m_popupBar ? S_OK : S_FALSE;
+}
+
 HRESULT STDMETHODCALLTYPE CMenuBand::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
 {
     UNIMPLEMENTED;
@@ -1071,14 +1215,15 @@ HRESULT STDMETHODCALLTYPE CMenuBand::IsEmpty(THIS)
 
 HRESULT STDMETHODCALLTYPE CMenuBand::HasFocusIO()
 {
-    UNIMPLEMENTED;
-    return S_OK;
+    if (m_popupBar)
+        return S_OK;
+    return S_FALSE;
 }
 
 HRESULT STDMETHODCALLTYPE CMenuBand::TranslateAcceleratorIO(LPMSG lpMsg)
 {
-    UNIMPLEMENTED;
-    return S_OK;
+    // TODO: Alt down -> toggle menu focus
+    return S_FALSE;
 }
 
 HRESULT STDMETHODCALLTYPE CMenuBand::IsDirty()