flatpopupmenu.cpp

来自「《突破Visual C++.NET编程实例五十讲+源文件,初学者学习的好东东!」· C++ 代码 · 共 917 行 · 第 1/2 页

CPP
917
字号
#include "stdafx.h"
#include "FlatPopupMenu.h"

// 初始化静态成员
bool CFlatPopupMenu::m_bClassRegistered=false;

// stub window 程序
static LRESULT CALLBACK stubWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
 
//构造函数
CFlatPopupMenu::CFlatPopupMenu()
{
// 缺省颜色
  m_Colors[colorBorder]=GetSysColor(COLOR_MENUTEXT);
  m_Colors[colorBackground]=GetSysColor(COLOR_MENU);
  m_Colors[colorText]=GetSysColor(COLOR_MENUTEXT);
  m_Colors[colorGrayedText]=GetSysColor(COLOR_GRAYTEXT);
  m_Colors[colorHighlight]=GetSysColor(COLOR_HIGHLIGHT);
  m_Colors[colorHighlightText]=GetSysColor(COLOR_HIGHLIGHTTEXT);
  m_Colors[colorIconTransparent]=RGB(255,0,255);    // 粉红色
  m_Colors[colorLightShadow]=GetSysColor(COLOR_3DHILIGHT);
  m_Colors[colorDarkShadow]=GetSysColor(COLOR_3DSHADOW);

// 缺省字体及大小
  m_strFont="楷体_GB2312";//MS Sans Serif
  m_FontSize=10;

// 弹出式子菜单延迟时间缺省为400ms
  m_PopupDelay=400;

// 设置GDI对象
  m_hFont=NULL;
  m_hBoldFont=NULL;
  m_hBorderPen=NULL;
  m_hBackBrush=NULL;
  m_hLightShadowPen=NULL;
  m_hDarkShadowPen=NULL;
  m_hBackPen=NULL;
  m_hSelectedBrush=NULL;
  m_hTextPen=NULL;
  m_hSelectedTextPen=NULL;
  m_hBitmap=NULL;

// 其他内部变量
  m_bChild=false;
  m_pPrevious=NULL;
  m_State=stateInactive;
  m_hWnd=NULL;
}

//析构函数
CFlatPopupMenu::~CFlatPopupMenu()
{
// 若正选择菜单,则释放
  if(m_State==stateTrack)
    ReleaseCapture();
//清除窗口
  if(m_hWnd)
    DestroyWindow(m_hWnd);
//清除GDI对象
  Cleanup();
}

//清除GDI对象
void CFlatPopupMenu::Cleanup(void)
{
  if(m_hFont)
  {
    DeleteObject(m_hFont);
    DeleteObject(m_hBoldFont);
    DeleteObject(m_hBorderPen);
    DeleteObject(m_hBackBrush);
    DeleteObject(m_hLightShadowPen);
    DeleteObject(m_hDarkShadowPen);
    DeleteObject(m_hBackPen);
    DeleteObject(m_hSelectedBrush);
    DeleteObject(m_hTextPen);
    DeleteObject(m_hSelectedTextPen);
    DeleteObject(m_hBitmap);

    m_hFont=NULL;
    m_hBoldFont=NULL;
    m_hBorderPen=NULL;
    m_hBackBrush=NULL;
    m_hLightShadowPen=NULL;
    m_hDarkShadowPen=NULL;
    m_hBackPen=NULL;
    m_hSelectedBrush=NULL;
    m_hTextPen=NULL;
    m_hSelectedTextPen=NULL;
    m_hBitmap=NULL;
  }
}

