UICoolMenu.cpp
上传用户:yatsl7111
上传日期:2007-01-08
资源大小:1433k
文件大小:48k
源码类别:

图形图象

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////
  2. // CoolMenu 1997 Microsoft Systems Journal. 
  3. // If this code works, it was written by Paul DiLascia.
  4. // If not, I don't know who wrote it.
  5. //
  6. // ==========================================================================  
  7. // HISTORY:   
  8. // ==========================================================================
  9. //    1.01  13 Aug 1998 - Andrew Bancroft [ABancroft@lgc.com] - Since we've already 
  10. //                added the entire toolbar to the imagelist we need to 
  11. //                increment nNextImage even if we didn't add this button to 
  12. //                m_mapIDtoImage in the LoadToolbar() method.
  13. //    1.01a 13 Aug 1998 - Peter Tewkesbury - Added AddSingleBitmap(...)
  14. //                method for adding a single bitmap to a pulldown
  15. //                menu item.
  16. //    1.02  13 Aug 1998 - Omar L Francisco - Fixed bug with lpds->CtlType
  17. //                and lpds->itemData item checking.
  18. //    1.03  12 Nov 1998 - Fixes debug assert in system menu. - Wang Jun
  19. //    1.04  17 Nov 1998 - Fixes debug assert when you maximize a view - Wang Jun
  20. //                window, then try to use the system menu for the view.
  21. //    1.05  09 Jan 1998 - Seain B. Conover [sc@tarasoft.com] - Fix for virtual 
  22. //                key names.
  23. //    1.06  24 Feb 1999 - Michael Lange [michael.home@topdogg.com] - Fix for memory 
  24. //                leak in CMyItemData structure, added a destructor that 
  25. //                calls text.Empty().
  26. //              - Boris Kartamishev [kbv@omegasoftware.com] - Fix for resource
  27. //                ID bug.
  28. //              - Jeremy Horgan [jeremyhorgan@hotmail.com] - During 
  29. //                accelerator key processing OnInitMenuPopup() calls 
  30. //                ConvertMenu() which allocates a new CMyItemData for each 
  31. //                  menu item. This is memory is normally freed by a call to 
  32. //                  OnMenuSelect(), which is not called when processing 
  33. //                accelerator keys. This results in a memory leak. This was
  34. //                fixed by modifying the ~CCoolMenuManager() destructor.
  35. //    1.07  24 Feb 1999 - Koji MATSUNAMI [kmatsu@inse.co.jp] - Fixed problem with 
  36. //                popup menus being drawn correctly as cool menus.
  37. //    1.08  7 Jul 2000 - Philip Oldaker [philip@masmex.com] - Added support for
  38. // IContextMenu2 as used by the windows shell for owner draw menus
  39. // ==========================================================================
  40. //
  41. /////////////////////////////////////////////////////////////////////////////
  42. #include "StdAfx.h"
  43. #include "UICoolMenu.h"
  44. #include <afxpriv.h>
  45. #ifdef _DEBUG
  46. #define new DEBUG_NEW
  47. #undef THIS_FILE
  48. static char THIS_FILE[] = __FILE__;
  49. #endif
  50. #define safe_delete(p){if(p){delete p;p=NULL;}}
  51. #define USES_ICON  -2
  52. CCoolMenuManager g_CoolMenuManager;
  53. // helpers
  54. void PLFillRect(CDC& dc, const CRect& rc, COLORREF color);
  55. void PLDrawEmbossed(CDC& dc, CImageList& il, int i,
  56.   CPoint p, BOOL bColor=FALSE);
  57. HBITMAP PLLoadSysColorBitmap(LPCTSTR lpResName, BOOL bMono=FALSE);
  58. inline HBITMAP PLLoadSysColorBitmap(UINT nResID, BOOL bMono=FALSE) {
  59.   return PLLoadSysColorBitmap(MAKEINTRESOURCE(nResID), bMono);
  60. }
  61. // if you want to see extra TRACE diagnostics, set below to TRUE
  62. BOOL CCoolMenuManager::bTRACE = FALSE;
  63. #ifdef _DEBUG
  64. #define CMTRACEFN     
  65.   CTraceFn __fooble;  
  66.   if (bTRACE)       
  67.     TRACE
  68. #define CMTRACE     
  69.   if (bTRACE)       
  70.     TRACE
  71. #else
  72. #define CMTRACEFN TRACE
  73. #define CMTRACE   TRACE
  74. #endif
  75. // constants used for drawing
  76. const CXGAP = 1;        // num pixels between button and text
  77. const CXTEXTMARGIN = 2;   // num pixels after hilite to start text
  78. const CXBUTTONMARGIN = 2; // num pixels wider button is than bitmap
  79. const CYBUTTONMARGIN = 2; // ditto for height
  80. // DrawText flags
  81. const DT_MYSTANDARD = DT_SINGLELINE|DT_LEFT|DT_VCENTER;
  82. // identifies owner-draw data as mine
  83. const LONG MYITEMID = MAKELONG(MAKEWORD(_T('m'),_T('i')),MAKEWORD(_T('d'),_T('0')));
  84. // private struct: one of these for each owner-draw menu item
  85. struct CMyItemData {
  86.     long    magicNum;   // magic number identifying me
  87.     CString text;       // item text
  88.     UINT    fType;      // original item type flags
  89.     int     iButton;    // index of button image in image list (or USES_ICON)
  90.     HICON   hIcon;
  91.     void *  pContext; // item data
  92. LPCONTEXTMENU lpcm; // if set this will handle menu messages (windows shell)
  93.     CMyItemData()       { lpcm=NULL; magicNum = MYITEMID; iButton = -1; hIcon = 0; pContext = NULL; }
  94.     ~CMyItemData()        { text.Empty(); if (hIcon) ::DestroyIcon(hIcon); if (lpcm) lpcm->Release(); }
  95.     BOOL IsMyItemData()   { return this != NULL  &&  magicNum == MYITEMID; }
  96. };
  97. static CMyItemData *GetMyItemData(HMENU hMenu, UINT uItem, 
  98.                                   BOOL bByPosition = FALSE)
  99. {
  100.     MENUITEMINFO mii;
  101. ZeroMemory(&mii,sizeof(mii));
  102.     mii.cbSize = sizeof(mii);
  103.     mii.fMask  = MIIM_DATA;
  104.     ::GetMenuItemInfo(hMenu, uItem, bByPosition, &mii);
  105.     return (CMyItemData *)mii.dwItemData;
  106. }
  107. static void SetMyItemData(CMyItemData *pmd, HMENU hMenu, UINT uItem, 
  108.                           BOOL bByPosition = FALSE)
  109. {
  110.     MENUITEMINFO mii;
  111.     mii.cbSize     = sizeof(mii);
  112.     mii.fMask      = MIIM_DATA;
  113.     mii.dwItemData = (DWORD)pmd;
  114.     ::SetMenuItemInfo(hMenu, uItem, bByPosition, &mii);
  115. }
  116. IMPLEMENT_DYNAMIC(CCoolMenuManager, CSubclassWnd)
  117. CCoolMenuManager::CCoolMenuManager()
  118. {
  119. m_lpcm = NULL;
  120. m_szBitmap = CSize(0,0); // will compute later
  121. m_szButton = CSize(0,0); 
  122. m_bShowButtons = TRUE;            // show buttons by default
  123. m_bAutoAccel = TRUE;              // auto accelerators by default
  124. m_pAccel = NULL;                // no accelerators loaded yet
  125. m_bUseDrawState = FALSE;          // use DrawEmbossed by default
  126. m_bDrawDisabledButtonsInColor = FALSE;  // use color for disabled buttons
  127. FixMFCDotBitmap();
  128. }
  129. CCoolMenuManager::~CCoolMenuManager()
  130. {
  131.   // Jeremy Horgan [jeremyhorgan@hotmail.com]
  132.   while (!m_menuList.IsEmpty()) 
  133.   {
  134.       UnconvertMenu(CMenu::FromHandle((HMENU)m_menuList.RemoveHead()));
  135.   }
  136.   
  137.   Destroy();
  138. }
  139. //////////////////
  140. // Destroy everything. Called from destructor and Refresh.
  141. //
  142. void CCoolMenuManager::Destroy()
  143. {
  144.   m_ilButtons.DeleteImageList();
  145.   m_ilTemp.DeleteImageList();
  146.   m_mapIDtoImage.RemoveAll();
  147.   m_mapHMENUtoID.RemoveAll();
  148.   m_szBitmap = m_szButton = CSize(0,0);
  149.   m_arToolbarID.RemoveAll();
  150.   m_fontMenu.DeleteObject();
  151.   DestroyAccel();
  152. }
  153. /////////////////
  154. // Destroy accelerators
  155. //
  156. void CCoolMenuManager::DestroyAccel()
  157. {
  158.   m_mapIDtoAccel.RemoveAll();   // delete ACCEL entries in map
  159.   safe_delete(m_pAccel);      // delete current accelerators
  160. }
  161. //////////////////
  162. // Call this to install the menu manager. Install(NULL) to un-install.
  163. //
  164. void CCoolMenuManager::Install(CWnd* pFrame)
  165. {
  166. m_pFrame = pFrame;
  167. HookWindow(pFrame);   // install message hook
  168. }
  169. void CCoolMenuManager::Uninstall()
  170. {
  171. m_pFrame = NULL;
  172. HookWindow((HWND)NULL); 
  173. }
  174. // Addition: Phliip Oldaker
  175. // call this to enable help text on the status bar
  176. void CCoolMenuManager::SetShellContextMenu(LPCONTEXTMENU lpcm,UINT nIDFirst,UINT nIDLast)
  177. {
  178. m_lpcm = lpcm;
  179. m_idShellMenuFirst = nIDFirst;
  180. m_idShellMenuLast = nIDLast;
  181. }
  182. //////////////////
  183. // Load array of toolbar IDs.
  184. //
  185. BOOL CCoolMenuManager::LoadToolbars(const UINT* arID, int n)
  186. {
  187.   ASSERT(arID);
  188.   BOOL bRet = TRUE;
  189.   for (int i=0; i<n; i++)
  190.     bRet |= LoadToolbar(arID[i]);
  191.   return bRet;
  192. }
  193. // structure of RT_TOOLBAR resource
  194. struct TOOLBARDATA {
  195.   WORD wVersion;    // version # should be 1
  196.   WORD wWidth;    // width of one bitmap
  197.   WORD wHeight;   // height of one bitmap
  198.   WORD wItemCount;  // number of items
  199.   WORD items[1];    // array of command IDs, actual size is wItemCount
  200. };
  201. // Addition: Philip Oldaker
  202. CSize CCoolMenuManager::SetButtonSize(CSize &sz)
  203. {
  204. // Set defaults
  205. if (sz.cx == 0)
  206. sz.cx = 16;
  207. if (sz.cy == 0)
  208. sz.cy = 15;
  209. CSize szOld(m_szBitmap);
  210. m_szBitmap = sz;
  211. m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
  212. return szOld;
  213. }
  214. //////////////////
  215. // Load one toolbar. Assumes bg color is gray.
  216. // 
  217. //  * add toolbar bitmap to image list
  218. //   * add each button ID to button map
  219. //
  220. BOOL CCoolMenuManager::LoadToolbar(UINT nIDToolbar)
  221. {
  222.   // load bitmap
  223.   HBITMAP hbmToolbar = PLLoadSysColorBitmap(nIDToolbar);
  224.   if (!hbmToolbar) {
  225.     TRACE(_T("*** Can't load bitmap for toolbar %d!n"), nIDToolbar);
  226.     return FALSE;
  227.   }
  228.   // load toolbar
  229.   LPTSTR lpResName = MAKEINTRESOURCE(nIDToolbar);
  230.   HINSTANCE hInst;
  231.   HRSRC hRsrc;
  232.   TOOLBARDATA* ptbd;
  233.   if ((hInst= AfxFindResourceHandle(lpResName, RT_TOOLBAR)) == NULL ||
  234.      (hRsrc= FindResource(hInst, lpResName, RT_TOOLBAR))   == NULL ||
  235.      (ptbd = (TOOLBARDATA*)LoadResource(hInst, hRsrc))     == NULL) {
  236.     TRACE(_T("*** Can't load toolbar %d!n"), nIDToolbar);
  237.     return FALSE;
  238.   }
  239.   ASSERT(ptbd->wVersion==1);
  240.     
  241.   // OK, I have the bitmap and toolbar. 
  242.   CSize sz(ptbd->wWidth, ptbd->wHeight);
  243.   if (m_szBitmap.cx==0) {
  244.     // First toolbar: initialized bitmap/button sizes and create image list.
  245.     m_szBitmap = sz;
  246.     m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
  247.     VERIFY(m_ilButtons.Create(sz.cx, sz.cy, ILC_COLOR32|ILC_MASK, 0, 10));
  248.     VERIFY(m_ilTemp.   Create(sz.cx, sz.cy, ILC_COLOR32|ILC_MASK, 0, 10));
  249.   } else if (m_szBitmap != sz) {
  250.     // button sizes different -- oops
  251.     TRACE(_T("*** Toolbar %d button size differs!n"), nIDToolbar);
  252.     return FALSE;
  253.   }
  254.   m_ilButtons.SetBkColor(::GetSysColor(COLOR_3DFACE));
  255.   // I have a good toolbar: now add bitmap to the image list, and each
  256.   // command ID to m_mapIDtoImage array. Note that LoadSysColorBitmap will
  257.   // change gray -> COLOR_3DFACE, so use that for image list background.
  258.   //
  259.   int iNextImage = m_ilButtons.GetImageCount();
  260.   m_ilButtons.Add(CBitmap::FromHandle(hbmToolbar), GetSysColor(COLOR_3DFACE));
  261.   for (int i = 0; i < ptbd->wItemCount; i++) {
  262.     UINT nID = ptbd->items[i];
  263.     if (nID > 0) {
  264.       if (GetButtonIndex(nID) >= 0) {
  265.         TRACE(_T("*** Duplicate button ID %d ignoredn"), nID);
  266.       } else {
  267.         m_mapIDtoImage.SetAt(nID, (void*)iNextImage);
  268.         TRACE(_T("CCoolMenuManager::LoadToolbar(). Added Menu Id %d, Button Number %dn"), nID, iNextImage-1);
  269.       }
  270.       // AB. 13-08-98. Since we've already added the entire toolbar to the imagelist
  271.       // we need to increment nNextImage even if we didn't add this button to
  272.       // m_mapIDtoImage.
  273.       iNextImage++;
  274.     }
  275.   }
  276.   m_arToolbarID.Add(nIDToolbar);  // remember toolbar ID for Refresh
  277.   ::DeleteObject(hbmToolbar);
  278.   return TRUE; // success!
  279. }
  280. //////////////////
  281. // Virtual CCoolMenuSubclassWnd window proc. All messages come here before frame
  282. // window. Isn't it cool? Just like in the old days!
  283. //
  284. LRESULT CCoolMenuManager::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  285. {
  286.   switch(msg) {
  287.       case WM_SYSCOLORCHANGE:
  288.       case WM_SETTINGCHANGE:
  289.         Refresh();
  290.         break;
  291.       case WM_MEASUREITEM:
  292.         if (OnMeasureItem((MEASUREITEMSTRUCT*)lp))
  293.           return TRUE; // handled
  294.         break;
  295.       case WM_DRAWITEM:
  296.         if (OnDrawItem((DRAWITEMSTRUCT*)lp))
  297.           return TRUE; // handled
  298.         break;
  299.       case WM_INITMENUPOPUP:
  300.         // Very important: must let frame window handle it first!
  301.         // Because if someone calls CCmdUI::SetText, MFC will change item to
  302.         // MFT_STRING, so I must change back to MFT_OWNERDRAW.
  303.         //
  304.         CSubclassWnd::WindowProc(msg, wp, lp);
  305.         OnInitMenuPopup(CMenu::FromHandle((HMENU)wp),
  306.           /*(UINT)LOWORD(lp)*/0, (BOOL)HIWORD(lp));
  307.         return 0;
  308.       case WM_MENUSELECT:
  309.         OnMenuSelect((UINT)LOWORD(wp), (UINT)HIWORD(wp), (HMENU)lp);
  310.         break;
  311.       case WM_MENUCHAR:
  312.         LRESULT lr = OnMenuChar((TCHAR)LOWORD(wp), (UINT)HIWORD(wp),
  313.           CMenu::FromHandle((HMENU)lp));
  314.         if (lr!=0)
  315.           return lr;
  316.         break;
  317.   }
  318.   return CSubclassWnd::WindowProc(msg, wp, lp);
  319. }
  320. //////////////////
  321. // Refresh all colors, fonts, etc. For WM_SETTINGCHANGE, WM_SYSCOLORCHANGE.
  322. //
  323. void CCoolMenuManager::Refresh()
  324. {
  325.   // first copy list (array) of toolbar IDs now loaded.
  326.   CUIntArray arToolbarID;
  327.   arToolbarID.Copy(m_arToolbarID);
  328.   // destroy everything
  329.   Destroy();
  330.   // re-load toolbars.
  331.   int nToolbars = arToolbarID.GetSize();
  332.   for (int i = 0; i < nToolbars; i++)
  333.     LoadToolbar(arToolbarID[i]);
  334. }
  335. //////////////////
  336. // Get menu font, creating if needed
  337. //
  338. CFont* CCoolMenuManager::GetMenuFont()
  339. {
  340.   if (!(HFONT)m_fontMenu) {
  341.     NONCLIENTMETRICS info;
  342.     info.cbSize = sizeof(info);
  343.     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  344.     VERIFY(m_fontMenu.CreateFontIndirect(&info.lfMenuFont));
  345.   }
  346.   return &m_fontMenu;
  347. }
  348. //////////////////
  349. // Handle WM_MEASUREITEM on behalf of frame: compute menu item size.
  350. //
  351. BOOL CCoolMenuManager::OnMeasureItem(LPMEASUREITEMSTRUCT lpms)
  352. {
  353. ASSERT(lpms);
  354. CMyItemData* pmd = (CMyItemData*)lpms->itemData;
  355. ASSERT(pmd);
  356. if (lpms->CtlType != ODT_MENU || !pmd->IsMyItemData())
  357. return FALSE; // not handled by me
  358. // Addition: Philip Oldaker
  359. // Pass it on to the shell if lpcm has been set
  360. if (pmd->lpcm)
  361. {
  362. IContextMenu2 *lpcm2=NULL;
  363. HRESULT hr = pmd->lpcm->QueryInterface(IID_IContextMenu2,(LPVOID*)&lpcm2);
  364. if (SUCCEEDED(hr))
  365. {
  366. // switch context item data
  367. lpms->itemData = (DWORD)pmd->pContext;
  368. lpcm2->HandleMenuMsg(WM_MEASUREITEM,0,(LPARAM)lpms);
  369. // clean up
  370. lpms->itemData = (DWORD)pmd;
  371. lpcm2->Release();
  372. }
  373. return TRUE;
  374. }
  375. /////////////////////
  376. if (pmd->fType & MFT_SEPARATOR) {
  377. // separator: use half system height and zero width
  378. lpms->itemHeight = GetSystemMetrics(SM_CYMENU)>>1;
  379. lpms->itemWidth  = 0;
  380. } else {
  381.     // compute size of text: use DrawText with DT_CALCRECT
  382.     CWindowDC dc(NULL); // screen DC--I won't actually draw on it
  383.     CRect rcText(0,0,0,0);
  384.     CFont* pOldFont = dc.SelectObject(GetMenuFont());
  385.     dc.DrawText(pmd->text, rcText, DT_MYSTANDARD|DT_CALCRECT);
  386.     dc.SelectObject(pOldFont);
  387.     // height of item is just height of a standard menu item
  388.     lpms->itemHeight= max(GetSystemMetrics(SM_CYMENU), rcText.Height());
  389.     // width is width of text plus a bunch of stuff
  390.     int cx = rcText.Width();  // text width 
  391.     cx += CXTEXTMARGIN<<1;    // L/R margin for readability
  392.     cx += CXGAP;          // space between button and menu text
  393.     cx += m_szButton.cx<<1;   // button width (L=button; R=empty margin)
  394.     // whatever value I return in lpms->itemWidth, Windows will add the
  395.     // width of a menu checkmark, so I must subtract to defeat Windows. Argh.
  396.     //
  397.     cx -= GetSystemMetrics(SM_CXMENUCHECK)-1;
  398.     lpms->itemWidth = cx;   // done deal
  399.     CMTRACE(_T("OnMeasureItem for '%s':tw=%d h=%dn"), (LPCTSTR)pmd->text,
  400.       lpms->itemWidth, lpms->itemHeight);
  401.   }
  402.   return TRUE; // handled
  403. }
  404. /////////////////
  405. // Handle WM_DRAWITEM on behalf of frame. Note: in all that goes
  406. // below, can't assume rcItem.left=0 because of multi-column menus!
  407. //
  408. BOOL CCoolMenuManager::OnDrawItem(LPDRAWITEMSTRUCT lpds)
  409. {
  410. ASSERT(lpds);
  411. // Omar L Francisco
  412. if (lpds->CtlType != ODT_MENU)
  413. return FALSE;
  414.   
  415. // Omar L Francisco
  416. CMyItemData* pmd = (CMyItemData*)lpds->itemData;
  417. if (!pmd->IsMyItemData())
  418. return FALSE;
  419. // Addition: Philip Oldaker
  420. // Pass it on to the shell if this has been set
  421. if (pmd->lpcm)
  422. {
  423. IContextMenu2 *lpcm2=NULL;
  424. HRESULT hr = pmd->lpcm->QueryInterface(IID_IContextMenu2,(LPVOID*)&lpcm2);
  425. if (SUCCEEDED(hr))
  426. {
  427. lpds->itemData = (DWORD)pmd->pContext;
  428. lpcm2->HandleMenuMsg(WM_DRAWITEM,0,(LPARAM)lpds);
  429. lpds->itemData = (DWORD)pmd;
  430. lpcm2->Release();
  431. }
  432. return TRUE;
  433. }
  434. ////////////////////////////////
  435.   ASSERT(lpds->itemAction != ODA_FOCUS);
  436.   ASSERT(lpds->hDC);
  437.   CDC dc;
  438.   dc.Attach(lpds->hDC);
  439.   const CRect& rcItem = lpds->rcItem;
  440.   if (pmd->fType & MFT_SEPARATOR) {
  441.     // draw separator
  442.     CRect rc = rcItem;                // copy rect
  443.     rc.top += rc.Height()>>1;           // vertical center
  444.     dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP);    // draw separator line
  445.   } else {                          // not a separator
  446.     CMTRACE(_T("OnDrawItem for '%s':tw=%d h=%dn"), (LPCTSTR)pmd->text,
  447.       rcItem.Width(), rcItem.Height());
  448.     BOOL bDisabled = lpds->itemState & ODS_GRAYED;
  449.     BOOL bSelected = lpds->itemState & ODS_SELECTED;
  450.     BOOL bChecked  = lpds->itemState & ODS_CHECKED;
  451.     BOOL bHaveButn=FALSE;
  452.     // Paint button, or blank if none
  453.     CRect rcButn(rcItem.TopLeft(), m_szButton); // button rect
  454.     rcButn += CPoint(0,                 // center vertically
  455.       (rcItem.Height() - rcButn.Height())>>1 );
  456.     int iButton = pmd->iButton;
  457.     if (iButton >= 0  ||  iButton == USES_ICON) 
  458.     {
  459.       // this item has a button!
  460.       bHaveButn = TRUE;
  461.       // compute point to start drawing
  462.       CSize sz = rcButn.Size() - m_szBitmap;
  463.       sz.cx >>= 1;
  464.       sz.cy >>= 1;
  465.       CPoint p(rcButn.TopLeft() + sz);
  466.       // draw disabled or normal
  467.       if (!bDisabled) {
  468.         // normal: fill BG depending on state
  469.         PLFillRect(dc, rcButn, GetSysColor(
  470.           (bChecked && !bSelected) ? COLOR_3DLIGHT : COLOR_MENU));
  471.         // draw pushed-in or popped-out edge
  472.         if (bSelected || bChecked) {
  473.           CRect rc2 = rcButn;
  474.           dc.DrawEdge(rc2, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER,
  475.             BF_RECT);
  476.         }
  477.         // draw the button!
  478.         if (iButton != USES_ICON)
  479.         {
  480.             m_ilButtons.Draw(&dc, iButton, p, ILD_TRANSPARENT);
  481.         }
  482.         else
  483.         {
  484.             dc.DrawState(p, CSize(0,0), pmd->hIcon, DSS_NORMAL, 
  485.                 (HBRUSH)NULL);
  486.         }
  487.       } else if (m_bUseDrawState) {
  488.         // use DrawState to draw disabled button: must convert to icon
  489.         if (iButton != USES_ICON)
  490.         {
  491.             HICON hIcon=m_ilButtons.ExtractIcon(iButton);
  492.             ASSERT(hIcon);
  493.             dc.DrawState(p, CSize(0,0), hIcon, DSS_DISABLED, (HBRUSH)NULL);
  494.             DestroyIcon(hIcon);
  495.         }
  496.         else
  497.         {
  498.             dc.DrawState(p, CSize(0,0), pmd->hIcon, DSS_DISABLED, 
  499.                 (HBRUSH)NULL);
  500.         }
  501.       } else
  502.         // use DrawEmbossed to draw disabeld button, w/color flag
  503.         if (iButton != USES_ICON)
  504.         {
  505.             PLDrawEmbossed(dc, m_ilButtons, iButton, p,
  506.               m_bDrawDisabledButtonsInColor);
  507.         }
  508.         else
  509.         {
  510.             int i = m_ilTemp.Add(pmd->hIcon);
  511.             PLDrawEmbossed(dc, m_ilTemp, i, p, 
  512.                 m_bDrawDisabledButtonsInColor);
  513.             m_ilTemp.Remove(i);
  514.         }
  515.     } else {
  516.       // no button: look for custom checked/unchecked bitmaps
  517.       CMenuItemInfo info;
  518.       info.fMask = MIIM_CHECKMARKS;
  519.       GetMenuItemInfo((HMENU)lpds->hwndItem,
  520.         lpds->itemID, MF_BYCOMMAND, &info);
  521.       if (bChecked || info.hbmpUnchecked) {
  522.         bHaveButn = Draw3DCheckmark(dc, rcButn, bSelected,
  523.           bChecked ? info.hbmpChecked : info.hbmpUnchecked);
  524.       }
  525.     }
  526.     // Done with button, now paint text. First do background if needed.
  527.     int cxButn = m_szButton.cx;       // width of button
  528.     COLORREF colorBG = GetSysColor(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU);
  529.     if (bSelected || lpds->itemAction==ODA_SELECT) {
  530.       // selected or selection state changed: paint text background
  531.       CRect rcBG = rcItem;              // whole rectangle
  532.       if (bHaveButn)                  // if there's a button:
  533.         rcBG.left += cxButn + CXGAP;      //  don't paint over it!
  534.       PLFillRect(dc, rcBG, colorBG);  // paint it!
  535.     }
  536.     // compute text rectangle and colors
  537.     CRect rcText = rcItem;         // start w/whole item
  538.     rcText.left += cxButn + CXGAP + CXTEXTMARGIN; // left margin
  539.     rcText.right -= cxButn;        // right margin
  540.     dc.SetBkMode(TRANSPARENT);       // paint transparent text
  541.     COLORREF colorText = GetSysColor(bDisabled ?  COLOR_GRAYTEXT :
  542.       bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
  543.     // Now paint menu item text.  No need to select font,
  544.     // because windows sets it up before sending WM_DRAWITEM
  545.     //
  546.     if (bDisabled && (!bSelected || colorText == colorBG)) {
  547.       // disabled: draw hilite text shifted southeast 1 pixel for embossed
  548.       // look. Don't do it if item is selected, tho--unless text color same
  549.       // as menu highlight color. Got it?
  550.       //
  551.       DrawMenuText(dc, rcText + CPoint(1,1), pmd->text,
  552.         GetSysColor(COLOR_3DHILIGHT));
  553.     }
  554.     DrawMenuText(dc, rcText, pmd->text, colorText); // finally!
  555.   }
  556.   dc.Detach();
  557.   return TRUE; // handled
  558. }
  559. /////////////////
  560. // Helper function to draw justified menu text. If the text contains a TAB,
  561. // draw everything after the tab right-aligned
  562. //
  563. void CCoolMenuManager::DrawMenuText(CDC& dc, CRect rc, CString text,
  564.   COLORREF color)
  565. {
  566.   CString left = text;
  567.   CString right;
  568.   int iTabPos = left.Find(_T('t'));
  569.   if (iTabPos >= 0) {
  570.     right = left.Right(left.GetLength() - iTabPos - 1);
  571.     left  = left.Left(iTabPos);
  572.   }
  573.   dc.SetTextColor(color);
  574.   dc.DrawText(left, &rc, DT_MYSTANDARD);
  575.   if (iTabPos > 0)
  576.     dc.DrawText(right, &rc, DT_MYSTANDARD|DT_RIGHT);
  577. }
  578. #ifndef OBM_CHECK
  579. #define OBM_CHECK 32760 // from winuser.h
  580. #endif
  581. //////////////////
  582. // Draw 3D checkmark
  583. //
  584. //    dc        device context to draw in
  585. //    rc        rectangle to center bitmap in
  586. //    bSelected TRUE if button is also selected
  587. //    hbmCheck    Checkmark bitmap to use, or NULL for default
  588. //
  589. BOOL CCoolMenuManager::Draw3DCheckmark(CDC& dc, const CRect& rc,
  590.   BOOL bSelected, HBITMAP hbmCheck)
  591. {
  592.   // get checkmark bitmap if none, use Windows standard
  593.   if (!hbmCheck) {
  594.     CBitmap bm;
  595.     VERIFY(bm.LoadOEMBitmap(OBM_CHECK));
  596.     hbmCheck = (HBITMAP)bm.Detach();
  597.     ASSERT(hbmCheck);
  598.   }
  599.   
  600.   // center bitmap in caller's rectangle
  601.   BITMAP bm;
  602.   ::GetObject(hbmCheck, sizeof(bm), &bm);
  603.   int cx = bm.bmWidth;
  604.   int cy = bm.bmHeight;
  605.   CRect rcDest = rc;
  606.   CPoint p(0,0);
  607.   CSize delta(CPoint((rc.Width() - cx)/2, (rc.Height() - cy)/2));
  608.   if (rc.Width() > cx)
  609.     rcDest = CRect(rc.TopLeft() + delta, CSize(cx, cy));
  610.   else
  611.     p -= delta;
  612.   // select checkmark into memory DC
  613.   CDC memdc;
  614.   memdc.CreateCompatibleDC(&dc);
  615.   HBITMAP hOldBM = (HBITMAP)::SelectObject(memdc, hbmCheck);
  616.   // set BG color based on selected state
  617.   COLORREF colorOld =
  618.     dc.SetBkColor(GetSysColor(bSelected ? COLOR_MENU : COLOR_3DLIGHT));
  619.   dc.BitBlt(rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(),
  620.     &memdc, p.x, p.y, SRCCOPY);
  621.   dc.SetBkColor(colorOld);
  622.   ::SelectObject(memdc, hOldBM); // restore
  623.   // draw pushed-in hilight.
  624.   if (rc.Width() > cx)        // if room:
  625.     rcDest.InflateRect(1,1);  // inflate checkmark by one pixel all around
  626.   dc.DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT);
  627.   return TRUE;
  628. }
  629. //////////////////
  630. // Handle WM_INITMENUPOPUP on behalf of frame.
  631. //
  632. void CCoolMenuManager::OnInitMenuPopup(CMenu* pMenu,
  633.   UINT nIndex, BOOL bSysMenu)
  634. {
  635. // Addition: Philip Oldaker
  636. /////////////////////////
  637. // Let the shell handle it
  638. if (bSysMenu)
  639. return;
  640. for(UINT i=0;i < pMenu->GetMenuItemCount();i++)
  641. {
  642. CMyItemData* pmd = GetMyItemData(*pMenu,i,TRUE);
  643. if (pmd->IsMyItemData() && pmd->lpcm)
  644. {
  645. IContextMenu2 *lpcm2=NULL;
  646. HRESULT hr = pmd->lpcm->QueryInterface(IID_IContextMenu2,(LPVOID*)&lpcm2);
  647. if (SUCCEEDED(hr))
  648. {
  649. SetMyItemData((CMyItemData*)pmd->pContext,*pMenu,i,TRUE); 
  650. lpcm2->HandleMenuMsg(WM_INITMENUPOPUP,(WPARAM)pMenu->GetSafeHmenu(),0);
  651. SetMyItemData(pmd,*pMenu,i,TRUE); 
  652. lpcm2->Release();
  653. }
  654. return;
  655. }
  656. }
  657. //////////////////////
  658.   if (m_bAutoAccel)
  659.   {
  660.     // check for new accels. If ASSERT bombs,
  661.     // you forgot to call Install.
  662. //    ASSERT_VALID(m_pFrame);
  663.     
  664. //    HACCEL hAccel = m_pFrame->GetDefaultAccelerator();
  665.     HACCEL hAccel = NULL;
  666.     
  667.     if (hAccel != m_hAccel)
  668.       LoadAccel(hAccel);
  669.     // 12 Nov 1998  - Wang Jun - Fixes debug assert in system menu.
  670.     // Check if click system menu.
  671.     if (!bSysMenu) {
  672.       ConvertMenu(pMenu, nIndex, bSysMenu, m_bShowButtons);
  673.     }
  674.   }
  675.   ConvertMenu(pMenu, nIndex, bSysMenu, m_bShowButtons);
  676. }
  677. //////////////////
  678. // Set the accelerator table used to generate automatic key
  679. // names in menus. Delete previous table if any.
  680. //
  681. void CCoolMenuManager::LoadAccel(HACCEL hAccel)
  682. {
  683.   DestroyAccel();
  684.   int nAccel;
  685.   if (hAccel && (nAccel = CopyAcceleratorTable(hAccel, NULL, 0)) > 0) {
  686.     m_pAccel = new ACCEL [nAccel];
  687.     ASSERT(m_pAccel);
  688.     CopyAcceleratorTable(hAccel, m_pAccel, nAccel);
  689.     // Now I have the accelerators. Look over list, linking each command
  690.     // ID with its ACCEL structure--i.e., m_mapIDtoAccel[nID] = ACCEL for
  691.     // that ID. If more than one ACCEL for a given command (command has more
  692.     // than one shortcut), fix up so ACCEL.cmd is offset of prev ACCEL
  693.     // 
  694.     for (int i=0; i<nAccel; i++) {
  695.       ACCEL& ac = m_pAccel[i];
  696.       ACCEL* pAccel = GetAccel(ac.cmd);
  697.       m_mapIDtoAccel.SetAt(ac.cmd, &ac);
  698.       ac.cmd = pAccel ? &ac - pAccel : 0; // ac.cmd = offset of prev, or 0
  699.     }
  700.   }
  701. }
  702. void CCoolMenuManager::ConvertMenu(CMenu *pMenu)
  703. {
  704.     ConvertMenu(pMenu, 0, FALSE, TRUE);
  705. }
  706. void CCoolMenuManager::UnconvertMenu(CMenu *pMenu)
  707. {
  708.     ConvertMenu(pMenu, 0, FALSE, FALSE);
  709. }
  710. void CCoolMenuManager::ConvertMenuItem(CMenu *pMenu, int iIndex, 
  711.     bool bRecurse /*= true*/, bool bUnconvert /*= false*/)
  712. {
  713.     ASSERT_VALID(pMenu);
  714.     CString   sItemName;
  715.     // get menu item info
  716.     TCHAR itemname[_MAX_PATH];
  717.     CMenuItemInfo info;
  718.     info.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_ID | MIIM_TYPE;
  719.     info.dwTypeData = itemname;
  720.     info.cch = sizeof(itemname);
  721.     ::GetMenuItemInfo(*pMenu, iIndex, TRUE, &info);
  722.     
  723.     // O.S. - ignore separators
  724.     if (info.fType & MFT_SEPARATOR)
  725.         return;
  726.     CMyItemData* pmd = (CMyItemData*)info.dwItemData;
  727.     // O.S. - recurse sub-menus
  728.     if (bRecurse && info.hSubMenu)
  729.     {
  730.         ConvertMenu(pMenu->GetSubMenu(iIndex), 0, FALSE, !bUnconvert);
  731.     }
  732.     
  733.     if ((info.fType & MFT_OWNERDRAW)  && pmd  &&  !pmd->IsMyItemData()) {
  734.         CMTRACE(_T("CCoolMenuManager: ignoring foreign owner-draw itemn"));
  735.         return;
  736.         // owner-draw menu item isn't mine--leave it alone
  737.     }
  738.     
  739. /*    // Koji MATSUNAMI 1999.2.23
  740.     //
  741.     if (bSysMenu && info.wID >= 0xF000) {
  742.         CMTRACE(_T("CCoolMenuManager: ignoring sys menu itemn"));
  743.         continue; // don't do for system menu commands
  744.     }
  745. */    
  746.     // now that I have the info, I will modify it
  747.     info.fMask = 0; // assume nothing to change
  748.     
  749.     if (!bUnconvert) 
  750.     {
  751.         // I'm showing buttons: convert to owner-draw
  752.         
  753.         if (!(info.fType & MFT_OWNERDRAW))
  754. {
  755.             // If not already owner-draw, make it so. NOTE: If app calls
  756.             // pCmdUI->SetText to change the text of a menu item, MFC will
  757.             // turn the item to MFT_STRING. So I must set it back to
  758.             // MFT_OWNERDRAW again. In this case, the menu item data (pmd)
  759.             // will still be there.
  760.             // 
  761.             info.fType |= MFT_OWNERDRAW;
  762.             info.fMask |= MIIM_TYPE;
  763.             if (!pmd || (pmd && !pmd->IsMyItemData()))
  764. {                   // if no item data:
  765. if (pmd && !pmd->IsMyItemData())
  766. {
  767. CMyItemData* pnmd = new CMyItemData;    //   create one
  768. pnmd->pContext = pmd;
  769. pmd = pnmd;
  770. }
  771. else
  772. pmd = new CMyItemData;    //   create one
  773.                 ASSERT(pmd);              //   (I hope)
  774.                 pmd->fType = info.fType;  //   handy when drawing
  775.                 if (info.hSubMenu)
  776.                 {
  777.                     int nID;
  778.                     if (m_mapHMENUtoID.Lookup((void *)info.wID, (WORD&)nID))
  779.                     {
  780.                         pmd->iButton = GetButtonIndex(nID);
  781.                     }
  782.                     else
  783.                     {
  784.                         pmd->iButton = -1;
  785.                     }
  786.                 }
  787.                 else
  788.                     pmd->iButton = GetButtonIndex(info.wID);
  789.                 
  790.                 info.dwItemData = (DWORD)pmd;   //   set in menu item data
  791.                 info.fMask |= MIIM_DATA;      //   set item data
  792.             }
  793.             pmd->text = info.dwTypeData;      // copy menu item string
  794.             // append accelerators to menu item name
  795.             if (m_pAccel && m_bAutoAccel)
  796.                 AppendAccelName(pmd->text, info.wID);
  797.         }
  798.    } 
  799.    else 
  800.    {
  801.         // no buttons -- I'm converting to strings
  802.         
  803.         if (info.fType & MFT_OWNERDRAW)  // if ownerdraw:
  804.         { 
  805.             info.fType &= ~MFT_OWNERDRAW;   //   turn it off
  806.             info.fMask |= MIIM_TYPE;        //   change item type
  807.             ASSERT(pmd);                    //   sanity check
  808.             sItemName = pmd->text;          //   save name before deleting pmd
  809.         } 
  810.         else                    // otherwise: 
  811.         {
  812.             sItemName = info.dwTypeData;    //   use name from MENUITEMINFO
  813.             pmd       = NULL;
  814.         }
  815.         
  816.         if (pmd) {
  817.             // NOTE: pmd (item data) could still be left hanging around even
  818.             // if MFT_OWNERDRAW is not set, in case mentioned above where app
  819.             // calls pCmdUI->SetText to set text of item and MFC sets the type
  820.             // to MFT_STRING.
  821.             //
  822.             info.dwItemData = (DWORD)pmd->pContext; // item data<-user's context
  823.             info.fMask |= MIIM_DATA;                // change it
  824.             safe_delete(pmd);                       // and item data too
  825.         }
  826.         
  827.         // possibly add accelerator name
  828.         if (m_pAccel  && m_bAutoAccel && AppendAccelName(sItemName, info.wID))
  829.             info.fMask |= MIIM_TYPE;      //  change item type (string)
  830.         
  831.         if (info.fMask & MIIM_TYPE) {
  832.             // if setting name, copy name from CString to buffer and set cch
  833.             _tcsncpy(itemname, sItemName, sizeof(itemname));
  834.             info.dwTypeData = itemname;
  835.             info.cch = sItemName.GetLength();
  836.         }
  837.     }
  838.     
  839.     // if after all the above, there is anything to change, change it
  840.     if (info.fMask) {
  841.         CMTRACE(_T("Converting '%s' to %sn"), itemname,
  842.             (info.fType & MFT_OWNERDRAW) ? _T("OWNERDRAW") : _T("STRING"));
  843.         SetMenuItemInfo(*pMenu, iIndex, TRUE, &info);
  844.     }
  845. }
  846. ///////////////////////
  847. // Addition: Philip Oldaker
  848. DWORD CCoolMenuManager::SwitchContextItemData(CMenu *pMenu, int iCmd, DWORD dwItemData /*0*/, BOOL bByPosition /*TRUE*/)
  849. {
  850. CMyItemData* pmd = GetMyItemData(*pMenu,iCmd,bByPosition);
  851. DWORD dwData=0;
  852. if (pmd == NULL)
  853. return dwData;
  854. CMenuItemInfo info;
  855.     info.fMask |= MIIM_DATA;                // change it
  856. if (pmd->IsMyItemData())
  857. {
  858. dwData= (DWORD)pmd;
  859.         info.dwItemData = (DWORD)pmd->pContext;
  860.         SetMenuItemInfo(*pMenu, iCmd, bByPosition, &info);
  861. }
  862. else if (dwItemData)
  863. {
  864. CMyItemData *pMyItemData = (CMyItemData*)dwItemData;
  865. if (pMyItemData->IsMyItemData())
  866. {
  867.         info.dwItemData = dwItemData;
  868.     SetMenuItemInfo(*pMenu, iCmd, bByPosition, &info);
  869. }
  870. }
  871. return dwData;
  872. }
  873. // This may not be the best way of doing it but it seems to work
  874. // Just save the IContextMenu2 pointer on behalf of the windows shell
  875. // and create new item data
  876. void CCoolMenuManager::AddShellContextMenu(CMenu *pMenu, LPCONTEXTMENU lpm, int iIndex)
  877. {
  878. CMyItemData* pmd = GetMyItemData(*pMenu,iIndex,TRUE);
  879. if (pmd == NULL || !pmd->IsMyItemData())
  880. {
  881. DWORD dw = (DWORD)pmd;
  882. pmd = new CMyItemData;
  883. pmd->pContext = (void*)dw;
  884. }
  885. pmd->lpcm = lpm;
  886. pmd->lpcm->AddRef();
  887.     SetMyItemData(pmd,*pMenu,iIndex,TRUE);
  888. }
  889. ///////////////////////////////
  890. //////////////////
  891. // This rather gnarly function is used both to convert the menu from strings to
  892. // owner-draw and vice versa. In either case, it also appends automagic
  893. // accelerator key names to the menu items, if m_bAutoAccel is TRUE.
  894. //
  895. void CCoolMenuManager::ConvertMenu(CMenu* pMenu, UINT nIndex, 
  896.                                    BOOL bSysMenu, BOOL bShowButtons)
  897. {
  898.     ASSERT_VALID(pMenu);
  899.     
  900.     UINT i, nItem;
  901.     // add/remove the menu to/from list of "converted" menus
  902.     HMENU hmenu = pMenu->GetSafeHmenu();
  903.     ASSERT(hmenu);
  904.     POSITION p = m_menuList.Find(hmenu);
  905.     if (!p)
  906.     {
  907.         if (bShowButtons)
  908.             m_menuList.AddHead(hmenu);
  909.     }
  910.     else
  911.     {
  912.         if (!bShowButtons)
  913.             m_menuList.RemoveAt(p);
  914.     }
  915.     
  916.     nItem = pMenu->GetMenuItemCount();    
  917.     for (i = 0; i < nItem; i++) 
  918.     {  
  919.         // loop over each item in menu
  920.         ConvertMenuItem(pMenu, i, true, !bShowButtons);
  921.     }
  922. }
  923. void *CCoolMenuManager::SetItemData(HMENU hMenu, void *pData, UINT uItem, BOOL bByPosition /*= FALSE*/)
  924. {
  925.     CMyItemData *pmd = GetMyItemData(hMenu, uItem, bByPosition);
  926.     ASSERT(pmd->IsMyItemData());
  927.     void *pOld = pmd->pContext;
  928.     pmd->pContext = pData;
  929.     SetMyItemData(pmd, hMenu, uItem, bByPosition);
  930.     return pOld;
  931. }
  932. void *CCoolMenuManager::GetItemData(HMENU hMenu, UINT uItem, BOOL bByPosition /*= FALSE*/)
  933. {
  934.     MENUITEMINFO mii;
  935.     mii.cbSize     = sizeof(mii);
  936.     mii.fMask      = MIIM_DATA | MIIM_TYPE;
  937.     mii.dwTypeData = NULL;
  938.     mii.cch        = 0;
  939.     if (!::GetMenuItemInfo(hMenu, uItem, bByPosition, &mii))
  940.         return NULL;
  941. if (mii.dwItemData == -1) _asm int 3;    
  942.     CMyItemData *pmd = (CMyItemData *)mii.dwItemData;
  943.     if ((mii.fType & MFT_OWNERDRAW)  &&  pmd  &&  pmd->IsMyItemData())
  944.         return pmd->pContext;
  945.     return pmd;
  946. }
  947. HICON CCoolMenuManager::SetItemIcon(HMENU hMenu, HICON hIcon, UINT uItem, BOOL bByPosition /*= FALSE*/)
  948. {
  949.     CMyItemData *pmd = GetMyItemData(hMenu, uItem, bByPosition);
  950.     ASSERT(pmd->IsMyItemData());
  951.     HICON hOldIcon = pmd->hIcon;
  952.     pmd->hIcon = hIcon;
  953.     if (hIcon)
  954.         pmd->iButton = USES_ICON;
  955.     else
  956.         pmd->iButton = -1;
  957.     SetMyItemData(pmd, hMenu, uItem, bByPosition);
  958.     return hOldIcon;
  959. }
  960. HICON CCoolMenuManager::GetItemIcon(HMENU hMenu, UINT uItem, BOOL bByPosition /*= FALSE*/)
  961. {
  962.     CMyItemData *pmd = GetMyItemData(hMenu, uItem, bByPosition);
  963.     ASSERT(pmd->IsMyItemData());
  964.     return pmd->hIcon;
  965. }
  966. void CCoolMenuManager::SetSubMenuIcon(HMENU hSubMenu, int nID)
  967. {
  968.     m_mapHMENUtoID[(void *)hSubMenu] = nID;
  969. }
  970. void CCoolMenuManager::UnmanageSubMenu(HMENU hSubMenu)
  971. {
  972.     m_mapHMENUtoID.RemoveKey((void *)hSubMenu);
  973. }
  974. //////////////////
  975. // User typed a char into menu. Look for item with & preceeding the char typed.
  976. //
  977. LRESULT CCoolMenuManager::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu)
  978. {
  979. ASSERT_VALID(pMenu);
  980. CMyItemData* pmd = GetMyItemData(*pMenu,0,TRUE);
  981. // Addition: Philip Oldaker
  982. // Pass it on to the shell if this has been set
  983. if (pmd->IsMyItemData() && pmd->lpcm)
  984. {
  985. LRESULT lResult=0;
  986. LPCONTEXTMENU3 lpcm3=NULL;
  987. HRESULT hr = pmd->lpcm->QueryInterface(IID_IContextMenu3,(LPVOID*)&lpcm3);
  988. if (SUCCEEDED(hr))
  989. {
  990. // convert menu back to original item data
  991. UINT nCount = pMenu->GetMenuItemCount();
  992. DWORD *pArrItemData = new DWORD[nCount];
  993. for(UINT i1=0;i1 < nCount;i1++)
  994. pArrItemData[i1] = SwitchContextItemData(pMenu,i1);
  995. lpcm3->HandleMenuMsg2(WM_MENUCHAR,MAKELPARAM(nChar,nFlags),(LPARAM)pMenu->GetSafeHmenu(),&lResult);
  996. lpcm3->Release();
  997. // Convert menu back to back our item data
  998. for(UINT i2=0;i2 < nCount;i2++)
  999. SwitchContextItemData(pMenu,i2,pArrItemData[i2]);
  1000. delete []pArrItemData;
  1001. }
  1002. return lResult;
  1003. }
  1004. ////////////////////////////////
  1005.   UINT iCurrentItem = (UINT)-1; // guaranteed higher than any command ID
  1006.   CUIntArray arItemsMatched;    // items that match the character typed
  1007.   UINT nItem = pMenu->GetMenuItemCount();
  1008.   for (UINT i=0; i< nItem; i++) {
  1009.     // get menu info
  1010.     CMenuItemInfo info;
  1011.     info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE;
  1012.     ::GetMenuItemInfo(*pMenu, i, TRUE, &info);
  1013.     CMyItemData* pmd = (CMyItemData*)info.dwItemData;
  1014.     if ((info.fType & MFT_OWNERDRAW) && pmd && pmd->IsMyItemData()) {
  1015.       CString& text = pmd->text;
  1016.       int iAmpersand = text.Find(_T('&'));
  1017.       if (iAmpersand >=0 && toupper(nChar)==toupper(text[iAmpersand+1]))
  1018.         arItemsMatched.Add(i);
  1019.     }
  1020.     if (info.fState & MFS_HILITE)
  1021.       iCurrentItem = i; // note index of current item
  1022.   }
  1023.   // arItemsMatched now contains indexes of items that match the char typed.
  1024.   //
  1025.   //   * if none: beep
  1026.   //   * if one:  execute it
  1027.   //   * if more than one: hilite next
  1028.   //
  1029.   UINT nFound = arItemsMatched.GetSize();
  1030.   if (nFound == 0)
  1031.     return 0;
  1032.   else if (nFound==1)
  1033.     return MAKELONG(arItemsMatched[0], MNC_EXECUTE);
  1034.   // more than one found--return 1st one past current selected item;
  1035.   UINT iSelect = 0;
  1036.   for (i=0; i < nFound; i++) {
  1037.     if (arItemsMatched[i] > iCurrentItem) {
  1038.       iSelect = i;
  1039.       break;
  1040.     }
  1041.   }
  1042.   return MAKELONG(arItemsMatched[iSelect], MNC_SELECT);
  1043. }
  1044. //////////////////
  1045. // Handle WM_MENUSELECT: check for menu closed
  1046. //
  1047. void CCoolMenuManager::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
  1048. {
  1049. if (m_lpcm)
  1050. {
  1051. char szHelpText[MAX_PATH];
  1052. ZeroMemory(szHelpText,sizeof(szHelpText));
  1053. if ((nFlags & MF_SYSMENU) != MF_SYSMENU && (nFlags & MF_POPUP) != MF_POPUP)
  1054. {
  1055. if (nItemID >= m_idShellMenuFirst && nItemID <= m_idShellMenuLast)
  1056. {
  1057. m_lpcm->GetCommandString(nItemID-m_idShellMenuFirst,GCS_HELPTEXT,NULL,szHelpText,sizeof(szHelpText));
  1058. }
  1059. }
  1060. CWnd *pWnd=m_pFrame;
  1061. if (pWnd == NULL)
  1062. pWnd = AfxGetMainWnd();
  1063. if (pWnd)
  1064. pWnd->SendMessage(WM_SETMESSAGESTRING,0,(LPARAM)szHelpText);
  1065. }
  1066. if (hSysMenu==NULL && nFlags==0xFFFF) 
  1067. {
  1068. if (m_lpcm)
  1069. {
  1070. TRACE0("Setting lpcm to nulln");
  1071. m_lpcm = NULL;
  1072. }
  1073. /*
  1074. //  de-comment this code for self destructing menus.s
  1075. // Windows has closed the menu: restore all menus to original state
  1076. while (!m_menuList.IsEmpty()) 
  1077. {
  1078.   ConvertMenu(CMenu::FromHandle((HMENU)m_menuList.RemoveHead()),
  1079. 0, FALSE, FALSE);
  1080. }
  1081. */
  1082. }
  1083. }
  1084. // fix for virtual key names - Seain Conover (1999/01/09) 
  1085. CString CCoolMenuManager::GetVirtualKeyName( const CString strVirtKey ) const
  1086. {
  1087.   CString strResult;
  1088.   
  1089.   if    ( strVirtKey == _T("Num 7"))    { strResult = _T("Home"); }
  1090.   else if ( strVirtKey == _T("Num 1"))    { strResult = _T("End");  }
  1091.   else if ( strVirtKey == _T("Num 9"))    { strResult = _T("PgUp"); }
  1092.   else if ( strVirtKey == _T("Num 3"))    { strResult = _T("PgDn"); }
  1093.   else if ( strVirtKey == _T("Num 0"))    { strResult = _T("Ins");  }
  1094.   else if ( strVirtKey == _T("Num Del"))    { strResult = _T("Del");  }
  1095.   else
  1096.   {
  1097.     strResult = strVirtKey;
  1098.   }
  1099.   
  1100.   return strResult;
  1101. }
  1102. //////////////////
  1103. // Append the name of accelerator for given command ID to menu string.
  1104. // sItemName is menu item name, which will have the accelerator appended.
  1105. // For example, might call with sItemName = "File &Open" and return with
  1106. // sItemName = "File &OpentCtrl-O". Returns BOOL = whether string changed.
  1107. //
  1108. BOOL CCoolMenuManager::AppendAccelName(CString& sItemName, UINT nID)
  1109. {
  1110.   int iTabPos = sItemName.Find(_T('t'));
  1111.   if (iTabPos > 0)
  1112.     sItemName = sItemName.Left(iTabPos);
  1113.   BOOL bFound = FALSE;
  1114.   for (ACCEL* pa = GetAccel(nID); pa; pa -= pa->cmd) {
  1115.     sItemName += bFound ? _T(", ") : _T("t");
  1116.     if (pa->fVirt & FALT)     sItemName += _T("Alt+");
  1117.     if (pa->fVirt & FCONTROL)   sItemName += _T("Ctrl+");
  1118.     if (pa->fVirt & FSHIFT)     sItemName += _T("Shift+");
  1119.     if (pa->fVirt & FVIRTKEY) {
  1120.       TCHAR keyname[64];
  1121.       UINT vkey = MapVirtualKey(pa->key, 0)<<16;
  1122.       GetKeyNameText(vkey, keyname, sizeof(keyname));
  1123.       // Seain Conover (1999/01/09) 
  1124.       sItemName += GetVirtualKeyName( keyname );
  1125.     } else
  1126.       sItemName += (char)pa->key;
  1127.     bFound = TRUE;
  1128.     if (pa->cmd == 0)
  1129.       break;
  1130.   }
  1131.   return bFound;
  1132. }
  1133. //////////////////
  1134. // This function fixes MFC's diseased dot bitmap used for
  1135. // "radio-style" menu items (CCmdUI->SetRadio), which is completely
  1136. // wrong if the menu font is large. 
  1137. //
  1138. void CCoolMenuManager::FixMFCDotBitmap()
  1139. {
  1140.   HBITMAP hbmDot = GetMFCDotBitmap();
  1141.   if (hbmDot) {
  1142.     // Draw a centered dot of appropriate size
  1143.     BITMAP bm;
  1144.     ::GetObject(hbmDot, sizeof(bm), &bm);
  1145.     CRect rcDot(0,0, bm.bmWidth, bm.bmHeight);
  1146.     rcDot.DeflateRect((bm.bmWidth>>1)-2, (bm.bmHeight>>1)-2);
  1147.     CWindowDC dcScreen(NULL);
  1148.     CDC memdc;
  1149.     memdc.CreateCompatibleDC(&dcScreen);
  1150.     int nSave = memdc.SaveDC();
  1151.     memdc.SelectStockObject(BLACK_PEN);
  1152.     memdc.SelectStockObject(BLACK_BRUSH);
  1153.     memdc.SelectObject((HGDIOBJ)hbmDot);
  1154.     memdc.PatBlt(0, 0, bm.bmWidth, bm.bmHeight, WHITENESS);
  1155.     memdc.Ellipse(&rcDot);
  1156.     memdc.RestoreDC(nSave);
  1157.   }
  1158. }
  1159. //////////////////
  1160. // This function gets MFC's dot bitmap.
  1161. //
  1162. HBITMAP CCoolMenuManager::GetMFCDotBitmap()
  1163. {
  1164.   // The bitmap is stored in afxData.hbmMenuDot, but afxData is MFC-private,
  1165.   // so the only way to get it is create a menu, set the radio check,
  1166.   // and then see what bitmap MFC set in the menu item.
  1167.   CMenu menu;
  1168.   VERIFY(menu.CreateMenu());
  1169.   VERIFY(menu.AppendMenu(MFT_STRING, 0, (LPCTSTR)NULL));
  1170.   CCmdUI cui;
  1171.   cui.m_pMenu = &menu;
  1172.   cui.m_nIndex = 0;
  1173.   cui.m_nIndexMax = 1;
  1174.   cui.SetRadio(TRUE);
  1175.   CMenuItemInfo info;
  1176.   info.fMask = MIIM_CHECKMARKS;
  1177.   GetMenuItemInfo(menu, 0, MF_BYPOSITION, &info);
  1178.   HBITMAP hbmDot = info.hbmpChecked;
  1179.   menu.DestroyMenu();
  1180.   return hbmDot;
  1181. }
  1182. // Peter Tewkesbury
  1183. BOOL CCoolMenuManager::AddSingleBitmap(UINT nBitmapID, UINT n, UINT *nID)
  1184. {
  1185.   // load bitmap
  1186.   HBITMAP hbmBitmap = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  1187.     MAKEINTRESOURCE(nBitmapID), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
  1188.   ASSERT(hbmBitmap);
  1189.   if (!hbmBitmap) {
  1190.     TRACE(_T("*** Can't load bitmap %d!n"), nBitmapID);
  1191.     return FALSE;
  1192.   }
  1193.   // Assign Bitmap to CBitmap
  1194.   CBitmap bmBitmap;
  1195.   bmBitmap.Attach(hbmBitmap); // destructor will detach & destroy
  1196.   
  1197.   // OK, I have the bitmap - Check that Bitmaps are correct size.
  1198.   if (m_szBitmap.cx==0) 
  1199.   {
  1200.     // First toolbar: initialized bitmap/button sizes and create image list.
  1201.     CSize sz(16,15);
  1202.     m_szBitmap = sz;
  1203.     m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
  1204.     VERIFY(m_ilButtons.Create(sz.cx, sz.cy, ILC_MASK, 0, 10));
  1205.     
  1206.   } 
  1207.   
  1208.   // Add Bitmap to ImageList
  1209.   int iNextImage = m_ilButtons.GetImageCount();
  1210.   m_ilButtons.Add(&bmBitmap, GetSysColor(COLOR_3DFACE)); 
  1211.   
  1212.   // Add ID to Map.
  1213.   for(UINT i=0;i<n;i++)
  1214.   {
  1215.     if (nID[i] > 0) 
  1216.     {
  1217.       if (GetButtonIndex(nID[i]) >= 0) 
  1218.       {
  1219.         TRACE(_T("*** Duplicate button ID %d ignoredn"), nID[i]);
  1220.       } 
  1221.       else m_mapIDtoImage.SetAt(nID[i], (void*)iNextImage++); 
  1222.     }
  1223.   }
  1224.   
  1225.   // All Done.
  1226.   return TRUE;
  1227. }
  1228. ////////////////////////////////////////////////////////////////
  1229. // Helper functions
  1230. //////////////////
  1231. // Load a bitmap, converting the standard colors.
  1232. // Calls AfxLoadSysColorBitmap to do the work.
  1233. //
  1234. // RGB(0x00, 0x00, 0x00) (black)      --> COLOR_BTNTEXT
  1235. // RGB(0x80, 0x80, 0x80) (dark gray)  --> COLOR_3DSHADOW
  1236. // RGB(0xC0, 0xC0, 0xC0) (gray)       --> COLOR_3DFACE
  1237. // RGB(0xFF, 0xFF, 0xFF) (white)      --> COLOR_3DHILIGHT
  1238. // 
  1239. HBITMAP PLLoadSysColorBitmap(LPCTSTR lpResName, BOOL bMono)
  1240. {
  1241.   HINSTANCE hInst = AfxFindResourceHandle(lpResName, RT_BITMAP);
  1242.   HRSRC hRsrc = ::FindResource(hInst, lpResName, RT_BITMAP);
  1243.   if (hRsrc == NULL)
  1244.     return NULL;
  1245.   return AfxLoadSysColorBitmap(hInst, hRsrc, bMono);
  1246. }
  1247. //////////////////
  1248. // Shorthand to fill a rectangle with a solid color.
  1249. //
  1250. void PLFillRect(CDC& dc, const CRect& rc, COLORREF color)
  1251. {
  1252.   CBrush brush(color);
  1253.   CBrush* pOldBrush = dc.SelectObject(&brush);
  1254.   dc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
  1255.   dc.SelectObject(pOldBrush);
  1256. }
  1257. // This is the magic ROP code used to generate the embossed look for
  1258. // a disabled button. It's listed in Appendix F of the Win32 Programmer's
  1259. // Reference as PSDPxax (!) which is a cryptic reverse-polish notation for
  1260. //
  1261. // ((Destination XOR Pattern) AND Source) XOR Pattern
  1262. //
  1263. // which I leave to you to figure out. In the case where I apply it,
  1264. // Source is a monochrome bitmap which I want to draw in such a way that
  1265. // the black pixels get transformed to the brush color and the white pixels
  1266. // draw transparently--i.e. leave the Destination alone.
  1267. //
  1268. // black ==> Pattern (brush)
  1269. // white ==> Destintation (ie, transparent)
  1270. //
  1271. // 0xb8074a is the ROP code that does this. For more info, see Charles
  1272. // Petzold, _Programming Windows_, 2nd Edition, p 622-624.
  1273. //
  1274. #define TRANSPARENTROP 0xb8074a
  1275. //////////////////
  1276. // Draw an image with the embossed (disabled) look.
  1277. //
  1278. //    dc      device context to draw in
  1279. //    il      image list containing image
  1280. //    i     index of image to draw
  1281. //    p     point in dc to draw image at
  1282. //    bColor  do color embossing. Default is B/W.
  1283. //
  1284. void PLDrawEmbossedOld(CDC& dc, CImageList& il, int i, CPoint p, BOOL bColor)
  1285. {
  1286.   IMAGEINFO info;
  1287.   VERIFY(il.GetImageInfo(0, &info));
  1288.   CRect rc = info.rcImage;
  1289.   int cx = rc.Width();
  1290.   int cy = rc.Height();
  1291.   // create memory dc
  1292.   CDC memdc;
  1293.   memdc.CreateCompatibleDC(&dc);
  1294.   // create mono or color bitmap
  1295.   CBitmap bm;
  1296.   if (bColor)
  1297.     bm.CreateCompatibleBitmap(&dc, cx, cy);
  1298.   else
  1299.     bm.CreateBitmap(cx, cy, 1, 1, NULL);
  1300.   // draw image into memory DC--fill BG white first
  1301.   CBitmap* pOldBitmap = memdc.SelectObject(&bm);
  1302.   memdc.PatBlt(0, 0, cx, cy, WHITENESS);
  1303.   il.Draw(&memdc, i, CPoint(0,0), ILD_TRANSPARENT);
  1304.   // This seems to be required. Why, I don't know. ???
  1305.   COLORREF colorOldBG = dc.SetBkColor(RGB(255,255,255)); // white
  1306.   // Draw using hilite offset by (1,1), then shadow
  1307.   CBrush brShadow(GetSysColor(COLOR_3DSHADOW));
  1308.   CBrush brHilite(GetSysColor(COLOR_3DHIGHLIGHT));
  1309.   CBrush* pOldBrush = dc.SelectObject(&brHilite);
  1310.   dc.BitBlt(p.x+1, p.y+1, cx, cy, &memdc, 0, 0, TRANSPARENTROP);
  1311.   dc.SelectObject(&brShadow);
  1312.   dc.BitBlt(p.x, p.y, cx, cy, &memdc, 0, 0, TRANSPARENTROP);
  1313.   dc.SelectObject(pOldBrush);
  1314.   dc.SetBkColor(colorOldBG);         // restore
  1315.   memdc.SelectObject(pOldBitmap);    // ...
  1316. }
  1317. #define NEWROP  0x00E20746L
  1318. void PLDrawEmbossed(CDC& dc, CImageList& il, int i, CPoint p, BOOL bColor)
  1319. {
  1320.     IMAGEINFO info;
  1321.     VERIFY(il.GetImageInfo(0, &info));
  1322.     CRect rc = info.rcImage;
  1323.     int cx = rc.Width();
  1324.     int cy = rc.Height();
  1325.     CBitmap patbmp, bmp;
  1326. CDC memdc, patdc;
  1327. patdc.CreateCompatibleDC(0);
  1328. memdc.CreateCompatibleDC(0);
  1329. patbmp.CreateCompatibleBitmap(&patdc, cx, cy);
  1330. bmp.CreateCompatibleBitmap(&dc, cx, cy);
  1331. CBitmap * pddcOldBmp = patdc.SelectObject(&patbmp);
  1332.     CBitmap * pdcOldBmp  = memdc.SelectObject(&bmp);
  1333.     il.Draw(&memdc, i, CPoint(0, 0), ILD_NORMAL); //ILD_TRANSPARENT);
  1334. // build a mask
  1335. patdc.PatBlt(0, 0, cx, cy, WHITENESS);
  1336. memdc.SetBkColor(GetSysColor(COLOR_BTNFACE));
  1337. patdc.BitBlt(0, 0, cx, cy, &memdc, 0, 0, SRCCOPY);
  1338. memdc.SetBkColor(GetSysColor(COLOR_BTNHILIGHT));
  1339. patdc.BitBlt(0, 0, cx, cy, &memdc, 0, 0, SRCPAINT);
  1340. memdc.SetBkColor(RGB(0, 0, 0));
  1341. memdc.SetTextColor(RGB(255, 255, 255));
  1342. CBrush brShadow, brHilight;
  1343. brHilight.CreateSolidBrush(GetSysColor(COLOR_BTNHILIGHT));
  1344. brShadow.CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
  1345. CBrush * pOldBrush = memdc.SelectObject(&brHilight);
  1346. memdc.BitBlt(1, 1, cx, cy, &patdc, 0, 0, NEWROP);
  1347. memdc.BitBlt(0, 0, cx, cy, &patdc, 0, 0, NEWROP);
  1348. memdc.SelectObject(&brShadow);
  1349. memdc.BitBlt(0, 0, cx, cy, &patdc, 0, 0, NEWROP);
  1350. dc.BitBlt(p.x, p.y, cx, cy, &memdc, 0, 0, SRCCOPY);
  1351.     // reset DCs
  1352. patdc.SelectObject(pddcOldBmp);
  1353. patdc.DeleteDC();
  1354. memdc.SelectObject(pOldBrush);
  1355. memdc.SelectObject(pdcOldBmp);
  1356. memdc.DeleteDC();
  1357. brShadow.DeleteObject();
  1358. brHilight.DeleteObject();
  1359. patbmp.DeleteObject();
  1360. }