You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3250 lines
68 KiB
C++

// 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;
}