FlatPopupMenu.cpp
上传用户:szcanmei
上传日期:2007-01-02
资源大小:48k
文件大小:26k
源码类别:

菜单

开发平台:

Visual C++

  1. // A flat popup menu for controls
  2. // Copyright (c) 1999 Andy Brown <andy@mirage.dabsol.co.uk>
  3. // You may do whatever you like with this file, I just don't care.
  4. #include "stdafx.h"
  5. #include "FlatPopupMenu.h"
  6. // initialise the static member
  7. bool CFlatPopupMenu::m_bClassRegistered=false;
  8. // stub window procedure
  9. static LRESULT CALLBACK stubWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
  10.  
  11. /***************/
  12. /* Constructor */
  13. /***************/
  14. CFlatPopupMenu::CFlatPopupMenu()
  15. {
  16. // default colors
  17.   m_Colors[colorBorder]=GetSysColor(COLOR_MENUTEXT);
  18.   m_Colors[colorBackground]=GetSysColor(COLOR_MENU);
  19.   m_Colors[colorText]=GetSysColor(COLOR_MENUTEXT);
  20.   m_Colors[colorGrayedText]=GetSysColor(COLOR_GRAYTEXT);
  21.   m_Colors[colorHighlight]=GetSysColor(COLOR_HIGHLIGHT);
  22.   m_Colors[colorHighlightText]=GetSysColor(COLOR_HIGHLIGHTTEXT);
  23.   m_Colors[colorIconTransparent]=RGB(255,0,255);    // bright pink (mmm, nice...)
  24.   m_Colors[colorLightShadow]=GetSysColor(COLOR_3DHILIGHT);
  25.   m_Colors[colorDarkShadow]=GetSysColor(COLOR_3DSHADOW);
  26. // default font and size
  27.   m_strFont="MS Sans Serif";
  28.   m_FontSize=8;
  29. // 400ms is the default submenu delay
  30.   m_PopupDelay=400;
  31. // GDI objects
  32.   m_hFont=NULL;
  33.   m_hBoldFont=NULL;
  34.   m_hBorderPen=NULL;
  35.   m_hBackBrush=NULL;
  36.   m_hLightShadowPen=NULL;
  37.   m_hDarkShadowPen=NULL;
  38.   m_hBackPen=NULL;
  39.   m_hSelectedBrush=NULL;
  40.   m_hTextPen=NULL;
  41.   m_hSelectedTextPen=NULL;
  42.   m_hBitmap=NULL;
  43. // other internal variables
  44.   m_bChild=false;
  45.   m_pPrevious=NULL;
  46.   m_State=stateInactive;
  47.   m_hWnd=NULL;
  48. }
  49. /**************/
  50. /* Destructor */
  51. /**************/
  52. CFlatPopupMenu::~CFlatPopupMenu()
  53. {
  54. // if we're tracking, release it
  55.   if(m_State==stateTrack)
  56.     ReleaseCapture();
  57. // clean up the window
  58.   if(m_hWnd)
  59.     DestroyWindow(m_hWnd);
  60. // clean up the GDI objects
  61.   Cleanup();
  62. }
  63. /***********************/
  64. /* Cleanup GDI objects */
  65. /***********************/
  66. void CFlatPopupMenu::Cleanup(void)
  67. {
  68.   if(m_hFont)
  69.   {
  70.     DeleteObject(m_hFont);
  71.     DeleteObject(m_hBoldFont);
  72.     DeleteObject(m_hBorderPen);
  73.     DeleteObject(m_hBackBrush);
  74.     DeleteObject(m_hLightShadowPen);
  75.     DeleteObject(m_hDarkShadowPen);
  76.     DeleteObject(m_hBackPen);
  77.     DeleteObject(m_hSelectedBrush);
  78.     DeleteObject(m_hTextPen);
  79.     DeleteObject(m_hSelectedTextPen);
  80.     DeleteObject(m_hBitmap);
  81.     m_hFont=NULL;
  82.     m_hBoldFont=NULL;
  83.     m_hBorderPen=NULL;
  84.     m_hBackBrush=NULL;
  85.     m_hLightShadowPen=NULL;
  86.     m_hDarkShadowPen=NULL;
  87.     m_hBackPen=NULL;
  88.     m_hSelectedBrush=NULL;
  89.     m_hTextPen=NULL;
  90.     m_hSelectedTextPen=NULL;
  91.     m_hBitmap=NULL;
  92.   }
  93. }
  94. /**********************/
  95. /* Create GDI objects */
  96. /**********************/
  97. void CFlatPopupMenu::CreateObjects(void)
  98. {
  99.   LOGFONT    lf;
  100.   HDC        hDC;
  101.   HGDIOBJ    hOldFont;
  102.   TEXTMETRIC tm;
  103. // cleanup any previous
  104.   Cleanup();
  105. // create normal menu font
  106.   ZeroMemory(&lf,sizeof(lf));
  107.   hDC=CreateIC("DISPLAY",NULL,NULL,NULL);
  108.   lf.lfHeight=-MulDiv(m_FontSize,GetDeviceCaps(hDC,LOGPIXELSY),72);
  109.   lf.lfWeight=FW_NORMAL;
  110.   lf.lfCharSet=ANSI_CHARSET;
  111.   lf.lfOutPrecision=OUT_DEFAULT_PRECIS;
  112.   lf.lfClipPrecision=CLIP_DEFAULT_PRECIS;
  113.   lf.lfQuality=DEFAULT_QUALITY;
  114.   lf.lfPitchAndFamily=FF_DONTCARE | DEFAULT_PITCH;
  115.   lstrcpyn(lf.lfFaceName,m_strFont.c_str(),sizeof(lf.lfFaceName)/sizeof(TCHAR));
  116.   m_hFont=::CreateFontIndirect(&lf);
  117. // create bold menu font
  118.   lf.lfWeight=FW_BOLD;
  119.   m_hBoldFont=::CreateFontIndirect(&lf);
  120. // get the font heights
  121.   hOldFont=SelectObject(hDC,m_hFont);
  122.   GetTextMetrics(hDC,&tm);
  123.   m_FontHeight=tm.tmHeight;
  124.   SelectObject(hDC,m_hBoldFont);
  125.   GetTextMetrics(hDC,&tm);
  126.   m_BoldFontHeight=tm.tmHeight;
  127.   SelectObject(hDC,hOldFont);
  128. // finished with the DC
  129.   DeleteDC(hDC);
  130. // create pens and brushes
  131.   m_hBorderPen=CreatePen(PS_SOLID,1,m_Colors[colorBorder]);
  132.   m_hBackBrush=CreateSolidBrush(m_Colors[colorBackground]);
  133.   m_hSelectedBrush=CreateSolidBrush(m_Colors[colorHighlight]);
  134.   m_hLightShadowPen=CreatePen(PS_SOLID,1,m_Colors[colorLightShadow]);
  135.   m_hDarkShadowPen=CreatePen(PS_SOLID,1,m_Colors[colorDarkShadow]);
  136.   m_hBackPen=CreatePen(PS_SOLID,1,m_Colors[colorBackground]);
  137.   m_hTextPen=CreatePen(PS_SOLID,1,m_Colors[colorText]);
  138.   m_hSelectedTextPen=CreatePen(PS_SOLID,1,m_Colors[colorHighlightText]);
  139. // create the bitmap
  140.   if(m_BitmapID!=(UINT)-1)
  141.     m_hBitmap=LoadBitmap(m_hInstance,MAKEINTRESOURCE(m_BitmapID));
  142. }
  143. /*************/
  144. /* Create it */
  145. /*************/
  146. bool CFlatPopupMenu::Create(HINSTANCE hInstance,const UINT bitmap_id)
  147. {
  148. // save variables
  149.   m_hInstance=hInstance;
  150.   m_BitmapID=bitmap_id;
  151. // register the class
  152.   if(!m_bClassRegistered)
  153.   {
  154.     if(!RegisterClass())
  155.       return false;
  156.     m_bClassRegistered=true;
  157.   }
  158. // remove any previous items
  159.   m_Items.clear();
  160. // create GDI objects
  161.   CreateObjects();
  162.   return true;
  163. }
  164. /*****************************/
  165. /* Register the window class */
  166. /*****************************/
  167. bool CFlatPopupMenu::RegisterClass(void)
  168. {
  169.   WNDCLASS wc;
  170.   wc.style=0;
  171.   wc.lpfnWndProc=stubWindowProc;
  172.   wc.cbClsExtra=0;
  173.   wc.cbWndExtra=0;
  174.   wc.hInstance=m_hInstance;
  175.   wc.hIcon=NULL;
  176.   wc.hCursor=LoadCursor(NULL,IDC_ARROW);
  177.   wc.hbrBackground=NULL;
  178.   wc.lpszMenuName=NULL;
  179.   wc.lpszClassName="fpmMenuClass";
  180.   return ::RegisterClass(&wc)!=FALSE;
  181. }
  182. /**********************/
  183. /* Append a menu item */
  184. /**********************/
  185. bool CFlatPopupMenu::AppendItem(const DWORD dwFlags,LPCTSTR pszName,const UINT itemid,const int icon)
  186. {
  187.   CItem item;
  188.   item.m_dwFlags=dwFlags;
  189.   if(pszName)
  190.     item.m_strName=pszName;
  191.   item.m_ItemID=itemid;
  192.   item.m_IconIndex=icon;
  193.   item.m_pPopup=NULL;
  194.   m_Items.push_back(item);
  195.   return true;
  196. }
  197. /******************/
  198. /* Append a popup */
  199. /******************/
  200. bool CFlatPopupMenu::AppendPopup(const DWORD dwFlags,LPCTSTR pszName,CFlatPopupMenu& popup,const int icon)
  201. {
  202.   CItem item;
  203. // the separator flag is not allowed
  204.   if((dwFlags & itemSeparator)!=0)
  205.     return false;
  206. // add the item
  207.   item.m_dwFlags=dwFlags;
  208.   item.m_strName=pszName;
  209.   item.m_ItemID=0;
  210.   item.m_IconIndex=icon;
  211.   item.m_pPopup=&popup;
  212.   popup.m_bChild=true;
  213.   popup.m_pPrevious=this;
  214.   m_Items.push_back(item);
  215.   return true;
  216. }
  217. /******************/
  218. /* Track the menu */
  219. /******************/
  220. UINT CFlatPopupMenu::Track(int x,int y,HWND hWnd,const bool bModal,const bool bPopup)
  221. {
  222.   HDC hDC;
  223.   std::vector<CItem>::iterator it;
  224.   SIZE size;
  225.   int maxw,top;
  226.   HGDIOBJ hOldFont;
  227.   MSG msg;
  228.   RECT rcScreen;
  229. // get an information context for the screen
  230.   hDC=CreateIC("DISPLAY",NULL,NULL,NULL);
  231. // get the greatest item width
  232.   maxw=0;
  233.   top=3;
  234.   for(it=m_Items.begin();it!=m_Items.end();it++)
  235.   {
  236.     it->m_Top=top;
  237.     if(!(it->m_dwFlags & itemSeparator))
  238.     {
  239.       hOldFont=SelectObject(hDC,(it->m_dwFlags & itemBold)==0 ? m_hFont : m_hBoldFont);
  240.       GetTextExtentPoint32(hDC,it->m_strName.c_str(),it->m_strName.length(),&size);
  241.       maxw=max(size.cx,maxw);
  242.       SelectObject(hDC,hOldFont);
  243.       top+=17;
  244.       it->m_Height=17;
  245.     }
  246.     else
  247.     {
  248.       top+=5;
  249.       it->m_Height=5;
  250.     }
  251.   }
  252. // finished with the DC
  253.   DeleteDC(hDC);
  254. // get the screen dimensions and adjust x and y if necessary
  255.   m_Width=1+2+18+2+maxw+18+1;
  256.   m_SelectedItem=-1;
  257.   m_SelectedID=0;
  258.   SystemParametersInfo(SPI_GETWORKAREA,0,&rcScreen,0);
  259.   
  260.   if(x+m_Width>rcScreen.right)
  261.     x=max(0,rcScreen.right-m_Width);
  262.   if(y+top+2>rcScreen.bottom)
  263.     y=max(0,rcScreen.bottom-(top+2));
  264. // create the window
  265.   m_hWnd=::CreateWindowEx(WS_EX_TOOLWINDOW,         // prevent button appearing on taskbar
  266.                           "fpmMenuClass",
  267.                           "",
  268.                           WS_POPUP | WS_VISIBLE,
  269.                           x,
  270.                           y,
  271.                           m_Width,
  272.                           top+2,
  273.                           NULL,
  274.                           NULL,
  275.                           m_hInstance,
  276.                           (LPVOID)this);
  277. // check created OK
  278.   if(m_hWnd==NULL)
  279.     return false;
  280.   if(bPopup)
  281.   {
  282.   // already in the menu, no need to wait for any buttons
  283.     m_bWaitLeftButton=false;
  284.     m_bWaitRightButton=false;
  285.     m_State=stateShow;
  286.   }
  287.   else
  288.   {
  289.   // enter tracking state
  290.     SetCapture(m_hWnd);
  291.     m_State=stateTrack;
  292.   // we wait for any already-down buttons to come up
  293.     m_bWaitLeftButton=(GetKeyState(VK_LBUTTON) & 0x8000)!=0;
  294.     m_bWaitRightButton=(GetKeyState(VK_RBUTTON) & 0x8000)!=0;
  295.   }
  296. // remember modal setting and command window handle
  297.   m_bModal=bModal;
  298.   m_hWndCommand=hWnd;
  299.   if(m_bModal)
  300.   {
  301.   // go into a modal loop
  302.     while(GetMessage(&msg,NULL,0,0)==TRUE && IsWindow(m_hWnd))
  303.     {
  304.       TranslateMessage(&msg);
  305.       DispatchMessage(&msg);
  306.     }
  307.     return m_SelectedID;
  308.   }
  309.   return 0;
  310. }
  311. /*************************/
  312. /* stub window procedure */
  313. /*************************/
  314. static LRESULT CALLBACK stubWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
  315. {
  316.   CFlatPopupMenu *pClass;
  317. // the usual window procedure...
  318.   switch(uMsg)
  319.   {
  320.     case WM_CREATE:
  321.       SetWindowLong(hWnd,GWL_USERDATA,(LONG)((LPCREATESTRUCT)lParam)->lpCreateParams);
  322.       break;
  323.   
  324.     case WM_ERASEBKGND:
  325.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  326.       pClass->OnEraseBkgnd(hWnd,(HDC)wParam);
  327.       return TRUE;
  328.   
  329.     case WM_LBUTTONDOWN:
  330.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  331.       pClass->OnLButtonDown(hWnd,LOWORD(lParam),HIWORD(lParam));
  332.       break;
  333.     case WM_RBUTTONDOWN:
  334.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  335.       pClass->OnLButtonDown(hWnd,LOWORD(lParam),HIWORD(lParam));
  336.       break;
  337.     case WM_LBUTTONUP:
  338.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  339.       pClass->OnLButtonUp(hWnd,LOWORD(lParam),HIWORD(lParam));
  340.       break;
  341.     case WM_RBUTTONUP:
  342.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  343.       pClass->OnRButtonUp(hWnd,LOWORD(lParam),HIWORD(lParam));
  344.       break;
  345.     case WM_MOUSEMOVE:
  346.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  347.       pClass->OnMouseMove(hWnd,LOWORD(lParam),HIWORD(lParam));
  348.       break;
  349.     case WM_TIMER:
  350.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  351.       pClass->OnTimer(hWnd,wParam);
  352.       break;
  353.     case WM_CHAR:
  354.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  355.       pClass->OnChar(hWnd,(char)wParam);
  356.       break;
  357.     case WM_PAINT:
  358.       {
  359.         PAINTSTRUCT ps;
  360.         
  361.         BeginPaint(hWnd,&ps);
  362.         pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  363.         pClass->OnPaint(hWnd,ps.hdc);
  364.         EndPaint(hWnd,&ps);
  365.       }
  366.       break;
  367.     case WM_DESTROY:
  368.       pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hWnd,GWL_USERDATA));
  369.       pClass->OnDestroy(hWnd);
  370.       break;
  371.   }
  372.   return DefWindowProc(hWnd,uMsg,wParam,lParam);
  373. }
  374. /***********/
  375. /* WM_CHAR */
  376. /***********/
  377. void CFlatPopupMenu::OnChar(HWND hWnd,TCHAR c)
  378. {
  379.   std::vector<CItem>::const_iterator it;
  380.   std::string::size_type pos;
  381. // convert to lower case
  382.   c=(TCHAR)CharLower((LPTSTR)c);
  383. // search the menu items
  384.   for(it=m_Items.begin();it!=m_Items.end();it++)
  385.   {
  386.     if((it->m_dwFlags & (itemSeparator | itemGrayed | itemNotSelectable))==0)
  387.     {
  388.       if((pos=it->m_strName.find('&'))!=std::string::npos && it->m_strName.length()>pos+1 && c==(TCHAR)CharLower((LPTSTR)it->m_strName[pos+1]))
  389.       {
  390.       // set return value and exit
  391.         SetReturn(it->m_ItemID);
  392.         return;
  393.       }
  394.     }
  395.   }
  396. }
  397. /************/
  398. /* WM_TIMER */
  399. /************/
  400. void CFlatPopupMenu::OnTimer(HWND hWnd,unsigned short timerid)
  401. {
  402.   POINT p;
  403. // stop the timer
  404.   KillTimer(hWnd,timerid);
  405. // show the popup menu
  406.   if(m_SelectedItem!=-1 && m_Items[m_SelectedItem].m_pPopup)
  407.   {
  408.   // get the display position
  409.     p.x=m_Width-5;
  410.     p.y=m_Items[m_SelectedItem].m_Top-3;
  411.     ClientToScreen(hWnd,&p);
  412.   // track it
  413.     if(m_Items[m_SelectedItem].m_pPopup->Track(p.x,p.y,NULL,true,true))
  414.       DestroyAll();
  415.   }
  416. }
  417. /**********************/
  418. /* Draw a single item */
  419. /**********************/
  420. void CFlatPopupMenu::DrawItem(HWND hWnd,HDC hDC,const int index,const CItem& item)
  421. {
  422.   bool    bSelected=index==m_SelectedItem;
  423.   HGDIOBJ hOldPen,hOldBrush,hOldFont;
  424.   POINT   p[3];
  425.   int     i,x,y,h,nColor;
  426.   RECT    rect;
  427. // test if a separator
  428.   if((item.m_dwFlags & itemSeparator)!=0)
  429.   {
  430.     DrawSeparator(hDC,item.m_Top);
  431.     return;
  432.   }
  433. // get the top left of the item area
  434.   x=3;
  435.   y=item.m_Top;
  436. // is there an icon?
  437.   if(item.m_IconIndex!=-1)
  438.   {
  439.   // draw or erase the frame
  440.     p[0].x=p[1].x=x;
  441.     p[0].y=y+16;
  442.     p[1].y=p[2].y=item.m_Top;
  443.     p[2].x=x+17;
  444.     hOldPen=SelectObject(hDC,bSelected ? m_hLightShadowPen : m_hBackPen);
  445.     Polyline(hDC,p,3);
  446.     p[0].x=p[1].x=x+17;
  447.     p[0].y=item.m_Top;
  448.     p[1].y=p[2].y=y+16;
  449.     p[2].x=x;
  450.     SelectObject(hDC,bSelected ? m_hDarkShadowPen : m_hBackPen);
  451.     Polyline(hDC,p,3);
  452.     SelectObject(hDC,hOldPen);
  453.   // draw the bitmap
  454.     DrawTransparentBitmap(hDC,item.m_IconIndex*16,x+1,item.m_Top+1);
  455.   // update co-ordinate
  456.     x+=18;
  457.   }
  458. // draw/clear the selection rectangle
  459.   hOldBrush=SelectObject(hDC,bSelected ? m_hSelectedBrush : m_hBackBrush);
  460.   hOldPen=SelectObject(hDC,GetStockObject(NULL_PEN));
  461.   Rectangle(hDC,x,item.m_Top,m_Width-2,item.m_Top+18);
  462.   SelectObject(hDC,hOldPen);
  463.   SelectObject(hDC,hOldBrush);
  464. // draw the item text
  465.   if((item.m_dwFlags & itemBold)!=0)
  466.   {
  467.     h=m_BoldFontHeight;
  468.     hOldFont=SelectObject(hDC,m_hBoldFont);
  469.   }
  470.   else
  471.   {
  472.     h=m_FontHeight;
  473.     hOldFont=SelectObject(hDC,m_hFont);
  474.   }
  475.   SetBkMode(hDC,TRANSPARENT);
  476.   if(bSelected)
  477.     nColor=colorHighlightText;
  478.   else
  479.   {
  480.     if((item.m_dwFlags & itemGrayed)!=0)
  481.       nColor=colorGrayedText;
  482.     else
  483.       nColor=colorText;
  484.   }
  485.   rect.left=1+2+18+2;
  486.   rect.top=item.m_Top+((17-h)/2);
  487.   rect.bottom=rect.top+1000;
  488.   rect.right=rect.left+1000;
  489.   SetTextColor(hDC,m_Colors[nColor]);
  490.   DrawText(hDC,item.m_strName.c_str(),item.m_strName.length(),&rect,DT_LEFT | DT_TOP | DT_NOCLIP);
  491.   SelectObject(hDC,hOldFont);
  492. // draw a subitem arrow
  493.   if(item.m_pPopup)
  494.   {
  495.     hOldPen=SelectObject(hDC,bSelected ? m_hSelectedTextPen : m_hTextPen);
  496.     p[0].x=p[1].x=m_Width-7;
  497.     p[0].y=item.m_Top+8;
  498.     p[1].y=item.m_Top+9;
  499.     for(i=0;i<4;i++)
  500.     {
  501.       Polyline(hDC,p,2);
  502.       p[0].x--;
  503.       p[1].x--;
  504.       p[0].y--;
  505.       p[1].y++;
  506.     }
  507.     SelectObject(hDC,hOldPen);
  508.   }
  509. }
  510. /********************/
  511. /* Draw a separator */
  512. /********************/
  513. void CFlatPopupMenu::DrawSeparator(HDC hDC,int top)
  514. {
  515.   POINT   p[2];
  516.   HGDIOBJ hOldPen;
  517.   p[0].x=0;
  518.   p[1].x=m_Width;
  519.   p[0].y=p[1].y=top+2;
  520.   hOldPen=SelectObject(hDC,m_hBorderPen);
  521.   Polyline(hDC,p,2);
  522.   SelectObject(hDC,hOldPen);
  523. }
  524. /**************/
  525. /* WM_DESTROY */
  526. /**************/
  527. void CFlatPopupMenu::OnDestroy(HWND hWnd)
  528. {
  529.   std::vector<CItem>::const_iterator it;
  530. // any active popups must also be destroyed
  531.   for(it=m_Items.begin();it!=m_Items.end();it++)
  532.     if(it->m_pPopup && it->m_pPopup->m_State!=stateInactive)
  533.       DestroyWindow(it->m_pPopup->m_hWnd);
  534.   if(m_State==stateTrack)
  535.     ReleaseCapture();
  536.   m_State=stateInactive;
  537.   m_hWnd=NULL;
  538.   if(!m_bModal && m_SelectedID && !m_bChild)
  539.     PostMessage(m_hWndCommand,WM_COMMAND,m_SelectedID,0);
  540. }
  541. /****************/
  542. /* WM_MOUSEMOVE */
  543. /****************/
  544. void CFlatPopupMenu::OnMouseMove(HWND hWnd,short x,short y)
  545. {
  546.   RECT            rect;
  547.   HDC             hDC;
  548.   HWND            hPointWnd;
  549.   int             item,newitem;
  550.   POINT           p;
  551.   TCHAR           szClass[100];
  552.   CFlatPopupMenu *pClass;
  553. // get a DC and the window rectangle
  554.   GetClientRect(hWnd,&rect);
  555.   hDC=GetDC(hWnd);
  556. // shift tracking if we're over another menu
  557.   p.x=x;
  558.   p.y=y;
  559.   ClientToScreen(hWnd,&p);
  560.   if((hPointWnd=WindowFromPoint(p))!=NULL && GetClassName(hPointWnd,szClass,sizeof(szClass)/sizeof(TCHAR)) && !lstrcmp(szClass,"fpmMenuClass"))
  561.   {
  562.     pClass=reinterpret_cast<CFlatPopupMenu *>(GetWindowLong(hPointWnd,GWL_USERDATA));
  563.     if(pClass!=this)
  564.     {
  565.       ReleaseCapture();
  566.       m_State=stateShow;
  567.       SetCapture(pClass->m_hWnd);
  568.       pClass->m_State=stateTrack;
  569.       return;
  570.     }
  571.   }
  572. // get new selected item, if any
  573.   newitem=GetItem(x,y,rect);
  574. // remove previous selection?
  575.   if(m_SelectedItem!=-1 && newitem!=m_SelectedItem)
  576.   {
  577.   // kill any ongoing timer
  578.     KillTimer(m_hWnd,1);
  579.   // remove any popups associated with the last active item
  580.     if(m_Items[m_SelectedItem].m_pPopup && m_Items[m_SelectedItem].m_pPopup->m_State!=stateInactive)
  581.       DestroyWindow(m_Items[m_SelectedItem].m_pPopup->m_hWnd);
  582.   // deselect the old item
  583.     item=m_SelectedItem;
  584.     m_SelectedItem=-1;
  585.     DrawItem(hWnd,hDC,item,m_Items[item]);
  586.   }
  587. // new selected item?
  588.   if(newitem!=-1 && m_SelectedItem!=newitem)
  589.   {
  590.   // draw the new item
  591.     m_SelectedItem=newitem;
  592.     DrawItem(hWnd,hDC,m_SelectedItem,m_Items[m_SelectedItem]);
  593.   // start a timer if this is a popup
  594.     if(m_Items[m_SelectedItem].m_pPopup)
  595.       SetTimer(m_hWnd,1,m_PopupDelay,NULL);
  596.   }
  597. }
  598. /************************/
  599. /* Get item at position */
  600. /************************/
  601. int CFlatPopupMenu::GetItem(short x,short y,const RECT& rect)
  602. {
  603.   int newitem;
  604.   std::vector<CItem>::size_type i;
  605.   if(x<=2 || y<=2 || x>rect.right || y>rect.bottom)
  606.     newitem=-1;
  607.   else
  608.   {
  609.     for(i=0;i<m_Items.size();i++)
  610.       if(y>m_Items[i].m_Top && y<m_Items[i].m_Top+m_Items[i].m_Height)
  611.         break;
  612.     newitem=i==m_Items.size() ? -1 : i;
  613.     if(newitem!=-1 && (m_Items[newitem].m_dwFlags & itemNotSelectable)!=0)
  614.       newitem=-1;
  615.   }
  616.   return newitem;
  617. }
  618. /********************/
  619. /* a button is down */
  620. /********************/
  621. void CFlatPopupMenu::ButtonDown(HWND hWnd,short x,short y,const bool bLeft)
  622. {
  623.   RECT rect;
  624. // check for click outside menu
  625.   GetClientRect(hWnd,&rect);
  626.   if(CheckOutsideMenu(hWnd,rect,x,y))
  627.     return;
  628. // check for click on popup
  629.   if(m_SelectedItem!=-1 && m_Items[m_SelectedItem].m_pPopup && m_Items[m_SelectedItem].m_pPopup->m_State==stateInactive)
  630.     OnTimer(hWnd,1);
  631. }
  632. /****************/
  633. /* WM_LBUTTONUP */
  634. /****************/
  635. void CFlatPopupMenu::OnLButtonUp(HWND hWnd,short x,short y)
  636. {
  637.   RECT rect;
  638.   if(m_bWaitLeftButton)
  639.   {
  640.     m_bWaitLeftButton=false;
  641.     return;
  642.   }
  643.   GetClientRect(hWnd,&rect);
  644.   if(CheckOutsideMenu(hWnd,rect,x,y))
  645.     return;
  646.   if(m_SelectedItem!=-1 && m_Items[m_SelectedItem].m_ItemID)
  647.     SetReturn(m_Items[m_SelectedItem].m_ItemID);
  648. }
  649. /****************/
  650. /* WM_RBUTTONUP */
  651. /****************/
  652. void CFlatPopupMenu::OnRButtonUp(HWND hWnd,short x,short y)
  653. {
  654.   RECT rect;
  655.   if(m_bWaitRightButton)
  656.   {
  657.     m_bWaitRightButton=false;
  658.     return;
  659.   }
  660.   GetClientRect(hWnd,&rect);
  661.   if(CheckOutsideMenu(hWnd,rect,x,y))
  662.     return;
  663.   if(m_SelectedItem!=-1 && m_Items[m_SelectedItem].m_ItemID)
  664.     SetReturn(m_Items[m_SelectedItem].m_ItemID);
  665. }
  666. /************************/
  667. /* Set the return value */
  668. /************************/
  669. void CFlatPopupMenu::SetReturn(const UINT id)
  670. {
  671.   CFlatPopupMenu *pMenu;
  672. // find the root
  673.   for(pMenu=this;pMenu->m_pPrevious;pMenu=pMenu->m_pPrevious);
  674.   pMenu->m_SelectedID=id;
  675.   DestroyAll();
  676. }
  677. /*************************/
  678. /* Check if outside menu */
  679. /*************************/
  680. bool CFlatPopupMenu::CheckOutsideMenu(HWND hWnd,const RECT& rect,short x,short y)
  681. {
  682.   if(x<=2 || y<=2 || x>rect.right || y>rect.bottom)
  683.   {
  684.   // clicked outside the window
  685.     m_SelectedItem=-1;
  686.     m_SelectedID=0;
  687.     DestroyAll();
  688.     return true;
  689.   }
  690.   return false;
  691. }
  692. /*****************/
  693. /* WM_ERASEBKGND */
  694. /*****************/
  695. void CFlatPopupMenu::OnEraseBkgnd(HWND hWnd,HDC hDC)
  696. {
  697.   RECT    rect;
  698.   HGDIOBJ hOldBrush,hOldPen;
  699.   GetClientRect(hWnd,&rect);
  700.   hOldBrush=SelectObject(hDC,m_hBackBrush);
  701.   hOldPen=SelectObject(hDC,m_hBorderPen);
  702.   
  703.   Rectangle(hDC,rect.left,rect.top,rect.right,rect.bottom);
  704.   SelectObject(hDC,hOldBrush);
  705.   SelectObject(hDC,hOldPen);
  706. }
  707. /***************************/
  708. /* Assign item to src item */
  709. /***************************/
  710. void CFlatPopupMenu::CItem::Assign(const CItem& src)
  711. {
  712.   m_dwFlags=src.m_dwFlags;
  713.   m_strName=src.m_strName;
  714.   m_ItemID=src.m_ItemID;
  715.   m_IconIndex=src.m_IconIndex;
  716.   m_Top=src.m_Top;
  717.   m_Height=src.m_Height;
  718.   m_pPopup=src.m_pPopup;
  719. }
  720. /*****************************/
  721. /* Draw a transparent bitmap */
  722. /*****************************
  723. This function is borrowed and slightly modified from an MSDN sample */
  724. void CFlatPopupMenu::DrawTransparentBitmap(HDC hdc,const int xSrcOffset,const int xStart,const int yStart)
  725. {
  726.   BITMAP    bm;
  727.   COLORREF  cColor;
  728.   HBITMAP   bmAndBack,bmAndObject,bmAndMem,bmSave;
  729.   HBITMAP   bmBackOld,bmObjectOld,bmMemOld,bmSaveOld;
  730.   HDC       hdcMem,hdcBack,hdcObject,hdcTemp,hdcSave;
  731.   POINT     ptSize;
  732.   hdcTemp=CreateCompatibleDC(hdc);
  733.   SelectObject(hdcTemp,m_hBitmap);      // Select the bitmap
  734.   GetObject(m_hBitmap,sizeof(BITMAP),(LPSTR)&bm);
  735.   ptSize.x=bm.bmWidth;                // Get width of bitmap
  736.   ptSize.y=bm.bmHeight;               // Get height of bitmap
  737.   DPtoLP(hdcTemp,&ptSize,1);          // Convert from device
  738.                                       // to logical points
  739. // Create some DCs to hold temporary data
  740.   hdcBack=CreateCompatibleDC(hdc);
  741.   hdcObject=CreateCompatibleDC(hdc);
  742.   hdcMem=CreateCompatibleDC(hdc);
  743.   hdcSave=CreateCompatibleDC(hdc);
  744. // Create a bitmap for each DC. DCs are required for a number of GDI functions
  745. // Monochrome DC
  746.   bmAndBack=CreateBitmap(ptSize.x,ptSize.y,1,1,NULL);
  747. // Monochrome DC
  748.   bmAndObject=CreateBitmap(ptSize.x,ptSize.y,1,1,NULL);
  749.   bmAndMem=CreateCompatibleBitmap(hdc,ptSize.x,ptSize.y);
  750.   bmSave=CreateCompatibleBitmap(hdc,ptSize.x,ptSize.y);
  751. // Each DC must select a bitmap object to store pixel data
  752.   bmBackOld=(HBITMAP)SelectObject(hdcBack,bmAndBack);
  753.   bmObjectOld=(HBITMAP)SelectObject(hdcObject,bmAndObject);
  754.   bmMemOld=(HBITMAP)SelectObject(hdcMem,bmAndMem);
  755.   bmSaveOld=(HBITMAP)SelectObject(hdcSave,bmSave);
  756. // Set proper mapping mode
  757.   SetMapMode(hdcTemp,GetMapMode(hdc));
  758. // Save the bitmap sent here, because it will be overwritten.
  759.   BitBlt(hdcSave,0,0,ptSize.x,ptSize.y,hdcTemp,0,0,SRCCOPY);
  760. // Set the background color of the source DC to the color.
  761. // contained in the parts of the bitmap that should be transparent
  762.   cColor=SetBkColor(hdcTemp,m_Colors[colorIconTransparent]);
  763. // Create the object mask for the bitmap by performing a BitBlt
  764. // from the source bitmap to a monochrome bitmap.
  765.   BitBlt(hdcObject,0,0,ptSize.x,ptSize.y,hdcTemp,0,0,SRCCOPY);
  766. // Set the background color of the source DC back to the original color
  767.   SetBkColor(hdcTemp,cColor);
  768. // Create the inverse of the object mask
  769.   BitBlt(hdcBack,0,0,ptSize.x,ptSize.y,hdcObject,0,0,NOTSRCCOPY);
  770. // Copy the background of the main DC to the destination
  771.   BitBlt(hdcMem,xSrcOffset,0,16,15,hdc,xStart,yStart,SRCCOPY);
  772. // Mask out the places where the bitmap will be placed
  773.   BitBlt(hdcMem,0,0,ptSize.x,ptSize.y,hdcObject,0,0,SRCAND);
  774. // Mask out the transparent colored pixels on the bitmap
  775.   BitBlt(hdcTemp,0,0,ptSize.x,ptSize.y,hdcBack,0,0,SRCAND);
  776. // XOR the bitmap with the background on the destination DC
  777.   BitBlt(hdcMem,0,0,ptSize.x,ptSize.y,hdcTemp,0,0,SRCPAINT);
  778. // Copy the destination to the screen
  779.   BitBlt(hdc,xStart,yStart,16,15,hdcMem,xSrcOffset,0,SRCCOPY);
  780. // Place the original bitmap back into the bitmap sent here
  781.   BitBlt(hdcTemp,0,0,ptSize.x,ptSize.y,hdcSave,0,0,SRCCOPY);
  782. // Delete the memory bitmaps
  783.   DeleteObject(SelectObject(hdcBack,bmBackOld));
  784.   DeleteObject(SelectObject(hdcObject,bmObjectOld));
  785.   DeleteObject(SelectObject(hdcMem,bmMemOld));
  786.   DeleteObject(SelectObject(hdcSave,bmSaveOld));
  787. // Delete the memory DCs
  788.   
  789.   DeleteDC(hdcMem);
  790.   DeleteDC(hdcBack);
  791.   DeleteDC(hdcObject);
  792.   DeleteDC(hdcSave);
  793.   DeleteDC(hdcTemp);
  794. }
  795. /************************/
  796. /* Change the menu text */
  797. /************************/
  798. bool CFlatPopupMenu::SetMenuItemText(const UINT itemid,LPCTSTR pszName,const bool bByPosition)
  799. {
  800.   CItem *pItem;
  801. // get the item
  802.   if((pItem=GetItem(itemid,bByPosition))==NULL)
  803.     return false;
  804. // adjust the name
  805.   pItem->m_strName=pszName;
  806.   return true;
  807. }
  808. /*************************/
  809. /* Change the menu flags */
  810. /*************************/
  811. bool CFlatPopupMenu::SetMenuItemFlags(const UINT itemid,const DWORD dwFlags,const bool bByPosition)
  812. {
  813.   CItem *pItem;
  814. // get the item
  815.   if((pItem=GetItem(itemid,bByPosition))==NULL)
  816.     return false;
  817. // adjust the flags
  818.   pItem->m_dwFlags=dwFlags;
  819.   return true;
  820. }
  821. /**************************/
  822. /* Get a menu item's text */
  823. /**************************/
  824. bool CFlatPopupMenu::GetString(const UINT itemid,LPTSTR pszText,const UINT cchText,const bool bByPosition)
  825. {
  826.   CItem *pItem;
  827. // get the item
  828.   if((pItem=GetItem(itemid,bByPosition))==NULL)
  829.     return false;
  830.   if(pItem->m_strName.length()+1>cchText)
  831.     return false;
  832.   lstrcpy(pszText,pItem->m_strName.c_str());
  833.   return true;
  834. }
  835. /*************************/
  836. /* Change the menu flags */
  837. /*************************/
  838. bool CFlatPopupMenu::SetMenuItemIcon(const UINT itemid,const int icon,const bool bByPosition)
  839. {
  840.   CItem *pItem;
  841. // get the item
  842.   if((pItem=GetItem(itemid,bByPosition))==NULL)
  843.     return false;
  844. // adjust the icon
  845.   pItem->m_IconIndex=icon;
  846.   return true;
  847. }
  848. /****************/
  849. /* Find an item */
  850. /****************/
  851. CFlatPopupMenu::CItem *CFlatPopupMenu::GetItem(const UINT itemid,const bool bByPosition)
  852. {
  853.   std::vector<CItem>::iterator it;
  854.   if(bByPosition)
  855.   {
  856.     if(itemid>=m_Items.size())
  857.       return false;
  858.     return &m_Items[itemid];
  859.   }
  860.   else
  861.   {
  862.     for(it=m_Items.begin();it!=m_Items.end();it++)
  863.       if(it->m_ItemID==itemid)
  864.         return &(*it);
  865.     return NULL;
  866.   }
  867. }