// XTPSkinObjectMDI.cpp: implementation of the CXTPSkinObjectMDI class. // // This file is a part of the XTREME SKINFRAMEWORK MFC class library. // (c)1998-2012 Codejock Software, All Rights Reserved. // // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN // CONSENT OF CODEJOCK SOFTWARE. // // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A // SINGLE COMPUTER. // // CONTACT INFORMATION: // support@codejock.com // http://www.codejock.com // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPWinThemeWrapper.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPImageManager.h" #include "XTPSkinObject.h" #include "XTPSkinObjectFrame.h" #include "XTPSkinObjectMenu.h" #include "XTPSkinManager.h" #include "XTPSkinManagerSchema.h" #include "XTPSkinDrawTools.h" #define TIMERID_MENUSHOW 0xACC #define TIMERID_MENUHIDE 0xABB #ifndef WM_UNINITMENUPOPUP #define WM_MENURBUTTONUP 0x0122 #define WM_MENUDRAG 0x0123 #define WM_MENUGETOBJECT 0x0124 #define WM_UNINITMENUPOPUP 0x0125 #define WM_MENUCOMMAND 0x0126 #endif #ifndef SPI_GETMENUSHOWDELAY #define SPI_GETMENUSHOWDELAY 0x006A #endif #define TPM_SYSMENU 0x0200L #ifndef HBMMENU_CALLBACK #define MIIM_STRING 0x00000040 #define MIIM_BITMAP 0x00000080 #define MIIM_FTYPE 0x00000100 #define HBMMENU_CALLBACK ((HBITMAP) -1) #define HBMMENU_SYSTEM ((HBITMAP) 1) #define HBMMENU_MBAR_RESTORE ((HBITMAP) 2) #define HBMMENU_MBAR_MINIMIZE ((HBITMAP) 3) #define HBMMENU_MBAR_CLOSE ((HBITMAP) 5) #define HBMMENU_MBAR_CLOSE_D ((HBITMAP) 6) #define HBMMENU_MBAR_MINIMIZE_D ((HBITMAP) 7) #define HBMMENU_POPUP_CLOSE ((HBITMAP) 8) #define HBMMENU_POPUP_RESTORE ((HBITMAP) 9) #define HBMMENU_POPUP_MAXIMIZE ((HBITMAP) 10) #define HBMMENU_POPUP_MINIMIZE ((HBITMAP) 11) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////////// // CXTPSkinObjectApplicationFrame IMPLEMENT_DYNCREATE(CXTPSkinObjectApplicationFrame, CXTPSkinObjectFrame) CXTPSkinObjectApplicationFrame::CXTPSkinObjectApplicationFrame() { m_rcMenuBar.SetRectEmpty(); m_pPopupMenu = new CXTPSkinPopupMenu(); m_pPopupMenu->m_bMenuBar = TRUE; }; CXTPSkinObjectApplicationFrame::~CXTPSkinObjectApplicationFrame() { if (m_pPopupMenu) m_pPopupMenu->InternalRelease(); }; BEGIN_MESSAGE_MAP(CXTPSkinObjectApplicationFrame, CXTPSkinObjectFrame) //{{AFX_MSG_MAP(CXTPSkinObjectMenu) ON_WM_NCCALCSIZE() ON_WM_CONTEXTMENU() //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL CXTPSkinObjectApplicationFrame::FrameHasMenuBar() { if (GetStyle() & WS_CHILD) return FALSE; HMENU hMenu = ::GetMenu(m_hWnd); if (!hMenu) return FALSE; return GetMenuItemCount(hMenu) > 0; } void CXTPSkinObjectApplicationFrame::DrawFrame(CDC* pDC) { if (m_bLockFrameDraw) return; if (!m_rcMenuBar.IsRectEmpty()) { DrawFrameMenuBar(pDC); } CXTPSkinObjectFrame::DrawFrame(pDC); } void CXTPSkinObjectApplicationFrame::UpdateMenuBar() { if (FrameHasMenuBar()) { RebuildMenuItems(); } else { if (m_pPopupMenu) m_pPopupMenu->m_hMenu = NULL; m_rcMenuBar.SetRectEmpty(); } } void CXTPSkinObjectApplicationFrame::RedrawMenuBar() { if (!m_rcMenuBar.IsRectEmpty()) { CWindowDC dc(this); DrawFrameMenuBar(&dc); CXTPClientRect rect(this); if (rect.Height() <= 0) CXTPSkinObjectFrame::DrawFrame(&dc); } } void CXTPSkinObjectApplicationFrame::DrawFrameMenuBar(CDC* pDC) { CXTPSkinManager* pSkinManager = XTPSkinManager(); CXTPSkinManagerClass* pClassWindow = pSkinManager->GetSkinClass(this, _T("WINDOW")); CRect rc = m_rcMenuBar; CXTPBufferDC dc(*pDC, rc); dc.FillSolidRect(rc.left, rc.top, rc.Width(), rc.Height() - 1, GetColor(COLOR_3DFACE)); dc.FillSolidRect(rc.left, rc.bottom - 1, rc.Width(), 1, GetColor(COLOR_WINDOW)); CXTPFontDC font(&dc, &GetMetrics()->m_fntMenu); dc.SetBkMode(TRANSPARENT); int nCount = m_pPopupMenu->GetCount(); for (int nIndex = 0; nIndex < nCount; nIndex++ ) { CXTPSkinPopupMenuItem* pItem = m_pPopupMenu->GetItem(nIndex); CRect rcItem = pItem->m_rcItem; BOOL bSelected = nIndex == m_pPopupMenu->m_nSelected; BOOL bEnabled = pItem->IsEnabled(); HBITMAP hbm = pItem->GetItemBitmap(); if (hbm == HBMMENU_SYSTEM) { HWND hwndItem = (HWND)pItem->GetItemData(); if (hwndItem && IsWindow(hwndItem)) { HICON hIcon = GetSchema()->GetFrameSmIcon(hwndItem, FALSE); if (hIcon) { rcItem = CRect(CPoint(rcItem.CenterPoint().x - 8, rcItem.CenterPoint().y - 8), CSize(16, 16)); DrawIconEx(dc.m_hDC, rcItem.left, rcItem.top, hIcon, rcItem.Width(), rcItem.Height(), 0, NULL, DI_NORMAL); } } continue; } if (pItem->IsSeparator()) continue; if (pItem->IsMDISysButton()) { UINT nID = pItem->GetID(); int nPartID = nID == SC_RESTORE ? WP_MDIRESTOREBUTTON : nID == SC_MINIMIZE ? WP_MDIMINBUTTON : WP_MDICLOSEBUTTON; int nState = !bEnabled ? SBS_DISABLED : bSelected ? SBS_HOT : SBS_NORMAL; pClassWindow->DrawThemeBackground(&dc, nPartID, nState, &rcItem); continue; } CString strMenuText = m_pPopupMenu->GetMenuString(nIndex); if (bSelected) { dc.FillSolidRect(rcItem, GetColor(COLOR_HIGHLIGHT)); } dc.SetTextColor(GetColor(!bEnabled ? COLOR_GRAYTEXT : bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)); dc.DrawText(strMenuText, rcItem, DT_VCENTER | DT_CENTER | DT_SINGLELINE); } } void CXTPSkinObjectApplicationFrame::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) { CXTPSkinObjectFrame::OnNcCalcSize(bCalcValidRects, lpncsp); if (FrameHasMenuBar()) { m_rcMenuBar.SetRect(m_rcBorders.left, m_rcBorders.top, m_rcBorders.left + lpncsp[0].rgrc->right - lpncsp[0].rgrc->left, m_rcBorders.top); RebuildMenuItems(); lpncsp[0].rgrc->top += m_rcMenuBar.Height(); } else { if (m_pPopupMenu) m_pPopupMenu->m_hMenu = NULL; m_rcMenuBar.SetRectEmpty(); } } BOOL CXTPSkinPopupMenuItem::IsMDISysButton() const { int nID = GetID(); if (nID == SC_CLOSE || nID == SC_RESTORE || nID == SC_MINIMIZE) return TRUE; return FALSE; } void CXTPSkinObjectApplicationFrame::RebuildMenuItems() { m_pPopupMenu->RemoveAll(); m_pPopupMenu->m_hMenu = NULL; if (GetStyle() & WS_CHILD) return; HMENU hMenu = ::GetMenu(m_hWnd); if (!hMenu) return; m_pPopupMenu->m_hMenu = hMenu; m_pPopupMenu->m_hWnd = m_hWnd; m_pPopupMenu->m_hWndNotify = m_hWnd; int nCount = ::GetMenuItemCount(hMenu); CWindowDC dc(this); CXTPFontDC font(&dc, &GetMetrics()->m_fntMenu); int nHeight = GetSystemMetrics(SM_CYMENUSIZE); //CRect rc = m_rcMenuBar; int x = m_rcMenuBar.left; int y = m_rcMenuBar.top; TEXTMETRIC tm; dc.GetTextMetrics(&tm); int nIndex; for (nIndex = 0; nIndex < nCount; nIndex++ ) { CXTPSkinPopupMenuItem* pItem = new CXTPSkinPopupMenuItem(m_pPopupMenu, nIndex); CSize sz(0, 0); HBITMAP hbm = pItem->GetItemBitmap(); if ((UINT_PTR)hbm == 1) { sz.cx = 16 + 2 + 2; sz.cy = 16; } else if (pItem->IsSeparator()) sz.cx = 6; else if (pItem->IsMDISysButton()) { sz.cx = sz.cy = nHeight; } else { CString strMenuText = pItem->GetText(); XTPDrawHelpers()->StripMnemonics(strMenuText); int nText = dc.GetTextExtent(strMenuText).cx; sz.cx = nText + (tm.tmAveCharWidth + 1) * 2; sz.cy = nHeight; } if (x + sz.cx > m_rcMenuBar.right && nIndex != 0) { y += nHeight; x = m_rcMenuBar.left; } CRect rcItem(x, y, x + sz.cx, y + nHeight); pItem->m_rcItem = rcItem; m_pPopupMenu->m_arrItems.Add(pItem); x = rcItem.right; } x = m_rcMenuBar.right; for (nIndex = nCount - 1; nIndex >= 0; nIndex--) { CXTPSkinPopupMenuItem* pItem = m_pPopupMenu->GetItem(nIndex); if ((pItem->GetType() & MFT_RIGHTJUSTIFY || pItem->IsMDISysButton()) && pItem->m_rcItem.top == y) { pItem->m_rcItem.OffsetRect(x - pItem->m_rcItem.right, 0); x -= pItem->m_rcItem.Width(); } else { break; } } m_rcMenuBar.bottom = y + nHeight + 1; } int CXTPSkinObjectApplicationFrame::HitTestMenuItem(CPoint point) { if (!m_rcMenuBar.PtInRect(point)) return -1; for (int i = 0; i < m_pPopupMenu->GetCount(); i++) { if (::PtInRect(&m_pPopupMenu->GetItem(i)->m_rcItem, point)) return i; } return -1; } BOOL CXTPSkinObjectApplicationFrame::HandleMouseMove(CPoint point) { if (CXTPSkinObjectFrame::HandleMouseMove(point)) return TRUE; if (m_rcMenuBar.IsRectEmpty()) return FALSE; CPoint ptClient(point); ScreenToFrame(&ptClient); int nSelected = HitTestMenuItem(ptClient); if (nSelected != -1 && HandleNcHitTest(point) != HTMENU) nSelected = -1; if (m_pPopupMenu->m_nSelected != nSelected) { m_pPopupMenu->m_nSelected = nSelected; RedrawMenuBar(); if (nSelected != -1) SetTimer (XTP_TID_MOUSELEAVE, 50, &CXTPSkinObjectFrame::OnTimerInternal); else CancelMouseLeaveTracking(); } return TRUE; } LRESULT CXTPSkinObjectApplicationFrame::HandleNcHitTest(CPoint point) { if (!m_rcMenuBar.IsRectEmpty()) { CPoint ptMenu(point); ScreenToFrame(&ptMenu); CRect rcMenuBar(m_rcMenuBar); CXTPWindowRect rcWindow(this); rcWindow.OffsetRect(-rcWindow.TopLeft()); rcWindow.DeflateRect(m_rcBorders); rcMenuBar.IntersectRect(rcMenuBar, rcWindow); rcMenuBar.top += 1; if (rcMenuBar.PtInRect(ptMenu)) return HTMENU; } return CXTPSkinObjectFrame::HandleNcHitTest(point); } CXTPSkinPopupMenuState* CXTPSkinObjectApplicationFrame::StartMenuState(UINT /*nID*/, LPARAM /*lParam*/) { if (m_pPopupMenu->m_hMenu == 0) { HMENU hMenu = ::GetSystemMenu(m_hWnd, FALSE); if (!hMenu) return NULL; ::SetMenuDefaultItem(hMenu, SC_CLOSE, MF_BYCOMMAND); m_pPopupMenu->m_hMenu = hMenu; m_pPopupMenu->m_bSysMenu = TRUE; m_pPopupMenu->m_hWnd = m_hWnd; m_pPopupMenu->m_hWndNotify = m_hWnd; m_pPopupMenu->PositionSysMenu(); } CXTPSkinPopupMenuState* pState = new CXTPSkinPopupMenuState(m_hWnd); pState->InitMenu(m_pPopupMenu); return pState; } void CXTPSkinPopupMenuState::PlayEventSound(UINT nID) { XTPSoundManager()->PlaySystemSound((XTPSoundManagerState)nID); } void CXTPSkinPopupMenuState::EndState() { if (m_pAlternatePopup) { if (!m_pAlternatePopup->IsSysMenuBar()) { CXTPSkinPopupMenu* pAlternatePopup = m_pAlternatePopup; m_pAlternatePopup = m_pRootPopup; m_pRootPopup = pAlternatePopup; } m_pAlternatePopup->InternalRelease(); } else if (m_pRootPopup && m_pRootPopup->m_bSysMenu) { m_pRootPopup->m_bSysMenu = FALSE; m_pRootPopup->m_hMenu = NULL; m_pRootPopup->m_hWnd = NULL; } if (m_pRootPopup) { m_pRootPopup->InternalRelease(); } delete this; } void CXTPSkinPopupMenuState::FilterMenuKey(LPARAM lParam) { if (m_bButtonDown) return; if (!StartMenu(focusKeyboard)) return; m_bInsideMenuLoop = TRUE; switch (lParam) { case 0: SelectItem(m_pRootPopup, 0); RunLoop(lParam); return; case '-': if ((GetWindowLong(m_pRootPopup->m_hWndNotify, GWL_STYLE) & WS_CHILD) == 0) { break; } // else fall through. case ' ': if (!m_pAlternatePopup && !m_pRootPopup->m_bSysMenu) { Dismiss(); return; } CloseHierarchy(m_pRootPopup); if (!m_pRootPopup->m_bSysMenu) SwitchToAlternateMenu(); SelectItem(m_pRootPopup, 0); OpenHierarchy(m_pRootPopup); m_pRootPopup->m_bToggle = FALSE; RunLoop(lParam); return; } if (m_pRootPopup->m_bSysMenu && m_pRootPopup->m_pState == NULL) { // ALT + key OnChar(m_pRootPopup, (UINT)lParam); } else { OnChar(m_pRootPopup, (UINT)lParam); } if (m_pRootPopup->m_nSelected != -1) { RunLoop(lParam); } else { Dismiss(); } } BOOL CXTPSkinObjectApplicationFrame::HandleSysCommand(UINT nID, LPARAM lParam) { if ((GetSkinManager()->GetApplyOptions() & xtpSkinApplyMenus) == 0) return CXTPSkinObjectFrame::HandleSysCommand(nID, lParam); UINT nCmd = (nID & 0xFFF0); if (nCmd == SC_DEFAULT) { return TRUE; } if (nCmd == SC_MOUSEMENU) { CXTPSkinPopupMenuState* pState = StartMenuState(nID, lParam); if (pState) { pState->RunLoop(lParam); pState->EndState(); } return TRUE; } if (nCmd == SC_KEYMENU) { CXTPSkinPopupMenuState* pState = StartMenuState(nID, lParam); if (pState) { pState->FilterMenuKey(lParam); pState->EndState(); } else { CXTPSkinObjectFrame::HandleSysCommand(nID, lParam); } return TRUE; } return FALSE; } ////////////////////////////////////////////////////////////////////////// // CXTPSkinPopupMenuItem CXTPSkinPopupMenuItem::CXTPSkinPopupMenuItem(CXTPSkinPopupMenu* pPopupMenu, int nItem) { m_hMenu = pPopupMenu->m_hMenu; m_nItem = nItem; m_pPopupMenu = pPopupMenu; m_rcItem.SetRectEmpty(); } CRect CXTPSkinPopupMenuItem::GetScreenPos() const { CRect rcItem(m_rcItem); CXTPSkinPopupMenuState::WindowToScreen(m_pPopupMenu->m_hWnd, rcItem); return rcItem; } HMENU CXTPSkinPopupMenuItem::GetPopupMenu() const { if (m_pPopupMenu->IsSysMenuBar()) return m_hMenu; return ::GetSubMenu(m_hMenu, m_nItem); } DWORD CXTPSkinPopupMenuItem::GetState() const { if (m_pPopupMenu->IsSysMenuBar()) return MF_ENABLED; MENUITEMINFO mii; GetMenuItemInfo(MIIM_STATE, &mii); return mii.fState; } void CXTPSkinPopupMenuItem::GetMenuItemInfo(UINT fMask, LPMENUITEMINFO lpMII) const { ZeroMemory(lpMII, sizeof(MENUITEMINFO)); lpMII->cbSize = sizeof(MENUITEMINFO); lpMII->fMask = fMask; ::GetMenuItemInfo(m_hMenu, m_nItem, TRUE, lpMII); } UINT CXTPSkinPopupMenuItem::GetID() const { MENUITEMINFO mii; GetMenuItemInfo(MIIM_ID, &mii); return mii.wID; } BOOL CXTPSkinPopupMenuItem::IsEnabled() const { return (GetState() & MFS_GRAYED) == 0; } BOOL CXTPSkinPopupMenuItem::IsChecked() const { return (GetState() & MFS_CHECKED); } BOOL CXTPSkinPopupMenuItem::IsDefault() const { return (GetState() & MFS_DEFAULT); } DWORD_PTR CXTPSkinPopupMenuItem::GetItemData() const { MENUITEMINFO mii; GetMenuItemInfo(MIIM_DATA, &mii); return mii.dwItemData; } CString CXTPSkinPopupMenuItem::GetText() const { CString strText; ::GetMenuString(m_hMenu, m_nItem, strText.GetBuffer(256), 256, MF_BYPOSITION); strText.ReleaseBuffer(); return strText; } BOOL CXTPSkinPopupMenuItem::IsSeparator() const { MENUITEMINFO mii; GetMenuItemInfo(MIIM_TYPE, &mii); return mii.fType & MFT_SEPARATOR; } BOOL CXTPSkinPopupMenuItem::IsOwnerDraw() const { MENUITEMINFO mii; GetMenuItemInfo(MIIM_TYPE, &mii); return mii.fType & MFT_OWNERDRAW; } UINT CXTPSkinPopupMenuItem::GetType() const { MENUITEMINFO mii; GetMenuItemInfo(MIIM_TYPE, &mii); return mii.fType; } #ifndef MIIM_BITMAP #define MIIM_BITMAP 0x00000080 #endif HBITMAP CXTPSkinPopupMenuItem::GetItemBitmap() const { struct MENUITEMINFO98 { UINT cbSize; UINT fMask; UINT fType; // used if MIIM_TYPE (4.0) or MIIM_FTYPE (>4.0) UINT fState; // used if MIIM_STATE UINT wID; // used if MIIM_ID HMENU hSubMenu; // used if MIIM_SUBMENU HBITMAP hbmpChecked; // used if MIIM_CHECKMARKS HBITMAP hbmpUnchecked; // used if MIIM_CHECKMARKS DWORD dwItemData; // used if MIIM_DATA LPWSTR dwTypeData; // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0) UINT cch; // used if MIIM_TYPE (4.0) or MIIM_STRING (>4.0) HBITMAP hbmpItem; // used if MIIM_BITMAP }; if (XTPSystemVersion()->IsWin95() || XTPSystemVersion()->IsWinNT4()) return NULL; MENUITEMINFO98 mii; ZeroMemory(&mii, sizeof(MENUITEMINFO98)); mii.cbSize = sizeof(MENUITEMINFO98); mii.fMask = MIIM_BITMAP; ::GetMenuItemInfo(m_hMenu, m_nItem, TRUE, (MENUITEMINFO*)&mii); return mii.hbmpItem; } ////////////////////////////////////////////////////////////////////////// // CXTPSkinPopupMenu CXTPSkinPopupMenu::CXTPSkinPopupMenu() { m_bMenuBar = FALSE; m_bSendUninit = FALSE; m_bSysMenu = FALSE; m_hWnd = 0; m_nSelected = -1; m_nDropped = -1; m_hMenu = NULL; m_pNextPopup = NULL; m_pPrevPopup = NULL; m_bToggle = FALSE; m_bDropNextPopup = FALSE; m_hWndNotify = 0; m_bAboutToHide = FALSE; m_bDroppedLeft = FALSE; m_pState = NULL; m_nHideTimer = 0; m_nShowTimer = 0; m_nGripperWidth = 0; } CXTPSkinPopupMenu::~CXTPSkinPopupMenu() { m_hWnd = NULL; RemoveAll(); } void CXTPSkinPopupMenu::OnFinalRelease() { delete this; } void CXTPSkinPopupMenu::RemoveAll() { for (int i = 0; i < m_arrItems.GetSize(); i++) { delete m_arrItems[i]; } m_arrItems.RemoveAll(); } CString CXTPSkinPopupMenu::GetMenuString(int nIndex) { CString strMenuText; if (m_hMenu) { ::GetMenuString(m_hMenu, nIndex, strMenuText.GetBuffer(256), 256, MF_BYPOSITION); strMenuText.ReleaseBuffer(); } return strMenuText; } void CXTPSkinPopupMenu::RebuildItems() { RemoveAll(); int nCount = !IsSysMenuBar() ? GetMenuItemCount(m_hMenu) : 1; for (int i = 0; i < nCount; i++) { m_arrItems.Add(new CXTPSkinPopupMenuItem(this, i)); } } int CXTPSkinPopupMenu::GetCount() { int nCount = !IsSysMenuBar() ? GetMenuItemCount(m_hMenu) : 1; if (nCount != m_arrItems.GetSize()) { RebuildItems(); } return nCount; } CXTPSkinPopupMenuItem* CXTPSkinPopupMenu::GetItem(int nIndex) const { return nIndex >= 0 && nIndex < m_arrItems.GetSize() ? m_arrItems.GetAt(nIndex) : NULL; } BEGIN_MESSAGE_MAP(CXTPSkinPopupMenu, CWnd) //{{AFX_MSG_MAP(CXTPSkinObjectMenu) ON_WM_PAINT() //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL CXTPSkinPopupMenu::IsMenuThemed() { CXTPSkinManager* pSkinManager = XTPSkinManager(); CXTPSkinManagerClass* pClass = pSkinManager->GetSkinClass(pSkinManager->Lookup(m_hWndNotify), _T("MENU")); BOOL bTheme = pClass->GetThemeInt(XTP_MP_POPUPBACKGROUND, 0, TMT_BORDERSIZE, 0) != 0; if (!bTheme) return FALSE; int nCount = ::GetMenuItemCount(m_hMenu); for (int i = 0; i < nCount; i++) { MENUITEMINFO mii; ZeroMemory(&mii, sizeof(MENUITEMINFO)); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_TYPE; ::GetMenuItemInfo(m_hMenu, i, TRUE, &mii); if (mii.fType & MFT_OWNERDRAW) return FALSE; } return TRUE; } void CXTPSkinPopupMenu::PositionSysMenu() { RebuildItems(); ASSERT(GetCount() == 1); CXTPSkinPopupMenuItem* pItem = GetItem(0); HWND hWnd = m_hWndNotify; DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE); DWORD dwExStyle = GetWindowLong(hWnd, GWL_EXSTYLE); CRect rcBorders = XTPSkinManager()->GetSchema()->CalcFrameBorders(dwStyle, dwExStyle); pItem->m_rcItem.SetRect(rcBorders.left, rcBorders.left, rcBorders.top, rcBorders.top); } CSize CXTPSkinPopupMenu::RecalcLayout() { ASSERT(m_pState); ASSERT(!m_bMenuBar); CXTPSkinManagerMetrics* pMetrics = m_pState->m_pSchema->GetMetrics(); BOOL bTheme = IsMenuThemed(); CDC dc; dc.Attach(::GetWindowDC(m_hWnd)); CXTPFontDC font(&dc, &pMetrics->m_fntMenu); CRect rcBorders(3, 3, 3, 3); int x = rcBorders.left; int y = rcBorders.top; TEXTMETRIC tm; dc.GetTextMetrics(&tm); RemoveAll(); int nCount = ::GetMenuItemCount(m_hMenu), nItem; int nMaxWidthColumn = 0; int nFirstColumn = 0; int nMaxHeight = 0; m_nGripperWidth = bTheme ? 33 : 17; for (nItem = 0; nItem < nCount; nItem++ ) { CXTPSkinPopupMenuItem* pItem = new CXTPSkinPopupMenuItem(this, nItem); MENUITEMINFO mii; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE | MIIM_ID; ::GetMenuItemInfo(m_hMenu, nItem, TRUE, &mii); BOOL bSeparator = pItem->IsSeparator(); if (!pItem->IsOwnerDraw() && !bSeparator) { HBITMAP hbm = pItem->GetItemBitmap(); if (hbm && HIWORD(hbm) != 0) { BITMAP bmpInfo; ::GetObject(hbm, sizeof(BITMAP), &bmpInfo); m_nGripperWidth = max(m_nGripperWidth, bmpInfo.bmWidth + 4 + (bTheme ? 5 + 3 : 0)); } } m_arrItems.Add(pItem); } for (nItem = 0; nItem < nCount; nItem++ ) { CXTPSkinPopupMenuItem* pItem = m_arrItems[nItem]; MENUITEMINFO mii; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE | MIIM_ID; ::GetMenuItemInfo(m_hMenu, nItem, TRUE, &mii); BOOL bSeparator = pItem->IsSeparator(); CString strMenuText = pItem->GetText(); CSize szText(0, 0); if (pItem->IsOwnerDraw()) { MEASUREITEMSTRUCT mis; mis.CtlType = ODT_MENU; mis.CtlID = 0; mis.itemID = pItem->GetID(); mis.itemWidth = 0; mis.itemHeight= (UINT) dc.GetTextExtent(_T(" "), 1).cy; mis.itemData = pItem->GetItemData(); ::SendMessage(m_hWndNotify, WM_MEASUREITEM, 0, (LPARAM)&mis); szText.cx = mis.itemWidth + 12; szText.cy = mis.itemHeight; } else if (bSeparator) { szText.cy = bTheme ? 6 : 9; } else { BOOL bDefault = pItem->IsDefault(); CFont fontDefault; if (bDefault) { LOGFONT lf; pMetrics->m_fntMenu.GetLogFont(&lf); lf.lfWeight = FW_BOLD; fontDefault.CreateFontIndirect(&lf); font.SetFont(&fontDefault); } int nIndex = strMenuText.Find(_T('\t')); CSize szShortcut(0, 0); if (nIndex != -1) { CString strShortcutText = strMenuText.Mid(nIndex); strMenuText.ReleaseBuffer(nIndex); szShortcut = dc.GetTextExtent(strShortcutText); } szText = dc.GetTextExtent(strMenuText); if (bTheme) { szText.cx = m_nGripperWidth + szText.cx + szShortcut.cx + 55; szText.cy = max(szText.cy, 22); } else { szText.cx = m_nGripperWidth + szText.cx + szShortcut.cx + 44; szText.cy = max(szText.cy, 17); } if (bDefault) { font.SetFont(&pMetrics->m_fntMenu); } } if ((pItem->GetType() & (MF_MENUBREAK | MF_MENUBARBREAK)) && (nItem != nFirstColumn)) { for (; nFirstColumn < nItem; nFirstColumn++ ) { CXTPSkinPopupMenuItem* pItem = m_arrItems[nFirstColumn]; pItem->m_rcItem.right = pItem->m_rcItem.left + nMaxWidthColumn; } x += nMaxWidthColumn + 4; nMaxHeight = max(nMaxHeight, y); y = rcBorders.top; nMaxWidthColumn = 0; } if (!pItem->IsOwnerDraw() && !bSeparator) { HBITMAP hbm = pItem->GetItemBitmap(); if (hbm && HIWORD(hbm) != 0) { BITMAP bmpInfo; ::GetObject(hbm, sizeof(BITMAP), &bmpInfo); szText.cy = max(szText.cy, bmpInfo.bmHeight + 4); } } CRect rcItem(x, y, x + szText.cx, y + szText.cy); nMaxWidthColumn = max(nMaxWidthColumn, szText.cx); pItem->m_rcItem = rcItem; y = rcItem.bottom; } for (nItem = nFirstColumn; nItem < nCount; nItem++ ) { CXTPSkinPopupMenuItem* pItem = m_arrItems[nItem]; pItem->m_rcItem.right = pItem->m_rcItem.left + nMaxWidthColumn; } int nTotalWidth = x + nMaxWidthColumn + rcBorders.right; nMaxHeight = max(nMaxHeight, y) + rcBorders.bottom; font.ReleaseFont(); ::ReleaseDC(m_hWnd, dc.Detach()); return CSize(nTotalWidth, nMaxHeight); } void CXTPSkinPopupMenu::OnPaint() { CPaintDC dcPaint(this); CXTPBufferDC dc(dcPaint); if (!m_pState) return; CXTPSkinManager* pSkinManager = XTPSkinManager(); CXTPSkinManagerMetrics* pMetrics = m_pState->m_pSchema->GetMetrics(); CXTPClientRect rc(this); CXTPSkinManagerClass* pClass = pSkinManager->GetSkinClass(pSkinManager->Lookup(m_hWndNotify), _T("MENU")); BOOL bTheme = IsMenuThemed();; if (bTheme) { CRect rcBackground(rc); pClass->DrawThemeBackground(&dc, XTP_MP_POPUPBORDERS, 0, &rcBackground); rcBackground.DeflateRect(3, 3); pClass->DrawThemeBackground(&dc, XTP_MP_POPUPBACKGROUND, 0, &rcBackground); rcBackground.right = rcBackground.left + m_nGripperWidth - 5; pClass->DrawThemeBackground(&dc, XTP_MP_POPUPGUTTER, 0, &rcBackground); } else { dc.FillSolidRect(rc, pMetrics->GetColor(COLOR_MENU)); dc.Draw3dRect(rc, pMetrics->GetColor(COLOR_3DSHADOW), pMetrics->GetColor(COLOR_3DSHADOW)); } CXTPFontDC font(&dc, &pMetrics->m_fntMenu); dc.SetBkMode(TRANSPARENT); int nCount = GetCount(); for (int nIndex = 0; nIndex < nCount; nIndex++) { CXTPSkinPopupMenuItem* pItem = GetItem(nIndex); MENUITEMINFO mii; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE | MIIM_CHECKMARKS | MIIM_ID; ::GetMenuItemInfo(m_hMenu, nIndex, TRUE, &mii); BOOL bSeparator = pItem->IsSeparator(); BOOL bEnabled = pItem->IsEnabled(); BOOL bSelected = (nIndex == m_nSelected) && !bSeparator; CRect rcItem(pItem->m_rcItem); if ((pItem->GetType() & MF_MENUBARBREAK) && (nIndex != 0) && !bTheme) { dc.FillSolidRect(rcItem.left - 3, rc.top + 3, 1, rc.Height() - 6, pMetrics->GetColor(COLOR_3DSHADOW)); dc.FillSolidRect(rcItem.left - 2, rc.top + 3, 1, rc.Height() - 6, pMetrics->GetColor(COLOR_3DHIGHLIGHT)); } if (pItem->IsOwnerDraw()) { dc.SetTextColor(pMetrics->GetColor(COLOR_MENUTEXT)); dc.SetBkColor(pMetrics->GetColor(COLOR_MENU)); DWORD dwState = mii.fState; DRAWITEMSTRUCT dis; dis.CtlType = ODT_MENU; dis.CtlID = 0; dis.itemID = mii.wID; dis.itemAction = ODA_DRAWENTIRE; dis.itemState = ((dwState & MF_GRAYED) ? ODS_GRAYED : 0) | ((dwState & MFS_DEFAULT) ? ODS_DEFAULT : 0) | ((dwState & MFS_CHECKED) ? ODS_CHECKED : 0) | ((dwState & MFS_DISABLED) ? ODS_DISABLED : 0) | (bSelected ? ODS_SELECTED : 0); dis.hwndItem = (HWND)m_hMenu; dis.hDC = dc.GetSafeHdc(); dis.rcItem = pItem->m_rcItem; dis.itemData = pItem->GetItemData(); if (m_hWndNotify) { ::SendMessage(m_hWndNotify, WM_DRAWITEM, 0, (LPARAM)&dis); } if (pItem->GetPopupMenu()) { CPoint ptCenter(rcItem.right - (bTheme ? 10 : 5), rcItem.CenterPoint().y); XTPDrawHelpers()->Triangle(&dc, ptCenter, CPoint(ptCenter.x - 3, ptCenter.y - 3), CPoint(ptCenter.x - 3, ptCenter.y + 3), pMetrics->GetColor(!bEnabled ? COLOR_GRAYTEXT : bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)); } continue; } if (bSeparator) { if (!bTheme) { dc.FillSolidRect(rcItem.left + 1, rcItem.CenterPoint().y, rcItem.Width() - 2, 1, pMetrics->GetColor(COLOR_3DSHADOW)); dc.FillSolidRect(rcItem.left + 1, rcItem.CenterPoint().y + 1, rcItem.Width() - 2, 1, pMetrics->GetColor(COLOR_3DHIGHLIGHT)); } else { CRect rcSeparator(rcItem.left + m_nGripperWidth - 5, rcItem.top, rcItem.right, rcItem.bottom); pClass->DrawThemeBackground(&dc, XTP_MP_POPUPSEPARATOR, 0, &rcSeparator); } continue; } if (bTheme) { if (bSelected) { pClass->DrawThemeBackground(&dc, XTP_MP_POPUPITEM, 2, &rcItem); } dc.SetTextColor(pMetrics->GetColor(!bEnabled ? COLOR_GRAYTEXT : COLOR_MENUTEXT)); } else { if (bSelected) { dc.FillSolidRect(rcItem, pMetrics->GetColor(COLOR_HIGHLIGHT)); } dc.SetTextColor(pMetrics->GetColor(!bEnabled ? COLOR_GRAYTEXT : bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)); } BOOL bDefault = pItem->IsDefault(); CFont fontDefault; if (bDefault) { LOGFONT lf; pMetrics->m_fntMenu.GetLogFont(&lf); lf.lfWeight = FW_BOLD; fontDefault.CreateFontIndirect(&lf); font.SetFont(&fontDefault); } CString strMenuText = pItem->GetText(); int nFind = strMenuText.Find(_T('\t')); CSize szShortcut(0, 0); if (nFind != -1) { CString strShortcutText = strMenuText.Mid(nFind + 1); strMenuText.ReleaseBuffer(nFind); CRect rcShortcut(rcItem); rcShortcut.right -= 20; dc.DrawText(strShortcutText, rcShortcut, DT_VCENTER | DT_RIGHT | DT_SINGLELINE); } CRect rcText(rcItem); rcText.left += m_nGripperWidth; dc.DrawText(strMenuText, rcText, DT_VCENTER | DT_LEFT | DT_SINGLELINE); if (pItem->GetPopupMenu()) { if (bTheme) { CRect rcPopup(rcText.right - 16, rcText.top, rcText.right - 7, rcText.bottom); pClass->DrawThemeBackground(&dc, XTP_MP_POPUPSUBMENU, bEnabled ? 1 : 2, &rcPopup); } else { CPoint ptCenter(rcText.right - (bTheme ? 10 : 5), rcText.CenterPoint().y); XTPDrawHelpers()->Triangle(&dc, ptCenter, CPoint(ptCenter.x - 3, ptCenter.y - 3), CPoint(ptCenter.x - 3, ptCenter.y + 3), dc.GetTextColor()); } } if (bDefault) { font.SetFont(&pMetrics->m_fntMenu); } BOOL bChecked = pItem->IsChecked(); HBITMAP hbm = bChecked ? mii.hbmpChecked : mii.hbmpUnchecked; if (hbm) { BITMAP bmpInfo; ::GetObject(hbm, sizeof(BITMAP), &bmpInfo); CXTPCompatibleDC dcBitmap(&dcPaint, hbm); dc.SetTextColor(0); dc.SetBkColor(0xFFFFFF); dc.BitBlt((bTheme ? 16 : 11) - bmpInfo.bmWidth / 2 , (rcItem.top + rcItem.bottom - bmpInfo.bmHeight) / 2, bmpInfo.bmWidth, bmpInfo.bmHeight, &dcBitmap, 0, 0, SRCAND); } else if (bChecked) { if (bTheme) { CRect rcCheck(rcItem.left, rcItem.top, rcItem.left + 22, rcItem.bottom); pClass->DrawThemeBackground(&dc, XTP_MP_POPUPCHECKBACKGROUND, bEnabled ? 2 : 1, rcCheck); pClass->DrawThemeBackground(&dc, XTP_MP_POPUPCHECK, bEnabled ? 1 : 2, rcCheck); } else { CXTPPenDC pen (dc, dc.GetTextColor()); CXTPBrushDC brush (dc, dc.GetTextColor()); CPoint pt(rcItem.left + 5, rcItem.CenterPoint().y); CPoint pts[] = {pt, CPoint(pt.x + 2, pt.y + 2), CPoint(pt.x + 6, pt.y - 2), CPoint(pt.x + 6, pt.y), CPoint(pt.x + 2, pt.y + 4), CPoint(pt.x, pt.y + 2)}; dc.Polygon(pts, _countof(pts)); } } else { hbm = pItem->GetItemBitmap(); int nSysBitmap = 0; switch ((UINT_PTR)hbm) { case (UINT_PTR)HBMMENU_POPUP_RESTORE: nSysBitmap = XTP_MP_SYSTEMRESTORE; break; case (UINT_PTR)HBMMENU_POPUP_MINIMIZE: nSysBitmap = XTP_MP_SYSTEMMINIMIZE; break; case (UINT_PTR)HBMMENU_POPUP_MAXIMIZE: nSysBitmap = XTP_MP_SYSTEMMAXIMIZE; break; case (UINT_PTR)HBMMENU_POPUP_CLOSE: nSysBitmap = XTP_MP_SYSTEMCLOSE; break; } if (nSysBitmap != 0) { if (bTheme) { CSize sz(20, 20); CRect rcBitmap(CPoint((bTheme ? 16 : 11) - sz.cx / 2 , (rcItem.top + rcItem.bottom - sz.cy) / 2), sz); pClass->DrawThemeBackground(&dc, nSysBitmap, bEnabled ? 1 : 2, rcBitmap); } else { CSize sz(20, 20); CRect rcBitmap(CPoint((bTheme ? 16 : 11) - sz.cx / 2 , (rcItem.top + rcItem.bottom - sz.cy) / 2 + 1), sz); CFont fontMarlett; fontMarlett.CreatePointFont(MulDiv(80, 96, dc.GetDeviceCaps(LOGPIXELSX)), _T("Marlett")); CFont* pFont = dc.SelectObject(&fontMarlett); dc.DrawText(nSysBitmap == XTP_MP_SYSTEMMINIMIZE ? _T("0") : nSysBitmap == XTP_MP_SYSTEMRESTORE ? _T("2") : nSysBitmap == XTP_MP_SYSTEMMAXIMIZE ? _T("1") :_T("r"), 1, rcBitmap, DT_SINGLELINE | DT_CENTER | DT_VCENTER); dc.SelectObject(pFont); } } else if (hbm) { BITMAP bmpInfo; ::GetObject(hbm, sizeof(BITMAP), &bmpInfo); int nCenter = 3 + (m_nGripperWidth - (bTheme ? 3 + 5 : 0)) / 2; if (bmpInfo.bmBitsPixel == 32 && bmpInfo.bmBits && XTPSystemVersion()->IsWinVistaOrGreater()) { CXTPImageManagerIcon::DrawAlphaBitmap(&dc, hbm, CPoint(nCenter - bmpInfo.bmWidth / 2 , (rcItem.top + rcItem.bottom - bmpInfo.bmHeight) / 2), CSize(bmpInfo.bmWidth, bmpInfo.bmHeight)); } else { CXTPCompatibleDC dcBitmap(&dcPaint, hbm); dc.SetTextColor(0); dc.SetBkColor(0xFFFFFF); dc.BitBlt(nCenter - bmpInfo.bmWidth / 2 , (rcItem.top + rcItem.bottom - bmpInfo.bmHeight) / 2, bmpInfo.bmWidth, bmpInfo.bmHeight, &dcBitmap, 0, 0, SRCAND); } } } } } ////////////////////////////////////////////////////////////////////////// // CXTPSkinPopupMenuState CXTPSkinPopupMenuState::CXTPSkinPopupMenuState(HWND hWndNotify) { m_bInsideMenuLoop = FALSE; m_bButtonDown = 0; m_bRightButton = FALSE; m_nFocus = focusMouse; m_hWndCapture = 0; m_bDismiss = FALSE; m_bNofyByPos = FALSE; m_pRootPopup = NULL; m_bNoNotify = FALSE; m_ptMouseLast = CPoint(0); m_nLastCommand = 0; m_bFirstClick = FALSE; m_bMenuStarted = FALSE; m_bSynchronous = FALSE; m_pAlternatePopup = NULL; CXTPSkinObject* pSkinObject = XTPSkinManager()->Lookup(hWndNotify); m_pSchema = pSkinObject ? pSkinObject->GetSchema() : XTPSkinManager()->GetSchema(); m_dwMenuShowDelay = 400; SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &m_dwMenuShowDelay, 0); } void CXTPSkinPopupMenuState::InitMenu(CXTPSkinPopupMenu* pPopupMenu) { m_pRootPopup = pPopupMenu; m_pRootPopup->InternalAddRef(); m_bButtonDown = ((GetKeyState(VK_LBUTTON) & 0x8000) != 0); pPopupMenu->m_bDropNextPopup = FALSE; if (m_pRootPopup->m_hWndNotify && !m_bNoNotify) { SendMessage(m_pRootPopup->m_hWndNotify, WM_ENTERMENULOOP, 0, 0); } } #ifndef MNS_NOTIFYBYPOS #define MNS_NOTIFYBYPOS 0x08000000 #define MIM_STYLE 0x00000010 #endif int CXTPSkinPopupMenuState::GetMenuFlags(HMENU hMenu) { struct XTP_MENUINFO { DWORD cbSize; DWORD fMask; DWORD dwStyle; UINT cyMax; HBRUSH hbrBack; DWORD dwContextHelpID; ULONG_PTR dwMenuData; }; HMODULE hLib = GetModuleHandle(_T("USER32")); if (!hLib) return 0; typedef BOOL (WINAPI *PFNGETMENUINFO) (HMENU hmenu, XTP_MENUINFO* lpcmi); PFNGETMENUINFO pfnGetMenuInfo = (PFNGETMENUINFO)::GetProcAddress(hLib, "GetMenuInfo"); if (!pfnGetMenuInfo) return 0; XTP_MENUINFO mi; ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); mi.fMask = MIM_STYLE; if (!(*pfnGetMenuInfo)(hMenu, &mi)) return FALSE; return mi.dwStyle; } BOOL CXTPSkinPopupMenuState::StartMenu(MenuFocus nFocus) { m_bMenuStarted = TRUE; m_nFocus = nFocus; m_pRootPopup->m_bToggle = FALSE; m_pRootPopup->m_bAboutToHide = FALSE; HWND hWndNotify = m_pRootPopup->m_hWndNotify; m_hWndCapture= hWndNotify; ::SetCapture(m_hWndCapture); ::SendMessage(hWndNotify, WM_SETCURSOR, (WPARAM)hWndNotify, MAKELONG(MSGF_MENU, 0)); if (m_pRootPopup->m_bMenuBar) { // TODO GetSysMenu from Child xxxGetInitMenuParam BOOL bSystemMenu = m_pRootPopup->m_bSysMenu; if (!bSystemMenu) { HMENU hMenu = ::GetSystemMenu(hWndNotify, FALSE); if (hMenu) { ::SetMenuDefaultItem(hMenu, SC_CLOSE, MF_BYCOMMAND); m_pAlternatePopup = new CXTPSkinPopupMenu(); m_pAlternatePopup->m_hMenu = hMenu; m_pAlternatePopup->m_hWnd = hWndNotify; m_pAlternatePopup->m_hWndNotify = hWndNotify; m_pAlternatePopup->m_bSysMenu = TRUE; m_pAlternatePopup->m_bMenuBar = TRUE; m_pAlternatePopup->PositionSysMenu(); m_pAlternatePopup->m_pState = this; } } } int nFlags = GetMenuFlags(m_pRootPopup->m_hMenu); if (nFlags & MNS_NOTIFYBYPOS) m_bNofyByPos = TRUE; if (!m_bNoNotify && hWndNotify) { ::SendMessage(hWndNotify, WM_INITMENU, (WPARAM)m_pRootPopup->m_hMenu, 0); } // TODO: Add EVENT_SYSTEM_MENUSTART WinEvent. return TRUE; } #define MENU_NOITEM (int)-1 int CXTPSkinPopupMenuState::ItemHitTest(CPoint point, CXTPSkinPopupMenu* pPopupMenu) { for (int i = 0; i < pPopupMenu->GetCount(); i++) { CXTPSkinPopupMenuItem* pItem = pPopupMenu->m_arrItems.GetAt(i); CRect rcItem(pItem->m_rcItem); WindowToScreen(pPopupMenu->m_hWnd, rcItem); if (rcItem.PtInRect(point)) return i; } return MENU_NOITEM; } void CXTPSkinPopupMenuState::WindowToScreen(HWND hWnd, CRect& rcItem) { CXTPWindowRect rcWindow(hWnd); if (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL ) { rcItem.OffsetRect(rcWindow.right - rcItem.right - rcItem.left, rcWindow.top); } else { rcItem.OffsetRect(rcWindow.TopLeft()); } } CXTPSkinPopupMenu* CXTPSkinPopupMenuState::FindMenu(CPoint point, CXTPSkinPopupMenu* pPopupMenu, int* pnItem) { *pnItem = 0; if (pPopupMenu->m_pNextPopup) { CXTPSkinPopupMenu* lResult = FindMenu(point, pPopupMenu->m_pNextPopup, pnItem); if (lResult) { return lResult; } } if (pPopupMenu->m_bMenuBar) { HWND hWnd = pPopupMenu->m_hWndNotify; if (pPopupMenu->m_bSysMenu) { HICON hIcon = m_pSchema->GetFrameSmIcon(hWnd); if (!hIcon) return NULL; CXTPWindowRect rcWindow(hWnd); if (GetWindowLong(hWnd, GWL_STYLE) & WS_MINIMIZE) { if (rcWindow.PtInRect(point)) return pPopupMenu; return NULL; } CRect rcIcon(pPopupMenu->GetItem(0)->m_rcItem); WindowToScreen(hWnd, rcIcon); if (rcIcon.PtInRect(point)) { *pnItem = 0; return pPopupMenu; } if (m_pAlternatePopup && m_pAlternatePopup != pPopupMenu) { int nHitTest = CXTPSkinPopupMenuState::ItemHitTest(point, m_pAlternatePopup); if (nHitTest != MENU_NOITEM) { *pnItem = nHitTest; return m_pAlternatePopup; } } return NULL; } else { if (GetWindowLong(hWnd, GWL_STYLE) & WS_MINIMIZE) return NULL; } } else { CXTPWindowRect rc(pPopupMenu->m_hWnd); if (!rc.PtInRect(point)) return NULL; } int nHitTest = CXTPSkinPopupMenuState::ItemHitTest(point, pPopupMenu); if (pPopupMenu->m_bMenuBar) { if (nHitTest == MENU_NOITEM) { if (m_pAlternatePopup && m_pAlternatePopup != pPopupMenu) { CXTPSkinPopupMenu* pAlternatePopup = FindMenu(point, m_pAlternatePopup, pnItem); if (pAlternatePopup) return pAlternatePopup; } return NULL; } } *pnItem = nHitTest; return pPopupMenu; } BOOL CXTPSkinPopupMenuState::SetTimerToCloseHierarchy(CXTPSkinPopupMenu* pPopupMenu) { CXTPSkinPopupMenu* pNextPopup = GetNextPopup(pPopupMenu); if (!pNextPopup) return FALSE; if (pPopupMenu->m_nHideTimer) return TRUE; if (!SetTimer(pPopupMenu->m_hWnd, TIMERID_MENUHIDE, m_dwMenuShowDelay, NULL)) return FALSE; pPopupMenu->m_nHideTimer = TIMERID_MENUHIDE; pNextPopup->m_bAboutToHide = TRUE; return TRUE; } BOOL CXTPSkinPopupMenuState::SetTimerToOpenHierarchy(CXTPSkinPopupMenu* pPopupMenu) { ASSERT(!pPopupMenu->m_bMenuBar); CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(pPopupMenu->m_nSelected); if (!pItem) return FALSE; if (pPopupMenu->m_nShowTimer || pPopupMenu->m_nSelected == pPopupMenu->m_nDropped) return 1; if (!SetTimer(pPopupMenu->m_hWnd, TIMERID_MENUSHOW, m_dwMenuShowDelay, NULL)) return FALSE; pPopupMenu->m_nShowTimer = TIMERID_MENUSHOW; return TRUE; } void CXTPSkinPopupMenuState::RedrawPopup(CXTPSkinPopupMenu* pPopupMenu) { if (pPopupMenu->m_bMenuBar) ::SendMessage(pPopupMenu->m_hWnd, WM_NCPAINT, 0, 0); else ::InvalidateRect(pPopupMenu->m_hWnd, 0, FALSE); } void CXTPSkinPopupMenuState::SendMenuSelect(CXTPSkinPopupMenu* pPopupMenu) { UINT dwFlags = 0; UINT nCommand = 0; int nIndex = -1; CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(pPopupMenu->m_nSelected); if (pItem) { dwFlags = pItem->GetType() | pItem->GetState(); nIndex = pItem->m_nItem; if (pItem->GetPopupMenu()) dwFlags |= MF_POPUP; if (dwFlags & MF_POPUP) nCommand = nIndex; else nCommand = pItem->GetID(); if (m_nFocus == focusMouse) dwFlags |= MF_MOUSESELECT; if (pPopupMenu->m_bSysMenu) dwFlags |= MF_SYSMENU; } ::SendMessage(pPopupMenu->m_hWndNotify, WM_MENUSELECT, (DWORD)MAKELONG(nCommand, dwFlags), (LPARAM)pPopupMenu->m_hMenu); } CXTPSkinPopupMenuItem* CXTPSkinPopupMenuState::SelectItem(CXTPSkinPopupMenu* pPopupMenu, int nItem) { if (pPopupMenu->m_nSelected == nItem) { return pPopupMenu->GetItem(nItem); } if (pPopupMenu->m_nShowTimer) { KillTimer(pPopupMenu->m_hWnd, pPopupMenu->m_nShowTimer); pPopupMenu->m_nShowTimer = 0; } if (pPopupMenu->m_bAboutToHide && pPopupMenu->m_pPrevPopup) { CXTPSkinPopupMenu* pPrevPopup = pPopupMenu->m_pPrevPopup; if (pPrevPopup->m_nHideTimer) { ::KillTimer(pPrevPopup->m_hWnd, pPrevPopup->m_nHideTimer); pPrevPopup->m_nHideTimer = NULL; } if (pPrevPopup->m_nShowTimer) { ::KillTimer(pPrevPopup->m_hWnd, pPrevPopup->m_nShowTimer); pPrevPopup->m_nShowTimer = NULL; } if (pPrevPopup->m_nSelected != pPrevPopup->m_nDropped) { pPrevPopup->m_nSelected = pPrevPopup->m_nDropped; RedrawPopup(pPrevPopup); SendMenuSelect(pPrevPopup); } pPopupMenu->m_bAboutToHide = FALSE; } if (pPopupMenu->m_nSelected != MENU_NOITEM) { if (GetNextPopup(pPopupMenu)) { if (pPopupMenu->m_bMenuBar) { CloseHierarchy(pPopupMenu); } else { SetTimerToCloseHierarchy(pPopupMenu); } } } pPopupMenu->m_nSelected = nItem; RedrawPopup(pPopupMenu); SendMenuSelect(pPopupMenu); return pPopupMenu->GetItem(nItem); } CPoint CXTPSkinPopupMenuState::PositionHierarchy(CXTPSkinPopupMenu* pSubMenu, CXTPSkinPopupMenu* pPopupMenu, CRect rcItem, CSize sz) { CPoint pt(pPopupMenu->m_bMenuBar ? rcItem.left : rcItem.right - 3, pPopupMenu->m_bMenuBar ? rcItem.bottom : rcItem.top - 3); CRect rcMonitor(XTPMultiMonitor()->GetScreenArea(pt)); if (pPopupMenu->m_bMenuBar) { BOOL bMinimized = (GetWindowLong(pPopupMenu->m_hWnd, GWL_STYLE) & WS_MINIMIZE) != 0; if (bMinimized) { pt.y = rcItem.top - sz.cy; if (pt.y < rcMonitor.top) pt.y = rcItem.bottom; } if (GetWindowLong(pPopupMenu->m_hWnd, GWL_EXSTYLE) & (WS_EX_LAYOUTRTL | WS_EX_RIGHT)) { pt.x = rcItem.right - sz.cx; pSubMenu->m_bDroppedLeft = TRUE; } pt.x = min(pt.x, rcMonitor.right - sz.cx); } else { if (pPopupMenu->m_bDroppedLeft) { int x = rcItem.left + 3 - sz.cx; if (x >= rcMonitor.left) { pt.x = x; pSubMenu->m_bDroppedLeft = TRUE; } } if (pt.x + sz.cx > rcMonitor.right) { pt.x = rcItem.left + 3 - sz.cx; pSubMenu->m_bDroppedLeft = TRUE; } } if (pt.y + sz.cy > rcMonitor.bottom) { pt.y -= sz.cy; if (pPopupMenu->m_bMenuBar) { pt.y -= GetSystemMetrics(SM_CYMENUSIZE); } else { pt.y += rcItem.Height() + 3; } if ((pt.y < rcMonitor.top) || (pt.y + sz.cy > rcMonitor.bottom)) pt.y = rcMonitor.bottom - sz.cy; } pt.x = max(pt.x, rcMonitor.left); pt.y = max(pt.y, rcMonitor.top); CRect rcPopup(pt.x, pt.y, pt.x + sz.cx, pt.y + sz.cy); rcPopup.DeflateRect(3, 3); if (CRect().IntersectRect(rcItem, rcPopup)) { if ((rcItem.right + sz.cx) <= rcMonitor.right) pt.x = rcItem.right; else if ((rcItem.left - sz.cx) >= rcMonitor.left) pt.x = rcItem.left - sz.cx; } return pt; } HWND CXTPSkinPopupMenuState::OpenHierarchy(CXTPSkinPopupMenu* pPopupMenu) { if (pPopupMenu->m_nSelected < 0 || pPopupMenu->m_nSelected >= pPopupMenu->GetCount()) return NULL; if (GetNextPopup(pPopupMenu)) { if (pPopupMenu->m_nHideTimer) { CloseHierarchy(pPopupMenu); } else { return NULL; } } if (pPopupMenu->m_nShowTimer) { KillTimer(pPopupMenu->m_hWnd, pPopupMenu->m_nShowTimer); pPopupMenu->m_nShowTimer = 0; } CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(pPopupMenu->m_nSelected); HMENU hSubMenu = pItem->GetPopupMenu(); if (!hSubMenu) return NULL; BOOL bSendNotify = FALSE; HWND hwndResult = NULL; TRY { if (pPopupMenu->m_hWndNotify && !m_bNoNotify) { SendMessage(pPopupMenu->m_hWndNotify, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELONG(pPopupMenu->m_nSelected, pPopupMenu->m_bSysMenu ? 1 : 0)); bSendNotify = TRUE; } if (!m_bInsideMenuLoop) AfxThrowMemoryException(); if (pPopupMenu->m_nSelected < 0 || pPopupMenu->m_nSelected >= pPopupMenu->GetCount()) AfxThrowMemoryException(); pItem = pPopupMenu->GetItem(pPopupMenu->m_nSelected); if (!pItem->IsEnabled() || !pItem->GetPopupMenu() || GetMenuItemCount(pItem->GetPopupMenu()) == 0) AfxThrowMemoryException(); CXTPSkinPopupMenu* pSubMenu = new CXTPSkinPopupMenu(); pSubMenu->m_pState = this; if (!pSubMenu->Create(pPopupMenu->m_hWndNotify)) { pSubMenu->InternalRelease(); AfxThrowMemoryException(); } pSubMenu->m_hMenu = hSubMenu; pSubMenu->m_hWndNotify = pPopupMenu->m_hWndNotify; pSubMenu->m_bSysMenu = pPopupMenu->m_bSysMenu; bSendNotify = FALSE; pSubMenu->m_bSendUninit = TRUE; pPopupMenu->m_pNextPopup = pSubMenu; pSubMenu->m_pPrevPopup = pPopupMenu; if (pPopupMenu->m_bMenuBar) { pPopupMenu->m_bDropNextPopup = TRUE; } if (pPopupMenu->m_hWndNotify) { UpdateWindow(pPopupMenu->m_hWndNotify); } CSize size = pSubMenu->RecalcLayout(); CRect rcItem = pItem->GetScreenPos(); CPoint pt = PositionHierarchy(pSubMenu, pPopupMenu, rcItem, size); PlayEventSound(xtpSoundMenuPopup); ::SetWindowPos(pSubMenu->m_hWnd, HWND_TOPMOST, pt.x, pt.y, size.cx, size.cy, SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOACTIVATE); if (m_nFocus == focusKeyboard) { SelectItem(pSubMenu, 0); } pPopupMenu->m_nDropped = pPopupMenu->m_nSelected; UpdateWindow(pSubMenu->m_hWnd); hwndResult = pSubMenu->m_hWnd; } CATCH (CMemoryException, e) { } END_CATCH if (bSendNotify) { SendMessage(pPopupMenu->m_hWndNotify, WM_UNINITMENUPOPUP, (WPARAM)hSubMenu, MAKELONG(0, pPopupMenu->m_bSysMenu ? 1 : 0)); } return hwndResult; } void CXTPSkinPopupMenuState::CloseHierarchy(CXTPSkinPopupMenu* pPopupMenu) { if (pPopupMenu->m_nHideTimer) { ::KillTimer(pPopupMenu->m_hWnd, pPopupMenu->m_nHideTimer); pPopupMenu->m_nHideTimer = 0; } CXTPSkinPopupMenu* pNextPopup = GetNextPopup(pPopupMenu); if (pNextPopup) { CloseHierarchy(pNextPopup); if (!pNextPopup->m_bMenuBar) { if (pNextPopup->m_bSendUninit && !m_bNoNotify) { SendMessage(pNextPopup->m_hWndNotify, WM_UNINITMENUPOPUP, (WPARAM)pNextPopup->m_hMenu, MAKELONG(0, pNextPopup->m_bSysMenu ? MF_SYSMENU: 0)); } pNextPopup->DestroyWindow(); pNextPopup->InternalRelease(); } pPopupMenu->m_pNextPopup = NULL; pPopupMenu->m_nDropped = MENU_NOITEM; } if (pPopupMenu->m_nSelected != MENU_NOITEM) { SendMenuSelect(pPopupMenu); } } CXTPSkinPopupMenu* CXTPSkinPopupMenuState::GetNextPopup(CXTPSkinPopupMenu* pPopupMenu) const { return pPopupMenu->m_pNextPopup; } CXTPSkinPopupMenu* CXTPSkinPopupMenuState::GetActivePopup() const { CXTPSkinPopupMenu* pActivePopup = m_pRootPopup; while (pActivePopup->m_pNextPopup != NULL) { pActivePopup = pActivePopup->m_pNextPopup; } return pActivePopup; } BOOL CXTPSkinPopupMenuState::HideNextHierarchy(CXTPSkinPopupMenu* pPopupMenu) { CXTPSkinPopupMenu* pNextPopup = GetNextPopup(pPopupMenu); if (pNextPopup) { if (GetNextPopup(pPopupMenu) != GetActivePopup()) CloseHierarchy(pNextPopup); SelectItem(pNextPopup, -1); return TRUE; } return FALSE; } void CXTPSkinPopupMenuState::DismissNotify(CXTPSkinPopupMenu* pPopupMenu, int nItem) { DismissNotify(pPopupMenu->m_hMenu, nItem); } void CXTPSkinPopupMenuState::DismissNotify(HMENU hMenu, int nItem) { if (nItem < 0 || nItem >= GetMenuItemCount(hMenu)) return; UINT nMessage = 0; UINT nCommand = 0; LPARAM lParam = 0; if (m_pRootPopup->m_bSysMenu) { nMessage = WM_SYSCOMMAND; nCommand = ::GetMenuItemID(hMenu, nItem); } else if (m_bNofyByPos) { nMessage = WM_MENUCOMMAND; nCommand = nItem; lParam = (LPARAM)hMenu; } else { nMessage = WM_COMMAND; nCommand = ::GetMenuItemID(hMenu, nItem); } Notify(nMessage, nCommand, lParam); } void CXTPSkinPopupMenuState::OnButtonUp(CXTPSkinPopupMenu* pPopupMenu, int nItem) { if (!m_bButtonDown) return; if (nItem == MENU_NOITEM) return; if (nItem != pPopupMenu->m_nSelected) return; if (pPopupMenu->m_bMenuBar) { if (GetNextPopup(pPopupMenu)) { if (!pPopupMenu->m_bToggle) return; pPopupMenu->m_bToggle = FALSE; Dismiss(); return; } } else if (pPopupMenu->m_nShowTimer) { pPopupMenu->m_bToggle = FALSE; OpenHierarchy(pPopupMenu); return; } CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(pPopupMenu->m_nSelected); if (!pItem) return; if (!pItem->IsSeparator() && pItem->IsEnabled() && !pItem->GetPopupMenu()) { DismissNotify(pPopupMenu, pPopupMenu->m_nSelected); } } void CXTPSkinPopupMenuState::OnButtonDblClick(CXTPSkinPopupMenu* pPopupMenu, int nItem) { CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(nItem); if (!pItem) return; if (!pItem->IsEnabled()) return; HMENU hMenu = pItem->GetPopupMenu(); if (hMenu) { nItem = GetMenuDefaultItem(hMenu, MF_BYPOSITION, 0); if (nItem == -1) return; } else { hMenu = pPopupMenu->m_hMenu; } DismissNotify(hMenu, nItem); } void CXTPSkinPopupMenuState::OnButtonDown(CXTPSkinPopupMenu* pPopupMenu, int nItem, BOOL bClick) { if (pPopupMenu->m_nSelected != nItem) { BOOL bOpenPopup = FALSE; if (bClick) { bOpenPopup = TRUE; pPopupMenu->m_bToggle = FALSE; } else { bOpenPopup = pPopupMenu->m_bDropNextPopup; } CXTPSkinPopupMenuItem* pItem = SelectItem(pPopupMenu, nItem); if (pItem && pItem->GetPopupMenu() && bOpenPopup) { if (!OpenHierarchy(pPopupMenu)) return; } } else { if (bClick) pPopupMenu->m_bToggle = TRUE; if (!HideNextHierarchy(pPopupMenu) && bClick && OpenHierarchy(pPopupMenu)) pPopupMenu->m_bToggle = FALSE; } if (bClick) { m_bButtonDown = TRUE; } } void CXTPSkinPopupMenuState::Dismiss() { Notify(0, 0, 0); } void CXTPSkinPopupMenuState::Notify(UINT nMessage, UINT nCommand, LPARAM lParam) { if (m_bDismiss) return; m_bDismiss = TRUE; m_bInsideMenuLoop = FALSE; m_bButtonDown = FALSE; CloseHierarchy(m_pRootPopup); SelectItem(m_pRootPopup, MENU_NOITEM); ReleaseCapture(); HWND hWndNotify = m_pRootPopup->m_hWndNotify; if (hWndNotify == 0) return; if (!m_pRootPopup->m_bMenuBar) { CloseHierarchy(m_pRootPopup); m_pRootPopup->DestroyWindow(); } ::SendMessage(hWndNotify, WM_MENUSELECT, (DWORD)MAKELONG(0, 0xFFFF), 0); // Send WM_MENUSELECT with flags = 0xFFFF if (!m_bNoNotify) { SendMessage(hWndNotify, WM_EXITMENULOOP, 0, 0); } m_nLastCommand = nCommand; if (nMessage != 0) { PlayEventSound(xtpSoundMenuCommand); if (!m_bSynchronous) { if (nMessage == WM_COMMAND) PostMessage(hWndNotify, nMessage, nCommand, lParam); else SendMessage(hWndNotify, nMessage, nCommand, lParam); if (nMessage == WM_SYSCOMMAND && IsWindow(hWndNotify)) { ::SendMessage(hWndNotify, WM_NCPAINT, 0, 0); } } } } void CXTPSkinPopupMenuState::OnMouseMove(CPoint point) { if (point == m_ptMouseLast) return; m_ptMouseLast = point; int nItem; CXTPSkinPopupMenu* pMenuHit = FindMenu(point, m_pRootPopup, &nItem); if (m_nFocus == focusKeyboard) { if (pMenuHit == NULL) return; m_nFocus = focusMouse; } if (pMenuHit == m_pAlternatePopup) { if (m_bButtonDown) { SwitchToAlternateMenu(); } else { pMenuHit = NULL; } } if (pMenuHit) { if (pMenuHit->m_bMenuBar) { OnButtonDown(pMenuHit, nItem, FALSE); } else { CXTPSkinPopupMenuItem* pItem = SelectItem(pMenuHit, nItem); if (pItem && pItem->GetPopupMenu() && pItem->IsEnabled()) { if (!SetTimerToOpenHierarchy(pMenuHit)) { HideNextHierarchy(pMenuHit); } } } } else { SelectItem(GetActivePopup(), MENU_NOITEM); } } CXTPSkinPopupMenu* CXTPSkinPopupMenuState::FindPopup(HWND hWnd) const { CXTPSkinPopupMenu* pPopupMenu = m_pRootPopup; while (pPopupMenu) { if (pPopupMenu->m_hWnd == hWnd) return pPopupMenu; pPopupMenu = GetNextPopup(pPopupMenu); } return NULL; } BOOL CXTPSkinPopupMenuState::RemoveMessage(UINT nMessage) { MSG msg; if (!PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE)) { return FALSE; } if (msg.message == nMessage) { PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); return TRUE; } return FALSE; } int CXTPSkinPopupMenuState::FindNextItem(CXTPSkinPopupMenu* pPopupMenu, int nItem, BOOL bForward, UINT nFlags) const { int nCount = pPopupMenu->GetCount(); int nStart = nItem; if ((nItem < 0) && bForward) // going forward from beginning -- stop after last menu item nItem = nStart = nCount; else if ((nItem >= nCount) && (!bForward)) // going backward from end -- stop after first menu item nItem = nStart = -1; if (!nCount) return MENU_NOITEM; BOOL bFirst = TRUE; for (;;) { nItem += bForward ? +1 : -1; if (nItem == nStart && !bFirst) return MENU_NOITEM; if (nItem >= nCount) { if (nFlags) return MENU_NOITEM; nItem = -1; continue; } else if (nItem < 0) { if (nFlags) return MENU_NOITEM; nItem = nCount; continue; } bFirst = FALSE; CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(nItem); if (pItem->IsSeparator()) continue; if (pPopupMenu->m_bMenuBar && !pPopupMenu->m_bSysMenu && pItem->IsMDISysButton()) continue; break; } return nItem; } void CXTPSkinPopupMenuState::OnKeyDown(CXTPSkinPopupMenu* pPopupMenu, WPARAM nKey) { if (m_bButtonDown) return; switch (nKey) { case VK_MENU: case VK_F10: Dismiss(); return; case VK_ESCAPE: if (pPopupMenu->m_bMenuBar || pPopupMenu == m_pRootPopup || pPopupMenu->m_pPrevPopup == NULL) { Dismiss(); } else { CXTPSkinPopupMenu* pPrevPopup = pPopupMenu->m_pPrevPopup; CloseHierarchy(pPrevPopup); pPrevPopup->m_bDropNextPopup = FALSE; } return; case VK_UP: case VK_DOWN: if (pPopupMenu->m_bMenuBar) { OpenHierarchy(pPopupMenu); } else { int nItem = FindNextItem(pPopupMenu, pPopupMenu->m_nSelected, (nKey == VK_UP ? FALSE : TRUE), 0); SelectItem(pPopupMenu, nItem); } return; case VK_LEFT: case VK_RIGHT: { int nScreenKey = GetWindowLong(pPopupMenu->m_hWnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL ? (nKey == VK_LEFT ? VK_RIGHT : VK_LEFT) : (int)nKey; if (!pPopupMenu->m_bMenuBar && nScreenKey == VK_RIGHT && !GetNextPopup(pPopupMenu)) { if (OpenHierarchy(pPopupMenu)) return; } BOOL bHierarchyWasDropped = FALSE; if (GetNextPopup(pPopupMenu)) { bHierarchyWasDropped = TRUE; if (nScreenKey == VK_LEFT && !pPopupMenu->m_bMenuBar) { CloseHierarchy(pPopupMenu); return; } } else { if (pPopupMenu->m_bDropNextPopup) bHierarchyWasDropped = TRUE; } if (pPopupMenu->m_pPrevPopup) { OnKeyDown(pPopupMenu->m_pPrevPopup, nKey); return; } if (pPopupMenu->m_bMenuBar) { int nItem = FindNextItem(pPopupMenu, pPopupMenu->m_nSelected, (nScreenKey == VK_RIGHT ? TRUE : FALSE), 1); if (nItem == MENU_NOITEM) { if (m_pAlternatePopup) { if (SwitchToAlternateMenu()) { pPopupMenu = m_pRootPopup; nItem = FindNextItem(pPopupMenu, MENU_NOITEM, (nScreenKey == VK_RIGHT ? TRUE : FALSE), 0); } } } if (nItem != MENU_NOITEM) { if (GetNextPopup(pPopupMenu)) { CloseHierarchy(pPopupMenu); } SelectItem(pPopupMenu, nItem); if (bHierarchyWasDropped) { OpenHierarchy(pPopupMenu); } } } } return; case VK_RETURN: { CXTPSkinPopupMenuItem* pItem = pPopupMenu->GetItem(pPopupMenu->m_nSelected); if (!pItem) return; if (!pItem->IsEnabled()) { Dismiss(); return; } if (pItem->GetPopupMenu()) { OpenHierarchy(pPopupMenu); return; } DismissNotify(pPopupMenu, pPopupMenu->m_nSelected); } return; } } int CXTPSkinPopupMenuState::FindChar(CXTPSkinPopupMenu* pPopupMenu, WPARAM nKey, int nItem) { if (!nKey) return MENU_NOITEM; CString sKey((TCHAR)nKey); sKey.MakeLower(); int nResult = MENU_NOITEM; int nFirst = MENU_NOITEM; int nStart = nItem; if (nStart < 0) nStart = FindNextItem(pPopupMenu, -1, FALSE, 0); do { int nPrev = nItem; nItem = FindNextItem(pPopupMenu, nItem, TRUE, 0); if (nItem == MENU_NOITEM || nItem == nFirst) break; if (nFirst == MENU_NOITEM) nFirst = nItem; CString str = pPopupMenu->GetMenuString(nItem); if (!str.IsEmpty()) { str.MakeLower(); int nIndex = str.Find(_T('&')); if (nIndex == -1) { if (str[0] == sKey[0] && nResult == MENU_NOITEM) nResult = nItem; } else { if (str[nIndex + 1] == sKey[0]) { return nItem; } } } if (nItem == nPrev) break; } while (nItem != nStart); return nResult; } void CXTPSkinPopupMenuState::OnChar(CXTPSkinPopupMenu* pPopupMenu, WPARAM nKey) { int nItem = FindChar(pPopupMenu, nKey, pPopupMenu->m_nSelected); BOOL bExecute = FALSE; if (nItem != MENU_NOITEM) { int nFirstItem = nItem; while (!pPopupMenu->GetItem(nItem)->IsEnabled()) { nItem = FindChar(pPopupMenu, nKey, nItem); if (nItem == nFirstItem) { Dismiss(); return; } } int nItemExecute = nItem; do { nItem = FindChar(pPopupMenu, nKey, nItem); } while (!pPopupMenu->GetItem(nItem)->IsEnabled() && (nItem != nFirstItem)); if (nFirstItem == nItem || nItem == nItemExecute) bExecute = TRUE; nItem = nItemExecute; } if (nItem == MENU_NOITEM && pPopupMenu->m_bMenuBar && nKey == ' ') { if (pPopupMenu->m_bSysMenu) { nItem = 0; bExecute = TRUE; } else if (m_pAlternatePopup) { if (SwitchToAlternateMenu()) { OnChar(m_pRootPopup, nKey); } return; } } #if 0 if (nItem == MENU_NOITEM && pPopupMenu->m_bMenuBar && m_pAlternatePopup && m_pAlternatePopup != pPopupMenu) { nItem = FindChar(m_pAlternatePopup, nKey, 0); if (nItem != MENU_NOITEM) { if (SwitchToAlternateMenu()) { OnChar(m_pRootPopup , nKey); } return; } } #endif if (nItem == MENU_NOITEM && pPopupMenu->m_hWndNotify && !m_bNoNotify) { int nFlag = (pPopupMenu->m_bSysMenu ? MF_SYSMENU : 0) | (pPopupMenu->m_bMenuBar ? 0 : MF_POPUP); LRESULT lResult = ::SendMessage(pPopupMenu->m_hWndNotify, WM_MENUCHAR, MAKELONG((WORD)nKey, (WORD)nFlag), (LPARAM)pPopupMenu->m_hMenu); switch (HIWORD(lResult)) { case MNC_IGNORE: MessageBeep(0); if (nFlag & MF_POPUP) return; Dismiss(); return; case MNC_CLOSE: Dismiss(); return; case MNC_EXECUTE: bExecute = TRUE; case MNC_SELECT: nItem = (short)LOWORD(lResult); if (nItem < 0 || nItem < pPopupMenu->GetCount()) return; break; } } if (nItem != MENU_NOITEM) { SelectItem(pPopupMenu, nItem); if (bExecute) { OnKeyDown(pPopupMenu, VK_RETURN); } } } BOOL CXTPSkinPopupMenuState::SwitchToAlternateMenu() { if (!m_pRootPopup->m_bMenuBar || !m_pAlternatePopup) return FALSE; SelectItem(m_pRootPopup, MENU_NOITEM); CXTPSkinPopupMenu* pAlternatePopup = m_pAlternatePopup; m_pAlternatePopup = m_pRootPopup; m_pRootPopup = pAlternatePopup; m_pRootPopup->m_bDropNextPopup = m_pAlternatePopup->m_bDropNextPopup; return TRUE; } BOOL CXTPSkinPopupMenuState::HandleMenuMessage(LPMSG lpMsg) { switch (lpMsg->message) { case WM_RBUTTONDOWN: case WM_NCRBUTTONDOWN: if (!m_bRightButton) { m_nFocus = CXTPSkinPopupMenuState::focusMouse; CPoint point; GetCursorPos(&point); int nItem = 0; CXTPSkinPopupMenu* pMenuHit = FindMenu(point, m_pRootPopup, &nItem); if (!pMenuHit) { Dismiss(); return TRUE; } RemoveMessage(lpMsg->message); return TRUE; } case WM_LBUTTONDBLCLK: case WM_NCLBUTTONDBLCLK: { CPoint point; GetCursorPos(&point); int nItem = 0; CXTPSkinPopupMenu* pMenuHit = FindMenu(point, m_pRootPopup, &nItem); if (!pMenuHit) { Dismiss(); return TRUE; } OnButtonDblClick(pMenuHit, nItem); } return TRUE; case WM_LBUTTONDOWN: case WM_NCLBUTTONDOWN: { m_nFocus = CXTPSkinPopupMenuState::focusMouse; CPoint point; GetCursorPos(&point); int nItem = 0; CXTPSkinPopupMenu* pMenuHit = FindMenu(point, m_pRootPopup, &nItem); if (!pMenuHit && !nItem) { Dismiss(); return TRUE; } if (pMenuHit == m_pAlternatePopup) { SwitchToAlternateMenu(); } if (pMenuHit) { OnButtonDown(pMenuHit, nItem, TRUE); } RemoveMessage(lpMsg->message); return TRUE; } case WM_RBUTTONUP: case WM_NCRBUTTONUP: if (!m_bRightButton) { if (m_bButtonDown) { RemoveMessage(lpMsg->message); return TRUE; } return TRUE; } case WM_LBUTTONUP: case WM_NCLBUTTONUP: { if (!m_bButtonDown) return TRUE; CPoint point; GetCursorPos(&point); int nItem = 0; CXTPSkinPopupMenu* pMenuHit = FindMenu(point, m_pRootPopup, &nItem); if (m_pRootPopup->m_bMenuBar) { if (pMenuHit == NULL) { Dismiss(); return TRUE; } } else { if (pMenuHit == NULL) { if (!m_bFirstClick) { Dismiss(); return TRUE; } } m_bFirstClick = FALSE; } if (pMenuHit) { OnButtonUp(pMenuHit, nItem); } m_bButtonDown = FALSE; return TRUE; } case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: { CPoint point; GetCursorPos(&point); OnMouseMove(point); return TRUE; } case WM_TIMER: { CXTPSkinPopupMenu* pPopupMenu = FindPopup(lpMsg->hwnd); if (pPopupMenu) { if (lpMsg->wParam == TIMERID_MENUSHOW && pPopupMenu->m_nShowTimer == lpMsg->wParam) { pPopupMenu->m_bToggle = FALSE; OpenHierarchy(pPopupMenu); } if (lpMsg->wParam == TIMERID_MENUHIDE && pPopupMenu->m_nHideTimer == lpMsg->wParam) { pPopupMenu->m_bToggle = FALSE; CloseHierarchy(pPopupMenu); } return TRUE; } return FALSE; } case WM_KEYDOWN: case WM_SYSKEYDOWN: { if (m_bButtonDown) return TRUE; m_nFocus = focusKeyboard; switch (lpMsg->wParam) { case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: case VK_RETURN: case VK_CANCEL: case VK_ESCAPE: case VK_MENU: case VK_F10: case VK_F1: OnKeyDown(GetActivePopup(), lpMsg->wParam); break; case VK_TAB: if (m_pRootPopup->m_bMenuBar && !GetNextPopup(m_pRootPopup)) { Dismiss(); return TRUE; } default: TranslateMessage(lpMsg); break; } return TRUE; } case WM_CHAR: case WM_SYSCHAR: OnChar(GetActivePopup(), lpMsg->wParam); return TRUE; case WM_SYSKEYUP: return TRUE; case WM_KEYUP: TranslateMessage(lpMsg); return TRUE; } return FALSE; } BOOL CXTPSkinPopupMenuState::ContinueLoop() const { return (::GetCapture() == m_hWndCapture) && (m_pRootPopup != NULL) && m_bInsideMenuLoop; } void CXTPSkinPopupMenuState::EndMenuLoop() { Dismiss(); } void CXTPSkinPopupMenuState::RunLoop(LPARAM lParam) { CPoint pt(0, 0); m_bInsideMenuLoop = TRUE; m_bDismiss = FALSE; BOOL bSendIdle = TRUE; if (!m_bMenuStarted) { if (!StartMenu(focusMouse)) { EndMenuLoop(); return; } if (m_pRootPopup->m_bMenuBar) { MSG msg; msg.message = WM_LBUTTONDOWN; msg.wParam = MK_LBUTTON; msg.lParam = lParam; msg.hwnd = m_pRootPopup->m_hWnd; HandleMenuMessage(&msg); } } // get messages until capture lost or cancelled/accepted while (ContinueLoop()) { MSG msg; BOOL bPeek = PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE); if (bPeek) { BOOL bQueue = (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_NCLBUTTONDOWN || msg.message == WM_NCRBUTTONDOWN); if (!bQueue) { PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); } if (!HandleMenuMessage(&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (!ContinueLoop()) break; if ((msg.message == WM_TIMER) || (msg.message == WM_PAINT)) continue; bSendIdle = TRUE; } else { if (!ContinueLoop()) break; if (bSendIdle && !m_bNoNotify && m_pRootPopup->m_hWndNotify) { SendMessage(m_pRootPopup->m_hWndNotify, WM_ENTERIDLE, MSGF_MENU, (LPARAM)GetActivePopup()->m_hWnd); } bSendIdle = FALSE; WaitMessage(); } } m_bInsideMenuLoop = FALSE; m_hWndCapture = 0; EndMenuLoop(); ReleaseCapture(); } void CXTPSkinObjectApplicationFrame::OnContextMenu(CWnd* pWnd, CPoint pos) { if ((GetSkinManager()->GetApplyOptions() & xtpSkinApplyMenus) == 0) { CXTPSkinObjectFrame::OnContextMenu(pWnd, pos); return; } if (pWnd && pWnd->m_hWnd != m_hWnd) { CXTPSkinObjectFrame::OnContextMenu(pWnd, pos); return; } LRESULT nHit = SendMessage(WM_NCHITTEST, 0, MAKELONG(pos.x, pos.y)); if (nHit == HTCAPTION || nHit == HTSYSMENU || nHit == HTMINBUTTON || nHit == HTMAXBUTTON || nHit == HTCLOSE) { HMENU hMenu = ::GetSystemMenu(m_hWnd, FALSE); if (hMenu) { int nDefault = SC_CLOSE; if (nHit != HTSYSMENU) { if (GetStyle() & (WS_MAXIMIZE | WS_MINIMIZE)) nDefault = SC_RESTORE; else nDefault = SC_MAXIMIZE; } ::SetMenuDefaultItem(hMenu, nDefault, MF_BYCOMMAND); CXTPSkinObjectApplicationFrame::TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_SYSMENU, pos.x, pos.y, m_hWnd, 0); return; } } else if (nHit == HTMENU) return; CXTPSkinObjectFrame::OnContextMenu(pWnd, pos); } BOOL CXTPSkinObjectApplicationFrame::TrackPopupMenu(HMENU hMenu, UINT dwFlags, int x, int y, HWND hWnd, CONST RECT *prcRect) { CXTPSkinPopupMenuState* pState = new CXTPSkinPopupMenuState(hWnd); BOOL bResult = pState->TrackPopupMenu(hMenu, dwFlags, x, y, hWnd, prcRect); pState->EndState(); return bResult; } #define CS_DROPSHADOW 0x00020000 #ifndef SPI_GETDROPSHADOW #define SPI_GETDROPSHADOW 0x1024 #endif BOOL CXTPSkinPopupMenu::Create(HWND hwndParent) { UINT nClassStyle = CS_SAVEBITS | CS_DBLCLKS; BOOL bDropShadow = FALSE; if (XTPSystemVersion()->IsWinXPOrGreater() && SystemParametersInfo(SPI_GETDROPSHADOW, 0, &bDropShadow, 0)) { if (bDropShadow) { nClassStyle |= CS_DROPSHADOW; } } XTPDrawHelpers()->RegisterWndClass(0, _T("XTPSkinManagerMenu"), nClassStyle); UINT nWindowStyle = WS_EX_TOOLWINDOW; if (GetWindowLong(hwndParent, GWL_EXSTYLE) & (WS_EX_LAYOUTRTL | WS_EX_RIGHT)) { nWindowStyle |= WS_EX_LAYOUTRTL; } HWND hWnd = ::CreateWindowEx(nWindowStyle, _T("XTPSkinManagerMenu"), NULL, WS_POPUP, 0, 0, 100, 100, hwndParent, 0, AfxGetInstanceHandle(), 0); if (!hWnd) return FALSE; SubclassWindow(hWnd); return TRUE; } void CXTPSkinPopupMenuState::AdjustMonitorRect(CXTPSkinPopupMenu* pPopupMenu, CPoint& pt, CSize sz, UINT wFlags, LPCRECT prcExclude) { CRect rcExclude(pt.x, pt.y, pt.x, pt.y); CRect rcMonitor(XTPMultiMonitor()->GetScreenArea(pt)); if (prcExclude) { rcExclude.IntersectRect(prcExclude, rcMonitor); } if (pt.x + sz.cx > rcMonitor.right) { if ((wFlags & TPM_CENTERALIGN) || (pt.x - sz.cx < rcMonitor.left) || (pt.x >= rcMonitor.right)) pt.x = rcMonitor.right - sz.cx; else pt.x -= sz.cx; pPopupMenu->m_bDroppedLeft = TRUE; if (!rcExclude.IsRectEmpty()) { if (CRect().IntersectRect(rcExclude, CRect(pt, sz))) { pt.x = rcExclude.left - sz.cx; } } } if (pt.x < rcMonitor.left) { pt.x += sz.cx; if ((wFlags & TPM_CENTERALIGN) || (pt.x >= rcMonitor.right) || (pt.x < rcMonitor.left)) pt.x = rcMonitor.left; } if (pt.y + sz.cy > rcMonitor.bottom) { if ((wFlags & TPM_VCENTERALIGN) || (pt.y - sz.cy < rcMonitor.top) || (pt.y >= rcMonitor.bottom)) pt.y = rcMonitor.bottom - sz.cy; else pt.y -= sz.cy; if (!rcExclude.IsRectEmpty()) { if (CRect().IntersectRect(rcExclude, CRect(pt, sz))) { pt.y = rcExclude.top - sz.cy; } } } if (pt.y < rcMonitor.top) { pt.y += sz.cy; if ((wFlags & TPM_VCENTERALIGN) || (pt.y >= rcMonitor.bottom) || (pt.y < rcMonitor.top)) pt.y = rcMonitor.top; } } BOOL CXTPSkinPopupMenuState::TrackPopupMenu(HMENU hMenu, UINT dwFlags, int x, int y, HWND hWnd, CONST RECT* prcRect) { if (!hWnd) return FALSE; if (!hMenu) return FALSE; if (GetMenuItemCount(hMenu) == 0) return FALSE; BOOL bButtonDown = (GetKeyState(dwFlags & TPM_RIGHTBUTTON ? VK_RBUTTON : VK_LBUTTON) & 0x8000) != 0; CXTPSkinPopupMenu* pSubMenu = new CXTPSkinPopupMenu(); pSubMenu->m_pState = this; if (!pSubMenu->Create(hWnd)) { pSubMenu->InternalRelease(); return 0; } pSubMenu->m_hMenu = hMenu; pSubMenu->m_hWndNotify = hWnd; pSubMenu->m_bSysMenu = ((dwFlags & TPM_SYSMENU) != 0); m_bRightButton = ((dwFlags & TPM_RIGHTBUTTON) != 0); m_bNoNotify = ((dwFlags & TPM_NONOTIFY) != 0); m_bSynchronous = (dwFlags & TPM_RETURNCMD); InitMenu(pSubMenu); if (!StartMenu(CXTPSkinPopupMenuState::focusMouse)) { pSubMenu->InternalRelease(); return 0; } m_bFirstClick = bButtonDown; if (!m_bNoNotify) { SendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, MAKELONG(0, (pSubMenu->m_bSysMenu ? 1 : 0))); } pSubMenu->m_bSendUninit = TRUE; CSize size = pSubMenu->RecalcLayout(); if ((GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) && ((dwFlags & TPM_CENTERALIGN) == 0)) dwFlags ^= TPM_RIGHTALIGN; CPoint pt(x, y); if (dwFlags & TPM_RIGHTALIGN) { pt.x -= size.cx; pSubMenu->m_bDroppedLeft = TRUE; } else if (dwFlags & TPM_CENTERALIGN) { pt.x -= size.cx / 2; } if (dwFlags & TPM_BOTTOMALIGN) { pt.y -= size.cy; } else if (dwFlags & TPM_VCENTERALIGN) { pt.y -= size.cy / 2; } AdjustMonitorRect(pSubMenu, pt, size, dwFlags, prcRect); PlayEventSound(xtpSoundMenuPopup); ::SetWindowPos(pSubMenu->m_hWnd, HWND_TOPMOST, pt.x, pt.y, size.cx, size.cy, SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOACTIVATE); m_bButtonDown = bButtonDown; RunLoop(0); CloseHierarchy(pSubMenu); if (pSubMenu->m_bSendUninit && !m_bNoNotify) { SendMessage(pSubMenu->m_hWndNotify, WM_UNINITMENUPOPUP, (WPARAM)pSubMenu->m_hMenu, MAKELONG(0, pSubMenu->m_bSysMenu ? MF_SYSMENU: 0)); } pSubMenu->DestroyWindow(); pSubMenu->InternalRelease(); return m_bSynchronous ? m_nLastCommand : TRUE; }