COOLMENU.CPP
上传用户:lvjun8202
上传日期:2013-04-30
资源大小:797k
文件大小:34k
源码类别:

SNMP编程

开发平台:

C/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. // ==========================================================================
  38. //
  39. /////////////////////////////////////////////////////////////////////////////
  40. #include "StdAfx.h"
  41. #include "CoolMenu.h"
  42. #ifdef _DEBUG
  43. #define new DEBUG_NEW
  44. #undef THIS_FILE
  45. static char THIS_FILE[] = __FILE__;
  46. #endif
  47. // helpers
  48. void PLFillRect(CDC& dc, const CRect& rc, COLORREF color);
  49. void PLDrawEmbossed(CDC& dc, CImageList& il, int i,
  50. CPoint p, BOOL bColor=FALSE);
  51. HBITMAP PLLoadSysColorBitmap(LPCTSTR lpResName, BOOL bMono=FALSE);
  52. inline HBITMAP PLLoadSysColorBitmap(UINT nResID, BOOL bMono=FALSE) {
  53. return PLLoadSysColorBitmap(MAKEINTRESOURCE(nResID), bMono);
  54. }
  55. // if you want to see extra TRACE diagnostics, set below to TRUE
  56. BOOL CCoolMenuManager::bTRACE = FALSE;
  57. #ifdef _DEBUG
  58. #define CMTRACEFN
  59. CTraceFn __fooble;
  60. if (bTRACE)
  61. TRACE
  62. #define CMTRACE
  63. if (bTRACE)
  64. TRACE
  65. #else
  66. #define CMTRACEFN TRACE
  67. #define CMTRACE   TRACE
  68. #endif
  69. // constants used for drawing
  70. const CXGAP = 1; // num pixels between button and text
  71. const CXTEXTMARGIN = 2; // num pixels after hilite to start text
  72. const CXBUTTONMARGIN = 2; // num pixels wider button is than bitmap
  73. const CYBUTTONMARGIN = 2; // ditto for height
  74. // DrawText flags
  75. const DT_MYSTANDARD = DT_SINGLELINE|DT_LEFT|DT_VCENTER;
  76. // identifies owner-draw data as mine
  77. const LONG MYITEMID = MAKELONG(MAKEWORD(_T('m'),_T('i')),MAKEWORD(_T('d'),_T('0')));
  78. // private struct: one of these for each owner-draw menu item
  79. struct CMyItemData {
  80. long magicNum; // magic number identifying me
  81. CString text; // item text
  82. UINT fType; // original item type flags
  83. int iButton; // index of button image in image list
  84. CMyItemData() { magicNum = MYITEMID; }
  85. ~CMyItemData() { text.Empty(); }
  86. BOOL IsMyItemData() { return magicNum == MYITEMID; }
  87. };
  88. IMPLEMENT_DYNAMIC(CCoolMenuManager, CSubclassWnd)
  89. CCoolMenuManager::CCoolMenuManager()
  90. {
  91. m_szBitmap = m_szButton = CSize(0,0); // will compute later
  92. m_bShowButtons = TRUE; // show buttons by default
  93. m_bAutoAccel = TRUE; // auto accelerators by default
  94. m_hAccel = NULL; // no accelerators loaded yet
  95. m_pAccel = NULL; // no accelerators loaded yet
  96. m_bUseDrawState = FALSE; // use DrawEmbossed by default
  97. m_bDrawDisabledButtonsInColor = FALSE; // use color for disabled buttons
  98. FixMFCDotBitmap();
  99. }
  100. CCoolMenuManager::~CCoolMenuManager()
  101. {
  102. // Jeremy Horgan [jeremyhorgan@hotmail.com]
  103. while (!m_menuList.IsEmpty()) 
  104. {
  105. ConvertMenu(CMenu::FromHandle((HMENU)m_menuList.RemoveHead()),0,     
  106. FALSE, FALSE);
  107. }
  108. Destroy();
  109. }
  110. //////////////////
  111. // Destroy everything. Called from destructor and Refresh.
  112. //
  113. void CCoolMenuManager::Destroy()
  114. {
  115. m_ilButtons.DeleteImageList();
  116. m_mapIDtoImage.RemoveAll();
  117. m_szBitmap = m_szButton = CSize(0,0);
  118. m_arToolbarID.RemoveAll();
  119. m_fontMenu.DeleteObject();
  120. DestroyAccel();
  121. }
  122. /////////////////
  123. // Destroy accelerators
  124. //
  125. void CCoolMenuManager::DestroyAccel()
  126. {
  127. m_mapIDtoAccel.RemoveAll(); // delete ACCEL entries in map
  128. safe_delete(m_pAccel); // delete current accelerators
  129. }
  130. //////////////////
  131. // Call this to install the menu manager. Install(NULL) to un-install.
  132. //
  133. void CCoolMenuManager::Install(CFrameWnd* pFrame)
  134. {
  135. ASSERT_VALID(pFrame);
  136. m_pFrame = pFrame;
  137. HookWindow(pFrame);   // install message hook
  138. }
  139. //////////////////
  140. // Load array of toolbar IDs.
  141. //
  142. BOOL CCoolMenuManager::LoadToolbars(const UINT* arID, int n)
  143. {
  144. // ASSERT(arID);
  145. BOOL bRet = TRUE;
  146. for (int i=0; i<n; i++)
  147. bRet |= LoadToolbar(arID[i]);
  148. return bRet;
  149. }
  150. // structure of RT_TOOLBAR resource
  151. struct TOOLBARDATA {
  152. WORD wVersion; // version # should be 1
  153. WORD wWidth; // width of one bitmap
  154. WORD wHeight; // height of one bitmap
  155. WORD wItemCount; // number of items
  156. WORD items[1]; // array of command IDs, actual size is wItemCount
  157. };
  158. //////////////////
  159. // Load one toolbar. Assumes bg color is gray.
  160. // 
  161. //  * add toolbar bitmap to image list
  162. //  * add each button ID to button map
  163. //
  164. BOOL CCoolMenuManager::LoadToolbar(UINT nIDToolbar)
  165. {
  166. // load bitmap
  167. HBITMAP hbmToolbar = PLLoadSysColorBitmap(nIDToolbar);
  168. if (!hbmToolbar) {
  169. TRACE(_T("*** Can't load bitmap for toolbar %d!n"), nIDToolbar);
  170. return FALSE;
  171. }
  172. CBitmap bmToolbar;
  173. bmToolbar.Attach(hbmToolbar); // destructor will detach & destroy
  174. // load toolbar
  175. LPTSTR lpResName = MAKEINTRESOURCE(nIDToolbar);
  176. HINSTANCE hInst;
  177. HRSRC hRsrc;
  178. TOOLBARDATA* ptbd;
  179. if ((hInst= AfxFindResourceHandle(lpResName, RT_TOOLBAR)) == NULL ||
  180.  (hRsrc= FindResource(hInst, lpResName, RT_TOOLBAR))   == NULL ||
  181.  (ptbd = (TOOLBARDATA*)LoadResource(hInst, hRsrc))     == NULL) {
  182. TRACE(_T("*** Can't load toolbar %d!n"), nIDToolbar);
  183. return FALSE;
  184. }
  185. // ASSERT(ptbd->wVersion==1);
  186. // OK, I have the bitmap and toolbar. 
  187. CSize sz(ptbd->wWidth, ptbd->wHeight);
  188. if (m_szBitmap.cx==0) {
  189. // First toolbar: initialized bitmap/button sizes and create image list.
  190. m_szBitmap = sz;
  191. m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
  192. VERIFY(m_ilButtons.Create(sz.cx, sz.cy, ILC_MASK, 0, 10));
  193. } else if (m_szBitmap != sz) {
  194. // button sizes different -- oops
  195. TRACE(_T("*** Toolbar %d button size differs!n"), nIDToolbar);
  196. return FALSE;
  197. }
  198. // I have a good toolbar: now add bitmap to the image list, and each
  199. // command ID to m_mapIDtoImage array. Note that LoadSysColorBitmap will
  200. // change gray -> COLOR_3DFACE, so use that for image list background.
  201. //
  202. int iNextImage = m_ilButtons.GetImageCount();
  203. m_ilButtons.Add(&bmToolbar, GetSysColor(COLOR_3DFACE));
  204. for (int i = 0; i < ptbd->wItemCount; i++) {
  205. UINT nID = ptbd->items[i];
  206. if (nID > 0) {
  207. if (GetButtonIndex(nID) >= 0) {
  208. TRACE(_T("*** Duplicate button ID %d ignoredn"), nID);
  209. } else {
  210. m_mapIDtoImage.SetAt(nID, (void*)iNextImage);
  211. TRACE(_T("CCoolMenuManager::LoadToolbar(). Added Menu Id %d, Button Number %dn"), nID, iNextImage-1);
  212. }
  213. // AB. 13-08-98. Since we've already added the entire toolbar to the imagelist
  214. // we need to increment nNextImage even if we didn't add this button to
  215. // m_mapIDtoImage.
  216. iNextImage++;
  217. }
  218. }
  219. m_arToolbarID.Add(nIDToolbar);  // remember toolbar ID for Refresh
  220. bmToolbar.Detach();
  221. return TRUE; // success!
  222. }
  223. //////////////////
  224. // Virtual CSubclassWnd window proc. All messages come here before frame
  225. // window. Isn't it cool? Just like in the old days!
  226. //
  227. LRESULT CCoolMenuManager::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  228. {
  229. switch(msg) {
  230. case WM_SYSCOLORCHANGE:
  231. case WM_SETTINGCHANGE:
  232. Refresh();
  233. break;
  234. case WM_MEASUREITEM:
  235. if (OnMeasureItem((MEASUREITEMSTRUCT*)lp))
  236. return TRUE; // handled
  237. break;
  238. case WM_DRAWITEM:
  239. if (OnDrawItem((DRAWITEMSTRUCT*)lp))
  240. return TRUE; // handled
  241. break;
  242. case WM_INITMENUPOPUP:
  243. // Very important: must let frame window handle it first!
  244. // Because if someone calls CCmdUI::SetText, MFC will change item to
  245. // MFT_STRING, so I must change back to MFT_OWNERDRAW.
  246. //
  247. CSubclassWnd::WindowProc(msg, wp, lp);
  248. OnInitMenuPopup(CMenu::FromHandle((HMENU)wp),
  249. (UINT)LOWORD(lp), (BOOL)HIWORD(lp));
  250. return 0;
  251. case WM_MENUSELECT:
  252. OnMenuSelect((UINT)LOWORD(wp), (UINT)HIWORD(wp), (HMENU)lp);
  253. break;
  254. case WM_MENUCHAR:
  255. LRESULT lr = OnMenuChar((TCHAR)LOWORD(wp), (UINT)HIWORD(wp),
  256. CMenu::FromHandle((HMENU)lp));
  257. if (lr!=0)
  258. return lr;
  259. break;
  260. }
  261. return CSubclassWnd::WindowProc(msg, wp, lp);
  262. }
  263. //////////////////
  264. // Refresh all colors, fonts, etc. For WM_SETTINGCHANGE, WM_SYSCOLORCHANGE.
  265. //
  266. void CCoolMenuManager::Refresh()
  267. {
  268. // first copy list (array) of toolbar IDs now loaded.
  269. CUIntArray arToolbarID;
  270. arToolbarID.Copy(m_arToolbarID);
  271. // destroy everything
  272. Destroy();
  273. // re-load toolbars.
  274. int nToolbars = arToolbarID.GetSize();
  275. for (int i = 0; i < nToolbars; i++)
  276. LoadToolbar(arToolbarID[i]);
  277. }
  278. //////////////////
  279. // Get menu font, creating if needed
  280. //
  281. CFont* CCoolMenuManager::GetMenuFont()
  282. {
  283. if (!(HFONT)m_fontMenu) {
  284. NONCLIENTMETRICS info;
  285. info.cbSize = sizeof(info);
  286. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  287. VERIFY(m_fontMenu.CreateFontIndirect(&info.lfMenuFont));
  288. }
  289. return &m_fontMenu;
  290. }
  291. //////////////////
  292. // Handle WM_MEASUREITEM on behalf of frame: compute menu item size.
  293. //
  294. BOOL CCoolMenuManager::OnMeasureItem(LPMEASUREITEMSTRUCT lpms)
  295. {
  296. // ASSERT(lpms);
  297. CMyItemData* pmd = (CMyItemData*)lpms->itemData;
  298. // ASSERT(pmd);
  299. if (lpms->CtlType != ODT_MENU || !pmd->IsMyItemData())
  300. return FALSE; // not handled by me
  301. if (pmd->fType & MFT_SEPARATOR) {
  302. // separator: use half system height and zero width
  303. lpms->itemHeight = GetSystemMetrics(SM_CYMENU)>>1;
  304. lpms->itemWidth  = 0;
  305. } else {
  306. // compute size of text: use DrawText with DT_CALCRECT
  307. CWindowDC dc(NULL); // screen DC--I won't actually draw on it
  308. CRect rcText(0,0,0,0);
  309. CFont* pOldFont = dc.SelectObject(GetMenuFont());
  310. dc.DrawText(pmd->text, rcText, DT_MYSTANDARD|DT_CALCRECT);
  311. dc.SelectObject(pOldFont);
  312. // height of item is just height of a standard menu item
  313. lpms->itemHeight= max(GetSystemMetrics(SM_CYMENU), rcText.Height());
  314. // width is width of text plus a bunch of stuff
  315. int cx = rcText.Width(); // text width 
  316. cx += CXTEXTMARGIN<<1; // L/R margin for readability
  317. cx += CXGAP; // space between button and menu text
  318. cx += m_szButton.cx<<1; // button width (L=button; R=empty margin)
  319. // whatever value I return in lpms->itemWidth, Windows will add the
  320. // width of a menu checkmark, so I must subtract to defeat Windows. Argh.
  321. //
  322. cx -= GetSystemMetrics(SM_CXMENUCHECK)-1;
  323. lpms->itemWidth = cx; // done deal
  324. CMTRACE(_T("OnMeasureItem for '%s':tw=%d h=%dn"), (LPCTSTR)pmd->text,
  325. lpms->itemWidth, lpms->itemHeight);
  326. }
  327. return TRUE; // handled
  328. }
  329. /////////////////
  330. // Handle WM_DRAWITEM on behalf of frame. Note: in all that goes
  331. // below, can't assume rcItem.left=0 because of multi-column menus!
  332. //
  333. BOOL CCoolMenuManager::OnDrawItem(LPDRAWITEMSTRUCT lpds)
  334. {
  335. // ASSERT(lpds);
  336. // Omar L Francisco
  337. if (lpds->CtlType != ODT_MENU)
  338. return FALSE;
  339. // Omar L Francisco
  340. CMyItemData* pmd = (CMyItemData*)lpds->itemData;
  341. // ASSERT(pmd);
  342. if (!pmd->IsMyItemData())
  343. return FALSE;
  344. // ASSERT(lpds->itemAction != ODA_FOCUS);
  345. // ASSERT(lpds->hDC);
  346. CDC dc;
  347. dc.Attach(lpds->hDC);
  348. const CRect& rcItem = lpds->rcItem;
  349. if (pmd->fType & MFT_SEPARATOR) {
  350. // draw separator
  351. CRect rc = rcItem; // copy rect
  352. rc.top += rc.Height()>>1; // vertical center
  353. dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP); // draw separator line
  354. } else { // not a separator
  355. CMTRACE(_T("OnDrawItem for '%s':tw=%d h=%dn"), (LPCTSTR)pmd->text,
  356. rcItem.Width(), rcItem.Height());
  357. BOOL bDisabled = lpds->itemState & ODS_GRAYED;
  358. BOOL bSelected = lpds->itemState & ODS_SELECTED;
  359. BOOL bChecked  = lpds->itemState & ODS_CHECKED;
  360. BOOL bHaveButn=FALSE;
  361. // Paint button, or blank if none
  362. CRect rcButn(rcItem.TopLeft(), m_szButton); // button rect
  363. rcButn += CPoint(0, // center vertically
  364. (rcItem.Height() - rcButn.Height())>>1 );
  365. int iButton = pmd->iButton;
  366. if (iButton >= 0) {
  367. // this item has a button!
  368. bHaveButn = TRUE;
  369. // compute point to start drawing
  370. CSize sz = rcButn.Size() - m_szBitmap;
  371. sz.cx >>= 1;
  372. sz.cy >>= 1;
  373. CPoint p(rcButn.TopLeft() + sz);
  374. // draw disabled or normal
  375. if (!bDisabled) {
  376. // normal: fill BG depending on state
  377. PLFillRect(dc, rcButn, GetSysColor(
  378. (bChecked && !bSelected) ? COLOR_3DLIGHT : COLOR_MENU));
  379. // draw pushed-in or popped-out edge
  380. if (bSelected || bChecked) {
  381. CRect rc2 = rcButn;
  382. dc.DrawEdge(rc2, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER,
  383. BF_RECT);
  384. }
  385. // draw the button!
  386. m_ilButtons.Draw(&dc, iButton, p, ILD_TRANSPARENT);
  387. } else if (m_bUseDrawState) {
  388. // use DrawState to draw disabled button: must convert to icon
  389. HICON hIcon=m_ilButtons.ExtractIcon(iButton);
  390. // ASSERT(hIcon);
  391. dc.DrawState(p, CSize(0,0), hIcon, DSS_DISABLED, (HBRUSH)NULL);
  392. DestroyIcon(hIcon);
  393. } else
  394. // use DrawEmbossed to draw disabeld button, w/color flag
  395. PLDrawEmbossed(dc, m_ilButtons, iButton, p,
  396. m_bDrawDisabledButtonsInColor);
  397. } else {
  398. // no button: look for custom checked/unchecked bitmaps
  399. CMenuItemInfo info;
  400. info.fMask = MIIM_CHECKMARKS;
  401. GetMenuItemInfo((HMENU)lpds->hwndItem,
  402. lpds->itemID, MF_BYCOMMAND, &info);
  403. if (bChecked || info.hbmpUnchecked) {
  404. bHaveButn = Draw3DCheckmark(dc, rcButn, bSelected,
  405. bChecked ? info.hbmpChecked : info.hbmpUnchecked);
  406. }
  407. }
  408. // Done with button, now paint text. First do background if needed.
  409. int cxButn = m_szButton.cx; // width of button
  410. COLORREF colorBG = GetSysColor(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU);
  411. if (bSelected || lpds->itemAction==ODA_SELECT) {
  412. // selected or selection state changed: paint text background
  413. CRect rcBG = rcItem; // whole rectangle
  414. if (bHaveButn) // if there's a button:
  415. rcBG.left += cxButn + CXGAP; //  don't paint over it!
  416. PLFillRect(dc, rcBG, colorBG); // paint it!
  417. }
  418. // compute text rectangle and colors
  419. CRect rcText = rcItem;  // start w/whole item
  420. rcText.left += cxButn + CXGAP + CXTEXTMARGIN; // left margin
  421. rcText.right -= cxButn;  // right margin
  422. dc.SetBkMode(TRANSPARENT);  // paint transparent text
  423. COLORREF colorText = GetSysColor(bDisabled ?  COLOR_GRAYTEXT :
  424. bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
  425. // Now paint menu item text. No need to select font,
  426. // because windows sets it up before sending WM_DRAWITEM
  427. //
  428. if (bDisabled && (!bSelected || colorText == colorBG)) {
  429. // disabled: draw hilite text shifted southeast 1 pixel for embossed
  430. // look. Don't do it if item is selected, tho--unless text color same
  431. // as menu highlight color. Got it?
  432. //
  433. DrawMenuText(dc, rcText + CPoint(1,1), pmd->text,
  434. GetSysColor(COLOR_3DHILIGHT));
  435. }
  436. DrawMenuText(dc, rcText, pmd->text, colorText); // finally!
  437. }
  438. dc.Detach();
  439. return TRUE; // handled
  440. }
  441. /////////////////
  442. // Helper function to draw justified menu text. If the text contains a TAB,
  443. // draw everything after the tab right-aligned
  444. //
  445. void CCoolMenuManager::DrawMenuText(CDC& dc, CRect rc, CString text,
  446. COLORREF color)
  447. {
  448. CString left = text;
  449. CString right;
  450. int iTabPos = left.Find(_T('t'));
  451. if (iTabPos >= 0) {
  452. right = left.Right(left.GetLength() - iTabPos - 1);
  453. left  = left.Left(iTabPos);
  454. }
  455. dc.SetTextColor(color);
  456. dc.DrawText(left, &rc, DT_MYSTANDARD);
  457. if (iTabPos > 0)
  458. dc.DrawText(right, &rc, DT_MYSTANDARD|DT_RIGHT);
  459. }
  460. #ifndef OBM_CHECK
  461. #define OBM_CHECK 32760 // from winuser.h
  462. #endif
  463. //////////////////
  464. // Draw 3D checkmark
  465. //
  466. // dc device context to draw in
  467. // rc rectangle to center bitmap in
  468. // bSelected TRUE if button is also selected
  469. // hbmCheck Checkmark bitmap to use, or NULL for default
  470. //
  471. BOOL CCoolMenuManager::Draw3DCheckmark(CDC& dc, const CRect& rc,
  472. BOOL bSelected, HBITMAP hbmCheck)
  473. {
  474. // get checkmark bitmap if none, use Windows standard
  475. if (!hbmCheck) {
  476. CBitmap bm;
  477. VERIFY(bm.LoadOEMBitmap(OBM_CHECK));
  478. hbmCheck = (HBITMAP)bm.Detach();
  479. // ASSERT(hbmCheck);
  480. }
  481. // center bitmap in caller's rectangle
  482. BITMAP bm;
  483. ::GetObject(hbmCheck, sizeof(bm), &bm);
  484. int cx = bm.bmWidth;
  485. int cy = bm.bmHeight;
  486. CRect rcDest = rc;
  487. CPoint p(0,0);
  488. CSize delta(CPoint((rc.Width() - cx)/2, (rc.Height() - cy)/2));
  489. if (rc.Width() > cx)
  490. rcDest = CRect(rc.TopLeft() + delta, CSize(cx, cy));
  491. else
  492. p -= delta;
  493. // select checkmark into memory DC
  494. CDC memdc;
  495. memdc.CreateCompatibleDC(&dc);
  496. HBITMAP hOldBM = (HBITMAP)::SelectObject(memdc, hbmCheck);
  497. // set BG color based on selected state
  498. COLORREF colorOld =
  499. dc.SetBkColor(GetSysColor(bSelected ? COLOR_MENU : COLOR_3DLIGHT));
  500. dc.BitBlt(rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(),
  501. &memdc, p.x, p.y, SRCCOPY);
  502. dc.SetBkColor(colorOld);
  503. ::SelectObject(memdc, hOldBM); // restore
  504. // draw pushed-in hilight.
  505. if (rc.Width() > cx) // if room:
  506. rcDest.InflateRect(1,1); // inflate checkmark by one pixel all around
  507. dc.DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT);
  508. return TRUE;
  509. }
  510. //////////////////
  511. // Handle WM_INITMENUPOPUP on behalf of frame.
  512. //
  513. void CCoolMenuManager::OnInitMenuPopup(CMenu* pMenu,
  514. UINT nIndex, BOOL bSysMenu)
  515. {
  516. if (m_bAutoAccel)
  517. {
  518. // check for new accels. If ASSERT bombs,
  519. // you forgot to call Install.
  520. ASSERT_VALID(m_pFrame);
  521. HACCEL hAccel = m_pFrame->GetDefaultAccelerator();
  522. if (hAccel != m_hAccel)
  523. LoadAccel(hAccel);
  524. // 12 Nov 1998 - Wang Jun - Fixes debug assert in system menu.
  525. // Check if click system menu.
  526. if (!bSysMenu) {
  527. ConvertMenu(pMenu, nIndex, bSysMenu, m_bShowButtons);
  528. }
  529. }
  530. ConvertMenu(pMenu, nIndex, bSysMenu, m_bShowButtons);
  531. }
  532. //////////////////
  533. // Set the accelerator table used to generate automatic key
  534. // names in menus. Delete previous table if any.
  535. //
  536. void CCoolMenuManager::LoadAccel(HACCEL hAccel)
  537. {
  538. DestroyAccel();
  539. int nAccel;
  540. if (hAccel && (nAccel = CopyAcceleratorTable(hAccel, NULL, 0)) > 0) {
  541. m_pAccel = new ACCEL [nAccel];
  542. // ASSERT(m_pAccel);
  543. CopyAcceleratorTable(hAccel, m_pAccel, nAccel);
  544. // Now I have the accelerators. Look over list, linking each command
  545. // ID with its ACCEL structure--i.e., m_mapIDtoAccel[nID] = ACCEL for
  546. // that ID. If more than one ACCEL for a given command (command has more
  547. // than one shortcut), fix up so ACCEL.cmd is offset of prev ACCEL
  548. // 
  549. for (int i=0; i<nAccel; i++) {
  550. ACCEL& ac = m_pAccel[i];
  551. ACCEL* pAccel = GetAccel(ac.cmd);
  552. m_mapIDtoAccel.SetAt(ac.cmd, &ac);
  553. ac.cmd = pAccel ? &ac - pAccel : 0; // ac.cmd = offset of prev, or 0
  554. }
  555. }
  556. }
  557. //////////////////
  558. // This rather gnarly function is used both to convert the menu from strings to
  559. // owner-draw and vice versa. In either case, it also appends automagic
  560. // accelerator key names to the menu items, if m_bAutoAccel is TRUE.
  561. //
  562. void CCoolMenuManager::ConvertMenu(CMenu* pMenu,
  563. UINT nIndex, BOOL bSysMenu, BOOL bShowButtons)
  564. {
  565. ASSERT_VALID(pMenu);
  566. CString sItemName;
  567. UINT nItem = pMenu->GetMenuItemCount();
  568. for (UINT i = 0; i < nItem; i++) {
  569. // loop over each item in menu
  570. // get menu item info
  571. char itemname[256];
  572. CMenuItemInfo info;
  573. info.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_ID
  574. | MIIM_TYPE;
  575. info.dwTypeData = itemname;
  576. info.cch = sizeof(itemname);
  577. ::GetMenuItemInfo(*pMenu, i, TRUE, &info);
  578. CMyItemData* pmd = (CMyItemData*)info.dwItemData;
  579. if (pmd && !pmd->IsMyItemData()) {
  580. CMTRACE(_T("CCoolMenuManager: ignoring foreign owner-draw itemn"));
  581. continue; 
  582. // owner-draw menu item isn't mine--leave it alone
  583. }
  584. // Koji MATSUNAMI 1999.2.23
  585. //
  586. if (bSysMenu && info.wID >= 0xF000) {
  587. CMTRACE(_T("CCoolMenuManager: ignoring sys menu itemn"));
  588. continue; // don't do for system menu commands
  589. }
  590. // now that I have the info, I will modify it
  591. info.fMask = 0; // assume nothing to change
  592. if (bShowButtons) {
  593. // I'm showing buttons: convert to owner-draw
  594. if (!(info.fType & MFT_OWNERDRAW)) {
  595. // If not already owner-draw, make it so. NOTE: If app calls
  596. // pCmdUI->SetText to change the text of a menu item, MFC will
  597. // turn the item to MFT_STRING. So I must set it back to
  598. // MFT_OWNERDRAW again. In this case, the menu item data (pmd)
  599. // will still be there.
  600. // 
  601. info.fType |= MFT_OWNERDRAW;
  602. info.fMask |= MIIM_TYPE;
  603. if (!pmd) { // if no item data:
  604. pmd = new CMyItemData; //   create one
  605. // ASSERT(pmd); //   (I hope)
  606. pmd->fType = info.fType; //   handy when drawing
  607. pmd->iButton = GetButtonIndex(info.wID);
  608. info.dwItemData = (DWORD)pmd; //   set in menu item data
  609. info.fMask |= MIIM_DATA; //   set item data
  610. }
  611. pmd->text = info.dwTypeData; // copy menu item string
  612. }
  613. // now add the menu to list of "converted" menus
  614. HMENU hmenu = pMenu->GetSafeHmenu();
  615. // ASSERT(hmenu);
  616. if (!m_menuList.Find(hmenu))
  617. m_menuList.AddHead(hmenu);
  618. // append accelerators to menu item name
  619. if (m_pAccel && m_bAutoAccel)
  620. AppendAccelName(pmd->text, info.wID);
  621. } else {
  622. // no buttons -- I'm converting to strings
  623. if (info.fType & MFT_OWNERDRAW) { // if ownerdraw:
  624. info.fType &= ~MFT_OWNERDRAW; //   turn it off
  625. info.fMask |= MIIM_TYPE; //   change item type
  626. // ASSERT(pmd); //   sanity check
  627. sItemName = pmd->text; //   save name before deleting pmd
  628. } else // otherwise:
  629. sItemName = info.dwTypeData; //   use name from MENUITEMINFO
  630. if (pmd) {
  631. // NOTE: pmd (item data) could still be left hanging around even
  632. // if MFT_OWNERDRAW is not set, in case mentioned above where app
  633. // calls pCmdUI->SetText to set text of item and MFC sets the type
  634. // to MFT_STRING.
  635. //
  636. info.dwItemData = NULL; // item data is NULL
  637. info.fMask |= MIIM_DATA; // change it
  638. safe_delete(pmd); // and item data too
  639. }
  640. // possibly add accelerator name
  641. if (m_pAccel  && m_bAutoAccel && AppendAccelName(sItemName, info.wID))
  642. info.fMask |= MIIM_TYPE; //  change item type (string)
  643. if (info.fMask & MIIM_TYPE) {
  644. // if setting name, copy name from CString to buffer and set cch
  645. strncpy(itemname, sItemName, sizeof(itemname));
  646. info.dwTypeData = itemname;
  647. info.cch = sItemName.GetLength();
  648. }
  649. }
  650. // if after all the above, there is anything to change, change it
  651. if (info.fMask) {
  652. CMTRACE(_T("Converting '%s' to %sn"), itemname,
  653. (info.fType & MFT_OWNERDRAW) ? _T("OWNERDRAW") : _T("STRING"));
  654. SetMenuItemInfo(*pMenu, i, TRUE, &info);
  655. }
  656. }
  657. }
  658. //////////////////
  659. // User typed a char into menu. Look for item with & preceeding the char typed.
  660. //
  661. LRESULT CCoolMenuManager::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu)
  662. {
  663. ASSERT_VALID(pMenu);
  664. UINT iCurrentItem = (UINT)-1; // guaranteed higher than any command ID
  665. CUIntArray arItemsMatched; // items that match the character typed
  666. UINT nItem = pMenu->GetMenuItemCount();
  667. for (UINT i=0; i< nItem; i++) {
  668. // get menu info
  669. CMenuItemInfo info;
  670. info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE;
  671. ::GetMenuItemInfo(*pMenu, i, TRUE, &info);
  672. CMyItemData* pmd = (CMyItemData*)info.dwItemData;
  673. if ((info.fType & MFT_OWNERDRAW) && pmd && pmd->IsMyItemData()) {
  674. CString& text = pmd->text;
  675. int iAmpersand = text.Find(_T('&'));
  676. if (iAmpersand >=0 && toupper(nChar)==toupper(text[iAmpersand+1]))
  677. arItemsMatched.Add(i);
  678. }
  679. if (info.fState & MFS_HILITE)
  680. iCurrentItem = i; // note index of current item
  681. }
  682. // arItemsMatched now contains indexes of items that match the char typed.
  683. //
  684. //   * if none: beep
  685. //   * if one:  execute it
  686. //   * if more than one: hilite next
  687. //
  688. UINT nFound = arItemsMatched.GetSize();
  689. if (nFound == 0)
  690. return 0;
  691. else if (nFound==1)
  692. return MAKELONG(arItemsMatched[0], MNC_EXECUTE);
  693. // more than one found--return 1st one past current selected item;
  694. UINT iSelect = 0;
  695. for (i=0; i < nFound; i++) {
  696. if (arItemsMatched[i] > iCurrentItem) {
  697. iSelect = i;
  698. break;
  699. }
  700. }
  701. return MAKELONG(arItemsMatched[iSelect], MNC_SELECT);
  702. }
  703. //////////////////
  704. // Handle WM_MENUSELECT: check for menu closed
  705. //
  706. void CCoolMenuManager::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
  707. {
  708. if (hSysMenu==NULL && nFlags==0xFFFF) {
  709. // Windows has closed the menu: restore all menus to original state
  710. while (!m_menuList.IsEmpty()) {
  711. ConvertMenu(CMenu::FromHandle((HMENU)m_menuList.RemoveHead()),
  712. 0, FALSE, FALSE);
  713. }
  714. }
  715. }
  716. // fix for virtual key names - Seain Conover (1999/01/09) 
  717. CString CCoolMenuManager::GetVirtualKeyName( const CString strVirtKey ) const
  718. {
  719. CString strResult;
  720. if ( strVirtKey == _T("Num 7")) { strResult = _T("Home"); }
  721. else if ( strVirtKey == _T("Num 1")) { strResult = _T("End");  }
  722. else if ( strVirtKey == _T("Num 9")) { strResult = _T("PgUp"); }
  723. else if ( strVirtKey == _T("Num 3")) { strResult = _T("PgDn"); }
  724. else if ( strVirtKey == _T("Num 0")) { strResult = _T("Ins");  }
  725. else if ( strVirtKey == _T("Num Del")) { strResult = _T("Del");  }
  726. else
  727. {
  728. strResult = strVirtKey;
  729. }
  730. return strResult;
  731. }
  732. //////////////////
  733. // Append the name of accelerator for given command ID to menu string.
  734. // sItemName is menu item name, which will have the accelerator appended.
  735. // For example, might call with sItemName = "File &Open" and return with
  736. // sItemName = "File &OpentCtrl-O". Returns BOOL = whether string changed.
  737. //
  738. BOOL CCoolMenuManager::AppendAccelName(CString& sItemName, UINT nID)
  739. {
  740. int iTabPos = sItemName.Find(_T('t'));
  741. if (iTabPos > 0)
  742. sItemName = sItemName.Left(iTabPos);
  743. BOOL bFound = FALSE;
  744. for (ACCEL* pa = GetAccel(nID); pa; pa -= pa->cmd) {
  745. sItemName += bFound ? _T(", ") : _T("t");
  746. if (pa->fVirt & FALT) sItemName += _T("Alt+");
  747. if (pa->fVirt & FCONTROL) sItemName += _T("Ctrl+");
  748. if (pa->fVirt & FSHIFT) sItemName += _T("Shift+");
  749. if (pa->fVirt & FVIRTKEY) {
  750. TCHAR keyname[64];
  751. UINT vkey = MapVirtualKey(pa->key, 0)<<16;
  752. GetKeyNameText(vkey, keyname, sizeof(keyname));
  753. // Seain Conover (1999/01/09) 
  754. sItemName += GetVirtualKeyName( keyname );
  755. } else
  756. sItemName += (char)pa->key;
  757. bFound = TRUE;
  758. if (pa->cmd == 0)
  759. break;
  760. }
  761. return bFound;
  762. }
  763. //////////////////
  764. // This function fixes MFC's diseased dot bitmap used for
  765. // "radio-style" menu items (CCmdUI->SetRadio), which is completely
  766. // wrong if the menu font is large. 
  767. //
  768. void CCoolMenuManager::FixMFCDotBitmap()
  769. {
  770. HBITMAP hbmDot = GetMFCDotBitmap();
  771. if (hbmDot) {
  772. // Draw a centered dot of appropriate size
  773. BITMAP bm;
  774. ::GetObject(hbmDot, sizeof(bm), &bm);
  775. CRect rcDot(0,0, bm.bmWidth, bm.bmHeight);
  776. rcDot.DeflateRect((bm.bmWidth>>1)-2, (bm.bmHeight>>1)-2);
  777. CWindowDC dcScreen(NULL);
  778. CDC memdc;
  779. memdc.CreateCompatibleDC(&dcScreen);
  780. int nSave = memdc.SaveDC();
  781. memdc.SelectStockObject(BLACK_PEN);
  782. memdc.SelectStockObject(BLACK_BRUSH);
  783. memdc.SelectObject((HGDIOBJ)hbmDot);
  784. memdc.PatBlt(0, 0, bm.bmWidth, bm.bmHeight, WHITENESS);
  785. memdc.Ellipse(&rcDot);
  786. memdc.RestoreDC(nSave);
  787. }
  788. }
  789. //////////////////
  790. // This function gets MFC's dot bitmap.
  791. //
  792. HBITMAP CCoolMenuManager::GetMFCDotBitmap()
  793. {
  794. // The bitmap is stored in afxData.hbmMenuDot, but afxData is MFC-private,
  795. // so the only way to get it is create a menu, set the radio check,
  796. // and then see what bitmap MFC set in the menu item.
  797. CMenu menu;
  798. VERIFY(menu.CreateMenu());
  799. VERIFY(menu.AppendMenu(MFT_STRING, 0, (LPCTSTR)NULL));
  800. CCmdUI cui;
  801. cui.m_pMenu = &menu;
  802. cui.m_nIndex = 0;
  803. cui.m_nIndexMax = 1;
  804. cui.SetRadio(TRUE);
  805. CMenuItemInfo info;
  806. info.fMask = MIIM_CHECKMARKS;
  807. GetMenuItemInfo(menu, 0, MF_BYPOSITION, &info);
  808. HBITMAP hbmDot = info.hbmpChecked;
  809. menu.DestroyMenu();
  810. return hbmDot;
  811. }
  812. // Peter Tewkesbury
  813. BOOL CCoolMenuManager::AddSingleBitmap(UINT nBitmapID, UINT n, UINT *nID)
  814. {
  815. // load bitmap
  816. HBITMAP hbmBitmap = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  817. MAKEINTRESOURCE(nBitmapID), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
  818. // ASSERT(hbmBitmap);
  819. if (!hbmBitmap) {
  820. TRACE(_T("*** Can't load bitmap %d!n"), nBitmapID);
  821. return FALSE;
  822. }
  823. // Assign Bitmap to CBitmap
  824. CBitmap bmBitmap;
  825. bmBitmap.Attach(hbmBitmap); // destructor will detach & destroy
  826. // OK, I have the bitmap - Check that Bitmaps are correct size.
  827. if (m_szBitmap.cx==0) 
  828. {
  829. // First toolbar: initialized bitmap/button sizes and create image list.
  830. CSize sz(16,15);
  831. m_szBitmap = sz;
  832. m_szButton = sz + CSize(CXBUTTONMARGIN<<1, CYBUTTONMARGIN<<1);
  833. VERIFY(m_ilButtons.Create(sz.cx, sz.cy, ILC_MASK, 0, 10));
  834. // Add Bitmap to ImageList
  835. int iNextImage = m_ilButtons.GetImageCount();
  836. m_ilButtons.Add(&bmBitmap, GetSysColor(COLOR_3DFACE)); 
  837. // Add ID to Map.
  838. for(UINT i=0;i<n;i++)
  839. {
  840. if (nID[i] > 0) 
  841. {
  842. if (GetButtonIndex(nID[i]) >= 0) 
  843. {
  844. TRACE(_T("*** Duplicate button ID %d ignoredn"), nID[i]);
  845. else m_mapIDtoImage.SetAt(nID[i], (void*)iNextImage++); 
  846. }
  847. }
  848. // All Done.
  849. return TRUE;
  850. }
  851. ////////////////////////////////////////////////////////////////
  852. // Helper functions
  853. //////////////////
  854. // Load a bitmap, converting the standard colors.
  855. // Calls AfxLoadSysColorBitmap to do the work.
  856. //
  857. // RGB(0x00, 0x00, 0x00) (black)      --> COLOR_BTNTEXT
  858. // RGB(0x80, 0x80, 0x80) (dark gray)  --> COLOR_3DSHADOW
  859. // RGB(0xC0, 0xC0, 0xC0) (gray)       --> COLOR_3DFACE
  860. // RGB(0xFF, 0xFF, 0xFF) (white)      --> COLOR_3DHILIGHT
  861. // 
  862. HBITMAP PLLoadSysColorBitmap(LPCTSTR lpResName, BOOL bMono)
  863. {
  864. HINSTANCE hInst = AfxFindResourceHandle(lpResName, RT_BITMAP);
  865. HRSRC hRsrc = ::FindResource(hInst, lpResName, RT_BITMAP);
  866. if (hRsrc == NULL)
  867. return NULL;
  868. return AfxLoadSysColorBitmap(hInst, hRsrc, bMono);
  869. }
  870. //////////////////
  871. // Shorthand to fill a rectangle with a solid color.
  872. //
  873. void PLFillRect(CDC& dc, const CRect& rc, COLORREF color)
  874. {
  875. CBrush brush(color);
  876. CBrush* pOldBrush = dc.SelectObject(&brush);
  877. dc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
  878. dc.SelectObject(pOldBrush);
  879. }
  880. // This is the magic ROP code used to generate the embossed look for
  881. // a disabled button. It's listed in Appendix F of the Win32 Programmer's
  882. // Reference as PSDPxax (!) which is a cryptic reverse-polish notation for
  883. //
  884. // ((Destination XOR Pattern) AND Source) XOR Pattern
  885. //
  886. // which I leave to you to figure out. In the case where I apply it,
  887. // Source is a monochrome bitmap which I want to draw in such a way that
  888. // the black pixels get transformed to the brush color and the white pixels
  889. // draw transparently--i.e. leave the Destination alone.
  890. //
  891. // black ==> Pattern (brush)
  892. // white ==> Destintation (ie, transparent)
  893. //
  894. // 0xb8074a is the ROP code that does this. For more info, see Charles
  895. // Petzold, _Programming Windows_, 2nd Edition, p 622-624.
  896. //
  897. #define TRANSPARENTROP 0xb8074a
  898. //////////////////
  899. // Draw an image with the embossed (disabled) look.
  900. //
  901. // dc device context to draw in
  902. // il image list containing image
  903. // i index of image to draw
  904. // p point in dc to draw image at
  905. //    bColor do color embossing. Default is B/W.
  906. //
  907. void PLDrawEmbossed(CDC& dc, CImageList& il, int i,
  908. CPoint p, BOOL bColor)
  909. {
  910. IMAGEINFO info;
  911. VERIFY(il.GetImageInfo(0, &info));
  912. CRect rc = info.rcImage;
  913. int cx = rc.Width();
  914. int cy = rc.Height();
  915. // create memory dc
  916. CDC memdc;
  917. memdc.CreateCompatibleDC(&dc);
  918. // create mono or color bitmap
  919. CBitmap bm;
  920. if (bColor)
  921. bm.CreateCompatibleBitmap(&dc, cx, cy);
  922. else
  923. bm.CreateBitmap(cx, cy, 1, 1, NULL);
  924. // draw image into memory DC--fill BG white first
  925. CBitmap* pOldBitmap = memdc.SelectObject(&bm);
  926. memdc.PatBlt(0, 0, cx, cy, WHITENESS);
  927. il.Draw(&memdc, i, CPoint(0,0), ILD_TRANSPARENT);
  928. // This seems to be required. Why, I don't know. ???
  929. COLORREF colorOldBG = dc.SetBkColor(RGB(255,255,255)); // white
  930. // Draw using hilite offset by (1,1), then shadow
  931. CBrush brShadow(GetSysColor(COLOR_3DSHADOW));
  932. CBrush brHilite(GetSysColor(COLOR_3DHIGHLIGHT));
  933. CBrush* pOldBrush = dc.SelectObject(&brHilite);
  934. dc.BitBlt(p.x+1, p.y+1, cx, cy, &memdc, 0, 0, TRANSPARENTROP);
  935. dc.SelectObject(&brShadow);
  936. dc.BitBlt(p.x, p.y, cx, cy, &memdc, 0, 0, TRANSPARENTROP);
  937. dc.SelectObject(pOldBrush);
  938. dc.SetBkColor(colorOldBG);  // restore
  939. memdc.SelectObject(pOldBitmap);  // ...
  940. }