//创建GDI对象
void CFlatPopupMenu::CreateObjects(void)
{
  LOGFONT    lf;
  HDC        hDC;
  HGDIOBJ    hOldFont;
  TEXTMETRIC tm;

  Cleanup();

// 创建菜单字体
  ZeroMemory(&lf,sizeof(lf));

  hDC=CreateIC("DISPLAY",NULL,NULL,NULL);
  lf.lfHeight=-MulDiv(m_FontSize,GetDeviceCaps(hDC,LOGPIXELSY),72);

  lf.lfWeight=FW_NORMAL;
  lf.lfCharSet=ANSI_CHARSET;
  lf.lfOutPrecision=OUT_DEFAULT_PRECIS;
  lf.lfClipPrecision=CLIP_DEFAULT_PRECIS;
  lf.lfQuality=DEFAULT_QUALITY;
  lf.lfPitchAndFamily=FF_DONTCARE | DEFAULT_PITCH;
  lstrcpyn(lf.lfFaceName,m_strFont.c_str(),sizeof(lf.lfFaceName)/sizeof(TCHAR));

  m_hFont=::CreateFontIndirect(&lf);

// 创建粗体菜单字体
  lf.lfWeight=FW_BOLD;
  m_hBoldFont=::CreateFontIndirect(&lf);

// 获取字体高度
  hOldFont=SelectObject(hDC,m_hFont);
  GetTextMetrics(hDC,&tm);
  m_FontHeight=tm.tmHeight;

  SelectObject(hDC,m_hBoldFont);
  GetTextMetrics(hDC,&tm);
  m_BoldFontHeight=tm.tmHeight;
  SelectObject(hDC,hOldFont);

  DeleteDC(hDC);

// 创建画笔、画刷
  m_hBorderPen=CreatePen(PS_SOLID,1,m_Colors[colorBorder]);
  m_hBackBrush=CreateSolidBrush(m_Colors[colorBackground]);
  m_hSelectedBrush=CreateSolidBrush(m_Colors[colorHighlight]);
  m_hLightShadowPen=CreatePen(PS_SOLID,1,m_Colors[colorLightShadow]);
  m_hDarkShadowPen=CreatePen(PS_SOLID,1,m_Colors[colorDarkShadow]);
  m_hBackPen=CreatePen(PS_SOLID,1,m_Colors[colorBackground]);
  m_hTextPen=CreatePen(PS_SOLID,1,m_Colors[colorText]);
  m_hSelectedTextPen=CreatePen(PS_SOLID,1,m_Colors[colorHighlightText]);

// 创建位图
  if(m_BitmapID!=(UINT)-1)
    m_hBitmap=LoadBitmap(m_hInstance,MAKEINTRESOURCE(m_BitmapID));
}

bool CFlatPopupMenu::Create(HINSTANCE hInstance,const UINT bitmap_id)
{
// 保存变量
  m_hInstance=hInstance;
  m_BitmapID=bitmap_id;

// 注册类
  if(!m_bClassRegistered)
  {
    if(!RegisterClass())
      return false;

    m_bClassRegistered=true;
  }

// 清除所有菜单项
  m_Items.clear();

// 创建GDI对象
  CreateObjects();
  return true;
}

// 注册window 类 
bool CFlatPopupMenu::RegisterClass(void)
{
  WNDCLASS wc;

  wc.style=0;
  wc.lpfnWndProc=stubWindowProc;
  wc.cbClsExtra=0;
  wc.cbWndExtra=0;
  wc.hInstance=m_hInstance;
  wc.hIcon=NULL;
  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
  wc.hbrBackground=NULL;
  wc.lpszMenuName=NULL;
  wc.lpszClassName="fpmMenuClass";

  return ::RegisterClass(&wc)!=FALSE;
}

//增加菜单项
bool CFlatPopupMenu::AppendItem(const DWORD dwFlags,LPCTSTR pszName,const UINT itemid,const int icon)
{
  CItem item;

  item.m_dwFlags=dwFlags;

  if(pszName)
    item.m_strName=pszName;

  item.m_ItemID=itemid;
  item.m_IconIndex=icon;
  item.m_pPopup=NULL;

  m_Items.push_back(item);
  return true;
}

//增加弹出式菜单
bool CFlatPopupMenu::AppendPopup(const DWORD dwFlags,LPCTSTR pszName,CFlatPopupMenu& popup,const int icon)
{
  CItem item;

// 不允许分隔条
  if((dwFlags & itemSeparator)!=0)
    return false;

//增加菜单项
  item.m_dwFlags=dwFlags;
  item.m_strName=pszName;
  item.m_ItemID=0;
  item.m_IconIndex=icon;
  item.m_pPopup=&popup;

  popup.m_bChild=true;
  popup.m_pPrevious=this;

  m_Items.push_back(item);
  return true;
}

