CoolMenu.cpp
上传用户:wlkj888
上传日期:2022-08-01
资源大小:806k
文件大小:37k
源码类别:

对话框与窗口

开发平台:

Visual C++

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