CoolMenu.cpp
上传用户:shilei2004
上传日期:2020-07-18
资源大小:83k
文件大小:34k
源码类别:

RichEdit

开发平台:

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