//选择菜单项
UINT CFlatPopupMenu::Track(int x,int y,HWND hWnd,const bool bModal,const bool bPopup)
{
  HDC hDC;
  std::vector<CItem>::iterator it;
  SIZE size;
  int maxw,top;
  HGDIOBJ hOldFont;
  MSG msg;
  RECT rcScreen;

  hDC=CreateIC("DISPLAY",NULL,NULL,NULL);

// 获取最大菜单项宽度
  maxw=0;
  top=3;

  for(it=m_Items.begin();it!=m_Items.end();it++)
  {
    it->m_Top=top;

    if(!(it->m_dwFlags & itemSeparator))
    {
      hOldFont=SelectObject(hDC,(it->m_dwFlags & itemBold)==0 ? m_hFont : m_hBoldFont);
      GetTextExtentPoint32(hDC,it->m_strName.c_str(),it->m_strName.length(),&size);
      maxw=max(size.cx,maxw);
      SelectObject(hDC,hOldFont);

      top+=17;
      it->m_Height=17;
    }
    else
    {
      top+=5;
      it->m_Height=5;
    }
  }

  DeleteDC(hDC);

// 获取屏幕大小,对x和y作必要的调整
  m_Width=1+2+18+2+maxw+18+1;
  m_SelectedItem=-1;
  m_SelectedID=0;

  SystemParametersInfo(SPI_GETWORKAREA,0,&rcScreen,0);
  
  if(x+m_Width>rcScreen.right)
    x=max(0,rcScreen.right-m_Width);

  if(y+top+2>rcScreen.bottom)
    y=max(0,rcScreen.bottom-(top+2));

// 创建窗口
  m_hWnd=::CreateWindowEx(WS_EX_TOOLWINDOW,   //按钮不能在任务栏上显示
                          "fpmMenuClass",
                          "",
                          WS_POPUP | WS_VISIBLE,
                          x,
                          y,
                          m_Width,
                          top+2,
                          NULL,
                          NULL,
                          m_hInstance,
                          (LPVOID)this);

  if(m_hWnd==NULL)
    return false;

  if(bPopup)
  {
    m_bWaitLeftButton=false;
    m_bWaitRightButton=false;
    m_State=stateShow;
  }
  else
  {
    SetCapture(m_hWnd);
    m_State=stateTrack;

    m_bWaitLeftButton=(GetKeyState(VK_LBUTTON) & 0x8000)!=0;
    m_bWaitRightButton=(GetKeyState(VK_RBUTTON) & 0x8000)!=0;
  }

  m_bModal=bModal;
  m_hWndCommand=hWnd;

  if(m_bModal)
  {
    while(GetMessage(&msg,NULL,0,0)==TRUE && IsWindow(m_hWnd))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return m_SelectedID;
  }

  return 0;
}

//stub window 程序
static LRESULT CALLBACK stubWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
  CFlatPopupMenu *pClass;
  switch(uMsg)
  {
    case WM_CREATE:
      SetWindowLong(hWnd,GWL_USERDATA,(LONG)((LPCREATESTRUCT)lParam)->lpCreateParams);
      break;
  
    case WM_ERASEBKGND:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnEraseBkgnd(hWnd,(HDC)wParam);
      return TRUE;
  
    case WM_LBUTTONDOWN:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnLButtonDown(hWnd,LOWORD(lParam),HIWORD(lParam));
      break;

    case WM_RBUTTONDOWN:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnLButtonDown(hWnd,LOWORD(lParam),HIWORD(lParam));
      break;

    case WM_LBUTTONUP:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnLButtonUp(hWnd,LOWORD(lParam),HIWORD(lParam));
      break;

    case WM_RBUTTONUP:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnRButtonUp(hWnd,LOWORD(lParam),HIWORD(lParam));
      break;

    case WM_MOUSEMOVE:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnMouseMove(hWnd,LOWORD(lParam),HIWORD(lParam));
      break;

    case WM_TIMER:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnTimer(hWnd,wParam);
      break;

    case WM_CHAR:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnChar(hWnd,(char)wParam);
      break;

    case WM_PAINT:
      {
        PAINTSTRUCT ps;
        
        BeginPaint(hWnd,&ps);
        pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
        pClass->OnPaint(hWnd,ps.hdc);
        EndPaint(hWnd,&ps);
      }
      break;

    case WM_DESTROY:
      pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
      pClass->OnDestroy(hWnd);
      break;
  }

  return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

// WM_CHAR消息处理函数
void CFlatPopupMenu::OnChar(HWND hWnd,TCHAR c)
{
  std::vector<CItem>::const_iterator it;
  std::string::size_type pos;

// 转换成小写
  c=(TCHAR)CharLower((LPTSTR)c);

// 查找菜单项
  for(it=m_Items.begin();it!=m_Items.end();it++)
  {
    if((it->m_dwFlags & (itemSeparator | itemGrayed | itemNotSelectable))==0)
    {
      if((pos=it->m_strName.find('&'))!=std::string::npos && it->m_strName.length()>pos+1 && c==(TCHAR)CharLower((LPTSTR)it->m_strName[pos+1]))
      {
      // 设置返回值
        SetReturn(it->m_ItemID);
        return;
      }
    }
  }
}

// WM_TIMER 消息处理函数
void CFlatPopupMenu::OnTimer(HWND hWnd,unsigned short timerid)
{
  POINT p;
  KillTimer(hWnd,timerid);

//显示弹出式菜单
  if(m_SelectedItem!=-1 && m_Items[m_SelectedItem].m_pPopup)
  {
  // 获取显示位置
    p.x=m_Width-5;
    p.y=m_Items[m_SelectedItem].m_Top-3;
    ClientToScreen(hWnd,&p);

  // 跟踪选择
    if(m_Items[m_SelectedItem].m_pPopup->Track(p.x,p.y,NULL,true,true))
      DestroyAll();
  }
}


//画菜单项
void CFlatPopupMenu::DrawItem(HWND hWnd,HDC hDC,const int index,const CItem& item)
{
  bool    bSelected=index==m_SelectedItem;
  HGDIOBJ hOldPen,hOldBrush,hOldFont;
  POINT   p[3];
  int     i,x,y,h,nColor;
  RECT    rect;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?