// MenuEx.h: interface for the CMenuEx class.
//CMenuEx
//接口:
// void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)
// void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar)
// 与InitMenu不同的是:InitMenu并不修改第一级菜单为自绘风格,而
// 该函数有包括第一级菜单但必须注意:该类的任一实例都只能调用这
// 两个函数中的一个,不能一同使用
// void SetHighLightColor(COLORREF crColor)
// void SetBackColor(COLORREF)
// void SetTextColor(COLORREF)
// void SetImageLeft(UINT idBmpLeft)
// 作者:lbird(郑恒)
// 2003.1.10 zh0116@163.net QQ:10992445
//
#if !defined(AFX_MENUEX_H__FE677F6B_2315_11D7_8869_BB2B2A4F4D45__INCLUDED_)
#define AFX_MENUEX_H__FE677F6B_2315_11D7_8869_BB2B2A4F4D45__INCLUDED_
#include "afxtempl.h"
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
typedef struct tagMENUITEM
{
CString strText;
UINT uID;
#ifdef MENUCHAR
UINT uChr;
#endif
UINT uIndex;
int uPositionImageLeft;
}MENUITEM;
typedef MENUITEM * LPMENUITEM;
///
class CMenuEx : public CMenu
{
public:
void InitPopupMenu(CMenu *pPopupMenu,UINT uToolBar,CToolBar *pToolBar);
void ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu=FALSE);
void SetHighLightColor(COLORREF crColor);
void SetBackColor(COLORREF);
void SetTextColor(COLORREF);
#ifdef MENUCHAR
LRESULT MenuChar(UINT nChar);
#endif
void SetImageLeft(UINT idBmpLeft);
void MeasureItem(LPMEASUREITEMSTRUCT lpMIS);
void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar);
void DrawItem(LPDRAWITEMSTRUCT lpDIS);
CMenuEx();
virtual ~CMenuEx();
protected:
int m_nSeparator;
CSize m_szImageLeft;
CBitmap m_bmpImageLeft;
int m_nWidthLeftImage;
BOOL m_bHasImageLeft; //是否有侧边位图
BOOL m_bInitial; //菜单是否已初始化,即设定了自绘风格
int GetImageFromToolBar(UINT uToolBar,CToolBar *pToolBar,COLORREF crMask=RGB(192,192,192));
CList<MENUITEM *,MENUITEM *> m_ListMenu;
#ifdef MENUCHAR
CList<MENUITEM *,MENUITEM *> m_currentListMenu; //是m_ListMenu的子集,指当前打开的菜单
#endif
COLORREF m_colMenu;
COLORREF m_colTextSelected;
void DrawImageLeft(CDC *pDC,CRect &rect,LPMENUITEM lpItem);
void TextMenu(CDC *pDC,CRect &rect,CRect rtText,BOOL bSelected,BOOL bGrayed,LPMENUITEM lpItem);
CImageList m_ImageList;
COLORREF m_colText;
CSize m_szImage; //菜单项位图的大小
void DrawMenuItemImage(CDC *pDC,CRect &rect,BOOL bSelected,BOOL bChecked,BOOL bGrayed,BOOL bHasImage,LPMENUITEM lpItem);
void GrayString(CDC *pDC,const CString &str,const CRect rect);
};
#endif // !defined(AFX_MENUEX_H__FE677F6B_2315_11D7_8869_BB2B2A4F4D45__INCLUDED_)
/*
*******************************************************************************************************************
.CPP
*******************************************************************************************************************
*/
// MenuEx.cpp: implementation of the CMenuEx class.
//
//
#include "stdafx.h"
#include "MenuEx.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//
// Construction/Destruction
//
CMenuEx::CMenuEx():m_szImage(16,15)
{
m_colMenu =::GetSysColor(COLOR_MENU);
m_colText =::GetSysColor(COLOR_MENUTEXT);
m_colTextSelected =::GetSysColor(COLOR_HIGHLIGHTTEXT);
m_bInitial =FALSE;
m_bHasImageLeft =FALSE;
m_nSeparator = 10; //sparator的默认高度
}
CMenuEx::~CMenuEx()
{
m_ImageList.DeleteImageList();
while(!m_ListMenu.IsEmpty())
delete m_ListMenu.RemoveHead();
if(m_bHasImageLeft)
m_bmpImageLeft.DeleteObject();
}
/
//当菜单项为不可用时绘制灰色的文本
void CMenuEx::GrayString(CDC *pDC, const CString &str, const CRect rect)
{
CRect rt(rect);
//int nMode =pDC->SetBkMode(TRANSPARENT);
rt.left +=1;
rt.top +=1;
pDC->SetTextColor(RGB(255,255,255));
pDC->DrawText(str,&rt,DT_EXPANDTABS|DT_VCENTER|DT_SINGLELINE);
rt.left -=1;
rt.top -=1;
pDC->SetTextColor(RGB(127,127,127));
pDC->DrawText(str,&rt,DT_EXPANDTABS|DT_VCENTER|DT_SINGLELINE);
//pDC->SetBkMode(nMode);
}
/
//绘制菜单项位图
void CMenuEx::DrawMenuItemImage(CDC *pDC, CRect &rect, BOOL bSelected, BOOL bChecked,
BOOL bGrayed, BOOL bHasImage,LPMENUITEM lpItem)
{
CRect rt(rect.left ,rect.top ,rect.left + m_szImage.cx + 4,
rect.top + m_szImage.cy + 4);
if(bChecked)
{
if(bGrayed)
{
//菜单不可用
GrayString(pDC,"√",rt);
}
else
{
if(bSelected)
{
//菜单选中
//当该项被选中仅多绘制一个立体矩形
pDC->Draw3dRect(&rt,RGB(255,255,255),RGB(127,127,127));
}
rt.InflateRect(-2,-2);
//画出"√"
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(m_colText);
pDC->DrawText("√",&rt,DT_EXPANDTABS|DT_VCENTER|DT_SINGLELINE);
}
rect.left +=m_szImage.cx + 4 +2 ;
return ;
}
if(bHasImage)
{
CPoint pt(rt.left+2 , rt.top+2 );
UINT uStyle =ILD_TRANSPARENT; //CImageList::Draw()绘制位图的风格
if(bGrayed)
{
uStyle |=ILD_BLEND50; //菜单不可用所以位图较暗
}
else
{
if(bSelected)
{
//当该项被选中仅多绘制一个立体矩形
pDC->Draw3dRect(&rt,RGB(255,255,255),RGB(127,127,127));
}
}
m_ImageList.Draw(pDC,lpItem->uIndex,pt,uStyle); //在菜单项中绘制位图
//调整可绘制矩形的大小
//4:位图外接矩形比位图大4
//2:菜单文本与位图外接矩形的间隔为2
rect.left +=m_szImage.cx + 4 + 2;
}
}
/
//绘制菜单项文本
//参数:rect:立体矩形的RECT
// rtText:菜单文本的RECT
void CMenuEx::TextMenu(CDC *pDC, CRect &rect,CRect rtText,BOOL bSelected, BOOL bGrayed, LPMENUITEM lpItem)
{
//选中状态的菜单项要先画出立体矩形
if(bSelected)
pDC->Draw3dRect(&rect,RGB(127,127,127),RGB(255,255,255));
if(bGrayed)
{
GrayString(pDC,lpItem->strText,rtText);
}
else
{
pDC->DrawText(lpItem->strText,rtText,DT_LEFT|DT_EXPANDTABS|DT_VCENTER);
}
}
void CMenuEx::DrawImageLeft(CDC *pDC, CRect &rect,LPMENUITEM lpItem)
{
if(!m_bHasImageLeft || lpItem->uPositionImageLeft ==-1)
return ;
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap *oldBmp =(CBitmap *) memDC.SelectObject(&m_bmpImageLeft);
int cy; //设定该菜单项应从哪画起
if(m_szImageLeft.cy >= lpItem->uPositionImageLeft + rect.Height())
{
cy =(int) m_szImageLeft.cy - lpItem->uPositionImageLeft - rect.Height();
ASSERT(cy>=0);
}
else
cy =0;
pDC->BitBlt(rect.left ,rect.top ,m_szImageLeft.cx ,rect.Height(),&memDC,0,cy,SRCCOPY);
memDC.SelectObject(oldBmp);
memDC.DeleteDC();
rect.left +=m_szImageLeft.cx+1;
}
void CMenuEx::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CDC dc;
LPMENUITEM lpItem;
CRect rect(lpDIS->rcItem);
dc.Attach(lpDIS->hDC);
lpItem =(LPMENUITEM)lpDIS->itemData;
if(lpDIS->itemState & ODS_SELECTED)
dc.SetTextColor(m_colTextSelected);
else
dc.SetTextColor(m_colText);
//设定背景色
CBrush brush(m_colMenu);
dc.FillRect(&rect, &brush);
//设定显示模式
dc.SetBkMode(TRANSPARENT);
//绘制侧边位图
DrawImageLeft(&dc,rect,lpItem);
if(lpItem->uID==0)//分隔条
{
rect.top =rect.Height()/2+rect.top ;
rect.bottom =rect.top +2;
rect.left +=2;
rect.right -=2;
dc.Draw3dRect(rect,RGB(64,0,128),RGB(255,255,255));
}
else
{
BOOL bSelected =lpDIS->itemState & ODS_SELECTED;
BOOL bChecked =lpDIS->itemState & ODS_CHECKED;
BOOL bGrayed =lpDIS->itemState & ODS_GRAYED;
BOOL bHasImage =(lpItem->uIndex!=-1);
//设定菜单文本的区域
CRect rtText(rect.left+m_szImage.cx+4+2, rect.top,rect.right ,rect.bottom );
rtText.InflateRect(-2,-2);
//绘制菜单位图
DrawMenuItemImage(&dc,rect,bSelected,bChecked,bGrayed,bHasImage,lpItem);
//绘制菜单文本
TextMenu(&dc,rect,rtText,bSelected,bGrayed,lpItem);
#ifdef MENUCHAR
//加入当前菜单链表
m_currentListMenu.AddTail(lpItem);
#endif
}
dc.Detach();
}
//
//改变菜单风格
//注意第二个参数:FALSE:表示pMenu指向的不是第一级菜单
void CMenuEx::ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu)
{
ASSERT(pMenu);
TRACE("ChangeStyle/n");//AfxMessageBox("");
LPMENUITEM lpItem;
CMenu *pSubMenu;
int m,nPosition=0; //该变量用来绘制侧边位图的位置
int inx;
UINT idx,x;
for(int i=(int)pMenu->GetMenuItemCount()-1 ;i>=0; i--)
{
lpItem =new MENUITEM;
lpItem->uID =pMenu->GetMenuItemID(i);
if(!bIsMainMenu) //不是第一级菜单
lpItem->uPositionImageLeft =-1;//二级以下菜单不支持侧边位图
else
lpItem->uPositionImageLeft =nPosition;
if(lpItem->uID >0)
{
if(bIsMainMenu)
nPosition +=m_szImage.cy+4;
//保存菜单文本
pMenu->GetMenuString(i,lpItem->strText,MF_BYPOSITION);
#ifdef MENUCHAR
//保存菜单文本中&后的字符
//如果没有则lpItem->uChr为0
int ret=lpItem->strText.Find('&');
lpItem->uChr =0;
if(ret>=0)
lpItem->uChr =lpItem->strText[ret+1];
//字符统一成大小
lpItem->uChr &=~0x20;
#endif
//由工具栏位图中寻找菜单项的位图
//如果没有则uIndex为-1
lpItem->uIndex =-1;
if(pToolBar)
{
for(m=0; m<(pToolBar->GetToolBarCtrl().GetButtonCount()) ;m++)
{
pToolBar->GetButtonInfo(m,idx,x,inx);
if(idx==lpItem->uID)
{
lpItem->uIndex=inx;
break;
}
}
}
//如果该项下还有子菜单,则递归调用该函数来修改其子菜单的风格
pSubMenu =pMenu->GetSubMenu(i);
if(pSubMenu)
ChangeStyle(pSubMenu,pToolBar);
}
else
{
if(bIsMainMenu)
nPosition +=m_nSeparator;
}
//修改菜单风格为自绘
pMenu->ModifyMenu(i,MF_BYPOSITION|MF_OWNERDRAW,lpItem->uID,(LPCTSTR)lpItem);
m_ListMenu.AddTail(lpItem);
}
}
//
//由工具栏的位图来产生菜单所用的位图列表m_ImageList
int CMenuEx::GetImageFromToolBar(UINT uToolBar, CToolBar *pToolBar,COLORREF crMask/*工具栏位图的掩码*/)
{
if(!pToolBar)
return 0;
CBitmap bmp;
int nWidth,nHeight;
BITMAP bmpInfo;
bmp.LoadBitmap(uToolBar);
bmp.GetBitmap(&bmpInfo);
//得到位图的高度
nHeight =bmpInfo.bmHeight;
int nCount=0;
int ret =pToolBar->GetToolBarCtrl().GetButtonCount();
//得到工具栏中位图的个数nCount
for(int i=0;i<ret;i++)
if(pToolBar->GetItemID(i)!=ID_SEPARATOR)
nCount ++;
//计算出位图的宽度
nWidth =bmpInfo.bmWidth/nCount;
bmp.DeleteObject();
TRACE("Menu Bitmap--width:%d/theight:%d/n",nWidth,nHeight);
//创建位图列表
m_ImageList.Create(uToolBar,nWidth,nHeight,crMask);
m_szImage.cx =nWidth;
m_szImage.cy =nHeight;
return nCount;
}
void CMenuEx::InitMenu(CMenu *pMenu, UINT uToolBar, CToolBar *pToolBar)
{
//已设定了风格
if(m_bInitial)
return ;
GetImageFromToolBar(uToolBar,pToolBar);
CMenu *pSubMenu,*pSubsub;
MENUITEM *lpItem;
UINT i;
int j,m;
int nPosition; //该变量用来绘制侧边位图的位置
for(i=0;i<pMenu->GetMenuItemCount();i++)
{
pSubMenu =pMenu->GetSubMenu(i);
if(pSubMenu)
{
nPosition =0;
//注意j一定要为int类型,如果为UINT是检查不出j>=0!
for(j=(int)pSubMenu->GetMenuItemCount()-1;j>=0;j--)
{
lpItem =new MENUITEM;
lpItem->uID =pSubMenu->GetMenuItemID(j);
lpItem->uPositionImageLeft =nPosition;
if(lpItem->uID>0)
{
nPosition +=m_szImage.cy+4;
pSubMenu->GetMenuString(j,lpItem->strText,MF_BYPOSITION);
#ifdef MENUCHAR
//保存菜单文本中&后的字符
//如果没有则lpItem->uChr为0
int ret =lpItem->strText.Find('&');
lpItem->uChr=0;
if(ret>=0)
lpItem->uChr =lpItem->strText[ret+1];
//统一大小
lpItem->uChr &=~0x20;
#endif
//由工具栏位图中寻找菜单项的位图
//如果没有则uIndex为-1
lpItem->uIndex =-1;
for(m=0; m<(pToolBar->GetToolBarCtrl().GetButtonCount()) ;m++)
{
int inx;
UINT idx,x;
pToolBar->GetButtonInfo(m,idx,x,inx);
if(idx==lpItem->uID)
{
lpItem->uIndex=inx;
break;
}
}
}
else
{
//separator
nPosition +=m_nSeparator;
}
m_ListMenu.AddTail(lpItem);
pSubMenu->ModifyMenu(j,MF_BYPOSITION|MF_OWNERDRAW,
lpItem->uID,LPCTSTR(lpItem));
pSubsub =pSubMenu->GetSubMenu(j);
//只有第一级菜单才由工具栏获得位图!!
if(pSubsub)
ChangeStyle(pSubsub,pToolBar);
}
}
}
m_bInitial =TRUE;
}
void CMenuEx::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
{
MENUITEM *lpItem =(LPMENUITEM)lpMIS->itemData;
if(lpItem->uID==0)//分隔条
{
lpMIS->itemHeight =m_nSeparator;
//lpMIS->itemWidth =50;
}
else
{
CDC *pDC =AfxGetMainWnd()->GetDC();
CString strText=lpItem->strText;
CSize size;
size=pDC->GetTextExtent(lpItem->strText);
lpMIS->itemWidth = size.cx +m_szImage.cx+4;
lpMIS->itemHeight =m_szImage.cy+4;
AfxGetMainWnd()->ReleaseDC(pDC);
}
}
void CMenuEx::SetImageLeft(UINT idBmpLeft)
{
m_bmpImageLeft.LoadBitmap(idBmpLeft);
m_bHasImageLeft = TRUE;
BITMAP bmpInfo;
m_bmpImageLeft.GetBitmap(&bmpInfo);
m_szImageLeft.cx =bmpInfo.bmWidth;
m_szImageLeft.cy =bmpInfo.bmHeight;
}
#ifdef MENUCHAR
LRESULT CMenuEx::MenuChar(UINT nChar)
{
nChar &=~0x20;
MENUITEM *lpItem;
for(POSITION pos=m_currentListMenu.GetHeadPosition();pos;)
{
lpItem =(LPMENUITEM)m_currentListMenu.GetNext(pos);
if(lpItem->uChr ==nChar)
{
AfxGetMainWnd()->SendMessage(WM_COMMAND,lpItem->uID);
return 1L;
}
}
return 0L;
}
#endif
void CMenuEx::SetTextColor(COLORREF crColor)
{
m_colText =crColor;
}
void CMenuEx::SetBackColor(COLORREF crColor)
{
m_colMenu =crColor;
}
void CMenuEx::SetHighLightColor(COLORREF crColor)
{
m_colTextSelected =crColor;
}
//
//修改菜单的风格
//与InitMenu不同的是:InitMenu并不修改第一级菜单为自绘风格,而
//该函数有包括第一级菜单
//但必须注意:该类的任一实例都只能调用这两个函数中的一个,不能一同使用
void CMenuEx::InitPopupMenu(CMenu *pPopupMenu,UINT uToolBar, CToolBar *pToolBar)
{
//已设定了主窗口菜单风格
if(m_bInitial)
return ;
GetImageFromToolBar(uToolBar,pToolBar);
ChangeStyle(pPopupMenu,pToolBar);
m_bInitial =TRUE;
}