[RSHELL]
[reactos.git] / base / shell / rshell / CMenuBand.cpp
index 127ef22..a9979b9 100644 (file)
@@ -35,26 +35,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(CMenuBand);
 extern "C"
 HRESULT WINAPI CMenuBand_Constructor(REFIID riid, LPVOID *ppv)
 {
-#if USE_SYSTEM_MENUBAND
-    hr = CoCreateInstance(CLSID_MenuBand,
-        NULL,
-        CLSCTX_INPROC_SERVER,
-        riid, ppv);
-#else
-    *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;
-#endif
+    return ShellObjectCreator<CMenuBand>(riid, ppv);
 }
 
 CMenuBand::CMenuBand() :
@@ -73,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();
 }
@@ -87,6 +72,9 @@ CMenuBand::~CMenuBand()
 
     if (m_SFToolbar)
         delete m_SFToolbar;
+
+    if (m_hmenu)
+        DestroyMenu(m_hmenu);
 }
 
 HRESULT STDMETHODCALLTYPE  CMenuBand::Initialize(
@@ -119,7 +107,10 @@ HRESULT STDMETHODCALLTYPE  CMenuBand::GetMenuInfo(
         return E_INVALIDARG;
 
     if (ppsmc)
+    {
+        m_psmc->AddRef();
         *ppsmc = m_psmc;
+    }
 
     if (puId)
         *puId = m_uId;
@@ -141,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);
@@ -253,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);
@@ -262,6 +258,8 @@ HRESULT STDMETHODCALLTYPE CMenuBand::GetWindow(
     if (m_staticToolbar != NULL)
         return m_staticToolbar->GetWindow(phwnd);
 
+    if (phwnd) *phwnd = NULL;
+
     return E_FAIL;
 }
 
@@ -289,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,
@@ -337,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;
 
@@ -361,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);
@@ -406,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;
 }
@@ -463,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;
     }
@@ -545,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()
@@ -555,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;
 }
 
@@ -601,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;
@@ -697,10 +766,16 @@ HRESULT CMenuBand::_TrackSubMenu(HMENU popup, INT x, INT y, RECT& rcExclude)
     UINT      flags  = TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_LEFTALIGN;
     HWND      hwnd   = m_menuOwner ? m_menuOwner : m_topLevelWindow;
 
+    m_trackedPopup = popup;
+    m_trackedHwnd = hwnd;
+
     m_focusManager->PushTrackedPopup(popup);
     ::TrackPopupMenuEx(popup, flags, x, y, hwnd, &params);
     m_focusManager->PopTrackedPopup(popup);
 
+    m_trackedPopup = NULL;
+    m_trackedHwnd = NULL;
+
     _DisableMouseTrack(FALSE);
 
     return S_OK;
@@ -726,9 +801,13 @@ HRESULT CMenuBand::_TrackContextMenu(IContextMenu * contextMenu, INT x, INT y)
 
     HWND hwnd = m_menuOwner ? m_menuOwner : m_topLevelWindow;
 
+    m_focusManager->PushTrackedPopup(popup);
+
     TRACE("Before Tracking\n");
     uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, hwnd, NULL);
 
+    m_focusManager->PopTrackedPopup(popup);
+
     if (uCommand != 0)
     {
         TRACE("Before InvokeCommand\n");
@@ -794,14 +873,14 @@ HRESULT  CMenuBand::_KeyboardItemChange(DWORD change)
     if (!tb)
     {
         // If no hot item was selected choose the appropriate toolbar
-        if (change == VK_UP)
+        if (change == VK_UP || change == VK_END)
         {
             if (m_staticToolbar)
                 tb = m_staticToolbar;
             else
                 tb = m_SFToolbar;
         }
-        else if (change == VK_DOWN)
+        else if (change == VK_DOWN || change == VK_HOME)
         {
             if (m_SFToolbar)
                 tb = m_SFToolbar;
@@ -831,6 +910,7 @@ HRESULT  CMenuBand::_KeyboardItemChange(DWORD change)
 
 HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
 {
+    CComPtr<CMenuBand> safeThis = this;
     HRESULT hr;
 
     if (m_dwFlags & SMINIT_VERTICAL)
@@ -869,9 +949,18 @@ HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
     switch (changeType)
     {
     case MPOS_EXECUTE:
-        m_hotBar->ExecuteItem(m_hotItem);
+    {
+        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);
@@ -882,7 +971,7 @@ HRESULT CMenuBand::_MenuItemHotTrack(DWORD changeType)
         return m_subMenuParent->OnSelect(MPOS_CANCELLEVEL);
 
     case MPOS_SELECTRIGHT:
-        if (m_hotBar && m_hotItem >= 0 && m_hotBar->PopupItem(m_hotItem) == S_OK)
+        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);
@@ -895,29 +984,53 @@ 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,
+        CLSCTX_INPROC_SERVER,
+        IID_PPV_ARG(IBandSite, &pBandSite));
+#else
     hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
+#endif
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
+#if USE_SYSTEM_MENUDESKBAR
+    hr = CoCreateInstance(CLSID_MenuDeskBar,
+        NULL,
+        CLSCTX_INPROC_SERVER,
+        IID_PPV_ARG(IDeskBar, &pDeskBar));
+#else
     hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
+#endif
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
@@ -942,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;
 }
@@ -970,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;
@@ -1074,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()