MenuXP.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. // CustMenu.cpp: implementation of the CMenuXP class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "MenuXP.h"
  6. #include "KeyHelper.h"
  7. #ifdef _DEBUG
  8. #undef THIS_FILE
  9. static char THIS_FILE[]=__FILE__;
  10. #define new DEBUG_NEW
  11. #endif
  12. // constants used for drawing
  13. const CXGAP = 0; // num pixels between button and text
  14. const CXTEXTMARGIN = 2; // num pixels after hilite to start text
  15. const CXBUTTONMARGIN = 2; // num pixels wider button is than bitmap
  16. //const CYBUTTONMARGIN = 10; // ditto for height
  17. // DrawText flags
  18. const DT_MYSTANDARD = DT_SINGLELINE|DT_LEFT|DT_VCENTER;
  19. //////////////////////////////////////////////////////////////////////
  20. // Construction/Destruction
  21. //////////////////////////////////////////////////////////////////////
  22. IMPLEMENT_DYNAMIC(CMenuXP, CMenu)
  23. CMenuXP::CMenuXP()
  24. {
  25. //initialize menu font with the default
  26. NONCLIENTMETRICS info;
  27. info.cbSize = sizeof(info);
  28. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  29. VERIFY(m_fontMenu.CreateFontIndirect(&info.lfMenuFont));
  30. //initialize colors with system default
  31. m_clrSelectedBar = RGB(43,43,43);
  32. m_clrDisabledText = ::GetSysColor(COLOR_GRAYTEXT);
  33. m_clrBackGround =RGB(200,200,200);
  34. // m_clrleftbar =RGB(153,153,153);
  35. m_clrSelectedText = RGB(230,230,230);
  36. m_clrText = RGB(0,0,0);
  37. m_clrIconArea = RGB(153,153,153);
  38. /*
  39. //initialize colors with system default
  40. m_clrBackGround = ::GetSysColor(COLOR_MENU);
  41. m_clrSelectedBar = ::GetSysColor(COLOR_HIGHLIGHT);
  42. m_clrSelectedText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  43. m_clrText = ::GetSysColor(COLOR_MENUTEXT);
  44. m_clrDisabledText = ::GetSysColor(COLOR_GRAYTEXT);
  45. m_clrIconArea = m_clrBackGround;
  46. */
  47. //initialize sidebar colors
  48. m_clrSideBarStart = RGB(0, 0, 192);
  49. m_clrSideBarEnd = RGB(0, 0, 0);
  50. //the default sytle is office style
  51. m_Style = STYLE_OFFICE;
  52. m_bBreak = false;
  53. m_bBreakBar = false;
  54. }
  55. CMenuXP::~CMenuXP()
  56. {
  57. m_fontMenu.DeleteObject();
  58. Clear();
  59. }
  60. void CMenuXP::MeasureItem( LPMEASUREITEMSTRUCT lpms )
  61. {
  62. if (lpms->CtlType != ODT_MENU)
  63. return;
  64. CMenuXPItem *pItem = (CMenuXPItem *)lpms->itemData;
  65. TRACE(_T("pItem: 0x%x"),(DWORD)pItem); //This line prevent boundschecker from issue a resource leak
  66. if (!pItem || !pItem->IsMyData())
  67. return;
  68. if (pItem->m_bSideBar)
  69. {
  70. lpms->itemWidth = pItem->m_nSize;
  71. lpms->itemHeight = 0;
  72. }
  73. else if (pItem->m_bSeparator)
  74. {
  75. // separator: use half system height and zero width
  76. lpms->itemHeight = ::GetSystemMetrics(SM_CYMENUCHECK)>>1;
  77. lpms->itemWidth = 0;
  78. }
  79. else
  80. {
  81. //calculate the size needed to draw the text: use DrawText with DT_CALCRECT
  82. CWindowDC dc(NULL); // screen DC--I won't actually draw on it
  83. CRect rcText(0,0,0,0);
  84. CFont* pOldFont = dc.SelectObject(&m_fontMenu);
  85. dc.DrawText(pItem->m_strText, rcText, DT_MYSTANDARD|DT_CALCRECT);
  86. dc.SelectObject(pOldFont);
  87. // the height of the item should be the maximun of the text and the button
  88. lpms->itemHeight = max(rcText.Height(), pItem->m_nSize + (CYBUTTONMARGIN<<1));
  89. if (pItem->m_bButtonOnly)
  90. { //for button only style, we set the item's width to be the same as its height
  91. lpms->itemWidth = lpms->itemHeight;
  92. }
  93. else
  94. {
  95. // width is width of text plus a bunch of stuff
  96. int cx = rcText.Width(); // text width
  97. cx += CXTEXTMARGIN<<1; // L/R margin for readability
  98. cx += CXGAP; // space between button and menu text
  99. cx += (pItem->m_nSize + CYBUTTONMARGIN * 2) <<1; // button width (L=button; R=empty margin)
  100. lpms->itemWidth = cx; // done deal
  101. }
  102. }
  103. // whatever value I return in lpms->itemWidth, Windows will add the
  104. // width of a menu checkmark, so I must subtract to defeat Windows. Argh.
  105. //
  106. lpms->itemWidth -= GetSystemMetrics(SM_CXMENUCHECK)-1;
  107. TRACE(_T("MeasureItem: ID(%d), Width(%d), Height(%d)\n"),
  108. lpms->itemID,
  109. lpms->itemWidth, lpms->itemHeight);
  110. }
  111. void CMenuXP::DrawItem( LPDRAWITEMSTRUCT lpds )
  112. {
  113. ASSERT(lpds);
  114. if (lpds->CtlType != ODT_MENU)
  115. return; // not handled by me
  116. CMenuXPItem * pItem = (CMenuXPItem *)lpds->itemData;
  117. if (!pItem)
  118. return;
  119. ASSERT(lpds->itemAction != ODA_FOCUS);
  120. ASSERT(lpds->hDC);
  121. CDC dc;
  122. dc.Attach(lpds->hDC);
  123. //get the drawing area
  124. CRect rcItem = lpds->rcItem;
  125. TRACE(_T("DrawItem: ID(%d), Widht(%d), Height(%d)\n"),
  126. lpds->itemID, rcItem.Width(), rcItem.Height());
  127. if (pItem->m_bSideBar)
  128. {
  129. CRect rcClipBox;
  130. dc.GetClipBox(rcClipBox);
  131. //before drawing the sidebar, we must fill the entire menu area with its backgroundcolor,
  132. //orelse, the breakbar area will remain the the default menu color
  133. //so, if you want to avoid strange color and don't want a sidebar, just add a sidebar with
  134. //zero width
  135. //dc.FillSolidRect(rcClipBox, m_Style==STYLE_XP? m_clrIconArea : m_clrBackGround);
  136. //draw the side bar
  137. CRect rc = rcItem;
  138. rc.top = rcClipBox.top;
  139. rc.bottom = rcClipBox.bottom;
  140. DrawSideBar(&dc, rc, pItem->m_hIcon, pItem->m_strText);
  141. }
  142. else if (pItem->m_bSeparator)
  143. {
  144. //draw background first
  145. DrawBackGround(&dc, rcItem, FALSE, FALSE);
  146. // draw the background
  147. CRect rc = rcItem; // copy rect
  148. rc.top += rc.Height()>>1; // vertical center
  149. dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP); // draw separator line
  150. // in XP mode, fill the icon area with the iconarea color
  151. if (m_Style == STYLE_XP)
  152. {
  153. CRect rcArea(rcItem.TopLeft(),
  154. CSize(pItem->m_nSize + (CYBUTTONMARGIN<<1),
  155. pItem->m_nSize + (CYBUTTONMARGIN<<1)));
  156. DrawIconArea(&dc, rcArea, FALSE, FALSE, FALSE);
  157. }
  158. }
  159. else
  160. {
  161. BOOL bDisabled = lpds->itemState & ODS_GRAYED;
  162. BOOL bSelected = lpds->itemState & ODS_SELECTED;
  163. BOOL bChecked = lpds->itemState & ODS_CHECKED;
  164. //draw the background first
  165. DrawBackGround(&dc, rcItem, bSelected, bDisabled);
  166. //Draw the icon area for XP style
  167. if (m_Style == STYLE_XP)
  168. {
  169. CRect rcArea(rcItem.TopLeft(), CSize(rcItem.Height(), rcItem.Height()));
  170. DrawIconArea(&dc, rcArea, bSelected, bDisabled, bChecked);
  171. }
  172. //draw the button, not the icon
  173. CRect rcButton(rcItem.TopLeft(), CSize(rcItem.Height(), rcItem.Height()));
  174. if (pItem->m_bButtonOnly)
  175. rcButton = rcItem;
  176. if (pItem->m_hIcon || bChecked)
  177. {
  178. DrawButton(&dc, rcButton, bSelected, bDisabled, bChecked);
  179. }
  180. //draw the icon actually
  181. if (pItem->m_hIcon)
  182. {
  183. CRect rcIcon = rcButton;
  184. int dt=(rcIcon.Width ()-16)/2;
  185. rcIcon.DeflateRect(dt, dt);
  186. rcIcon.right =rcIcon.left +16;
  187. rcIcon.bottom =rcIcon.top +16;
  188. DrawIcon(&dc, rcIcon, pItem->m_hIcon, bSelected, bDisabled);
  189. }
  190. else if (bChecked)
  191. {
  192. //draw the check mark
  193. CRect rcCheck = rcButton;
  194. rcCheck.DeflateRect(2, 2);
  195. DrawCheckMark(&dc, rcCheck, bSelected);
  196. }
  197. //draw text finally
  198. if (!pItem->m_bButtonOnly)
  199. {
  200. CRect rcText = rcItem; // start w/whole item
  201. rcText.left += rcButton.Width() + CXGAP + CXTEXTMARGIN; // left margin
  202. rcText.right -= pItem->m_nSize; // right margin
  203. DrawText(&dc, rcText, pItem->m_strText, bSelected, bDisabled, lpds->itemState&ODS_DEFAULT ? 1 : 0);
  204. }
  205. }
  206. dc.Detach();
  207. }
  208. //draw background
  209. void CMenuXP::DrawBackGround(CDC *pDC, CRect rect, BOOL bSelected, BOOL bDisabled)
  210. {
  211. if (bSelected)
  212. {
  213. //FillRect(pDC, rect, bDisabled? ((m_Style==STYLE_XP)?m_clrBackGround:m_clrSelectedBar) : m_clrSelectedBar);
  214. FillRect(pDC, rect, m_clrSelectedBar);
  215. CRect rc=rect;
  216. rc.right =rc.left +rc.Height ();
  217. FillRect(pDC, rc, RGB(82,82,82) );
  218. }
  219. else
  220. {
  221. FillRect(pDC, rect, m_clrBackGround);
  222. }
  223. //return;
  224. //in XP mode, draw a line rectangle around
  225. if (m_Style == STYLE_XP && bSelected && !bDisabled)
  226. {
  227. CGdiObject *pOldBrush = pDC->SelectStockObject(HOLLOW_BRUSH);
  228. CGdiObject *pOldPen = pDC->SelectStockObject(BLACK_PEN);
  229. pDC->Rectangle(rect);
  230. pDC->SelectObject(pOldBrush);
  231. pDC->SelectObject(pOldPen);
  232. }
  233. }
  234. //draw the icon button, the icon is not included
  235. void CMenuXP::DrawButton(CDC *pDC, CRect rect, BOOL bSelected, BOOL bDisabled, BOOL bChecked)
  236. {
  237. if (m_Style == STYLE_OFFICE)
  238. {
  239. // normal: fill BG depending on state
  240. FillRect(pDC, rect, (bChecked && !bSelected) ? m_clrBackGround+RGB(2, 2, 2) : m_clrBackGround);
  241. // draw pushed-in or popped-out edge
  242. if (!bDisabled && (bSelected || bChecked) )
  243. {
  244. pDC->DrawEdge(rect, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER,
  245. BF_RECT);
  246. }
  247. }
  248. else if (m_Style == STYLE_XP && !bSelected)
  249. {
  250. if (bChecked && !bDisabled)
  251. {
  252. DrawBackGround(pDC, rect, TRUE, FALSE);
  253. }
  254. }
  255. }
  256. //draw the icon area, the icon is not included, only in XP style
  257. void CMenuXP::DrawIconArea(CDC *pDC, CRect rect, BOOL bSelected, BOOL bDisabled, BOOL bChecked)
  258. {
  259. if (m_Style != STYLE_XP)
  260. return;
  261. // normal: fill BG depending on state
  262. if (!bSelected || bDisabled)
  263. {
  264. FillRect(pDC, rect, m_clrIconArea);
  265. }
  266. }
  267. //draw the icon
  268. void CMenuXP::DrawIcon(CDC *pDC, CRect rect, HICON hIcon, BOOL bSelected, BOOL bDisabled)
  269. {
  270. if (bDisabled)
  271. {
  272. DrawEmbossed(pDC, hIcon, rect);
  273. }
  274. else
  275. {
  276. ::DrawIconEx(pDC->m_hDC, rect.left, rect.top, hIcon,
  277. rect.Width(), rect.Height(), 0, NULL,
  278. DI_NORMAL);
  279. }
  280. }
  281. //draw the sidebar
  282. void CMenuXP::DrawSideBar(CDC *pDC, CRect rect, HICON hIcon, CString strText)
  283. {
  284. rect.right += 3; //fill the gap produced by the menubreak
  285. HBITMAP bmpBar = CreateGradientBMP(
  286. pDC->m_hDC, m_clrSideBarStart, m_clrSideBarEnd,
  287. rect.Width(), rect.Height(),
  288. 0, 256);
  289. if (bmpBar)
  290. {
  291. CDC memDC;
  292. memDC.CreateCompatibleDC(pDC);
  293. HBITMAP hOldBmp = (HBITMAP)::SelectObject(memDC.m_hDC, bmpBar);
  294. pDC->BitBlt(rect.left, rect.top,
  295. rect.Width(), rect.Height(),
  296. &memDC, 0, 0, SRCCOPY);
  297. ::SelectObject(memDC, hOldBmp);
  298. ::DeleteObject(bmpBar);
  299. }
  300. //Draw Sidebar text
  301. CFont vertFont;
  302. vertFont.CreateFont(16, 0, 900, 900, FW_BOLD,
  303. 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
  304. CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  305. DEFAULT_PITCH, _T("Arial"));
  306. CFont *pOldFont = pDC->SelectObject(&vertFont);
  307. COLORREF oldColor = pDC->GetTextColor();
  308. pDC->SetTextColor(RGB(255, 255, 255));
  309. pDC->SetBkMode(TRANSPARENT);
  310. pDC->TextOut(rect.left+2, rect.bottom-4, strText);
  311. pDC->SetTextColor(oldColor);
  312. pDC->SelectObject(pOldFont);
  313. vertFont.DeleteObject();
  314. }
  315. //draw the check mark
  316. void CMenuXP::DrawCheckMark(CDC *pDC, CRect rect, BOOL bSelected)
  317. {
  318. /* CBitmap bmp; //Check mark bitmap
  319. //"#define OEMRESOURCE" must be in the begining of your stdafx.h
  320. //for the LoadOEMBitmap to work
  321. VERIFY(bmp.LoadOEMBitmap(OBM_CHECK));
  322. // center bitmap in caller's rectangle
  323. BITMAP bm;
  324. bmp.GetBitmap(&bm);
  325. int cx = bm.bmWidth;
  326. int cy = bm.bmHeight;
  327. CRect rcDest = rect;
  328. CPoint p(0,0);
  329. CSize delta(CPoint((rect.Width() - cx)/2, (rect.Height() - cy)/2));
  330. if (rect.Width() > cx)
  331. rcDest = CRect(rect.TopLeft() + delta, CSize(cx, cy));
  332. else
  333. p -= delta;
  334. // select checkmark into memory DC
  335. CDC memdc;
  336. memdc.CreateCompatibleDC(pDC);
  337. CBitmap *pOldBmp = memdc.SelectObject(&bmp);
  338. COLORREF colorOld =
  339. pDC->SetBkColor(GetSysColor(bSelected ? COLOR_MENU : COLOR_3DLIGHT));
  340. pDC->BitBlt(rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(),
  341. &memdc, p.x, p.y, SRCCOPY);
  342. pDC->SetBkColor(colorOld);
  343. memdc.SelectObject(pOldBmp);
  344. bmp.DeleteObject();
  345. */
  346. CRect rcDest = rect;
  347. pDC->DrawFrameControl(rcDest, DFC_MENU, DFCS_MENUCHECK);
  348. }
  349. //Draw menu text
  350. void CMenuXP::DrawText(CDC *pDC, CRect rect, CString strText, BOOL bSelected, BOOL bDisabled, BOOL bBold)
  351. {
  352. CFont* pOldFont;
  353. CFont fontBold;
  354. if (bBold)
  355. {
  356. LOGFONT logFont;
  357. m_fontMenu.GetLogFont(&logFont);
  358. logFont.lfWeight = FW_BOLD;
  359. fontBold.CreateFontIndirect(&logFont);
  360. pOldFont = pDC->SelectObject(&fontBold);
  361. }
  362. else
  363. {
  364. pOldFont = pDC->SelectObject(&m_fontMenu);
  365. }
  366. pDC->SetBkMode(TRANSPARENT);
  367. if (bDisabled && (!bSelected || m_Style == STYLE_XP))
  368. {
  369. DrawMenuText(*pDC, rect + CPoint(1, 1), strText, m_clrSelectedText);
  370. }
  371. if (bDisabled)
  372. {
  373. DrawMenuText(*pDC, rect, strText, m_clrDisabledText);
  374. }
  375. else
  376. {
  377. DrawMenuText(*pDC, rect, strText, bSelected? m_clrSelectedText : m_clrText);
  378. }
  379. pDC->SelectObject(pOldFont);
  380. if (bBold)
  381. fontBold.DeleteObject();
  382. }
  383. //set menu font
  384. BOOL CMenuXP::SetMenuFont(LOGFONT lgfont)
  385. {
  386. m_fontMenu.DeleteObject();
  387. return m_fontMenu.CreateFontIndirect(&lgfont);
  388. }
  389. //clear all memory and handles
  390. void CMenuXP::Clear(void)
  391. {
  392. UINT nCount = GetMenuItemCount();
  393. for (UINT i=0; i<nCount; i++)
  394. {
  395. MENUITEMINFO info;
  396. memset(&info, 0, sizeof(MENUITEMINFO));
  397. info.cbSize = sizeof(MENUITEMINFO);
  398. info.fMask = MIIM_DATA | MIIM_TYPE;
  399. GetMenuItemInfo(i, &info, TRUE);
  400. CMenuXPItem *pData = (CMenuXPItem *)info.dwItemData;
  401. if ((info.fType & MFT_OWNERDRAW) && pData && pData->IsMyData())
  402. {
  403. delete pData;
  404. }
  405. CMenu *pSubMenu = GetSubMenu(i);
  406. if (pSubMenu && pSubMenu->IsKindOf(RUNTIME_CLASS(CMenuXP)))
  407. delete pSubMenu;
  408. }
  409. }
  410. //draw embossed icon for the disabled item
  411. const DWORD MAGICROP = 0xb8074a;
  412. const COLORREF CWHITE = RGB(255,255,255);
  413. void CMenuXP::DrawEmbossed(CDC *pDC, HICON hIcon, CRect rect, BOOL bColor)
  414. {
  415. CDC memdc;
  416. memdc.CreateCompatibleDC(pDC);
  417. int cx = rect.Width();
  418. int cy = rect.Height();
  419. // create mono or color bitmap
  420. CBitmap bm;
  421. if (bColor)
  422. bm.CreateCompatibleBitmap(pDC, cx, cy);
  423. else
  424. bm.CreateBitmap(cx, cy, 1, 1, NULL);
  425. // draw image into memory DC--fill BG white first
  426. CBitmap* pOldBitmap = memdc.SelectObject(&bm);
  427. //FillRect(&memdc, CRect(0, 0, cx, cy), m_clrBackGround);
  428. memdc.PatBlt(0, 0, cx, cy, WHITENESS);
  429. ::DrawIconEx(memdc.m_hDC, 0, 0, hIcon, cx, cy, 1, NULL, DI_NORMAL);
  430. // This seems to be required. Why, I don't know. ???
  431. COLORREF colorOldBG = pDC->SetBkColor(CWHITE);
  432. // Draw using hilite offset by (1,1), then shadow
  433. CBrush brShadow(GetSysColor(COLOR_3DSHADOW));
  434. CBrush brHilite(GetSysColor(COLOR_3DHIGHLIGHT));
  435. CBrush* pOldBrush = pDC->SelectObject(&brHilite);
  436. pDC->BitBlt(rect.left+1, rect.top+1, cx, cy, &memdc, 0, 0, MAGICROP);
  437. pDC->SelectObject(&brShadow);
  438. pDC->BitBlt(rect.left, rect.top, cx, cy, &memdc, 0, 0, MAGICROP);
  439. pDC->SelectObject(pOldBrush);
  440. pDC->SetBkColor(colorOldBG); // restore
  441. memdc.SelectObject(pOldBitmap); // ...
  442. bm.DeleteObject();
  443. brShadow.DeleteObject();
  444. brHilite.DeleteObject();
  445. }
  446. //////////////////
  447. // Shorthand to fill a rectangle with a solid color.
  448. //
  449. void CMenuXP::FillRect(CDC *pDC, const CRect& rc, COLORREF color)
  450. {
  451. CBrush brush(color);
  452. CBrush* pOldBrush = pDC->SelectObject(&brush);
  453. pDC->PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
  454. pDC->SelectObject(pOldBrush);
  455. brush.DeleteObject();
  456. }
  457. HBITMAP CMenuXP::CreateGradientBMP(HDC hDC,COLORREF cl1,COLORREF cl2,int nWidth,int nHeight,int nDir,int nNumColors)
  458. {
  459. if(nNumColors > 256)
  460. nNumColors = 256;
  461. COLORREF PalVal[256];
  462. memset(PalVal, 0, sizeof(COLORREF)*256);
  463. int nIndex;
  464. BYTE peRed=0,peGreen=0,peBlue=0;
  465. int r1=GetRValue(cl1);
  466. int r2=GetRValue(cl2);
  467. int g1=GetGValue(cl1);
  468. int g2=GetGValue(cl2);
  469. int b1=GetBValue(cl1);
  470. int b2=GetBValue(cl2);
  471. for (nIndex = 0; nIndex < nNumColors; nIndex++)
  472. {
  473. peRed = (BYTE) (r1 + MulDiv((r2-r1),nIndex,nNumColors-1));
  474. peGreen = (BYTE) (g1 + MulDiv((g2-g1),nIndex,nNumColors-1));
  475. peBlue = (BYTE) (b1 + MulDiv((b2-b1),nIndex,nNumColors-1));
  476. PalVal[nIndex]=(peRed << 16) | (peGreen << 8) | (peBlue);
  477. }
  478. int x,y,w,h;
  479. w=nWidth;
  480. h=nHeight;
  481. LPDWORD pGradBits;
  482. BITMAPINFO GradBitInfo;
  483. pGradBits=(DWORD*) malloc(w*h*sizeof(DWORD));
  484. memset(&GradBitInfo,0,sizeof(BITMAPINFO));
  485. GradBitInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  486. GradBitInfo.bmiHeader.biWidth=w;
  487. GradBitInfo.bmiHeader.biHeight=h;
  488. GradBitInfo.bmiHeader.biPlanes=1;
  489. GradBitInfo.bmiHeader.biBitCount=32;
  490. GradBitInfo.bmiHeader.biCompression=BI_RGB;
  491. if(nDir==0)
  492. {
  493. for(y=0;y<h;y++)
  494. {
  495. for(x=0;x<w;x++)
  496. {
  497. *(pGradBits+(y*w)+x)=PalVal[MulDiv(nNumColors,y,h)];
  498. }
  499. }
  500. }
  501. else if(nDir==1)
  502. {
  503. for(y=0;y<h;y++)
  504. {
  505. int l,r;
  506. l=MulDiv((nNumColors/2),y,h);
  507. r=l+(nNumColors/2)-1;
  508. for(x=0;x<w;x++)
  509. {
  510. *(pGradBits+(y*w)+x)=PalVal[l+MulDiv((r-l),x,w)];
  511. }
  512. }
  513. }
  514. else if(nDir==2)
  515. {
  516. for(x=0;x<w;x++)
  517. {
  518. for(y=0;y<h;y++)
  519. {
  520. *(pGradBits+(y*w)+x)=PalVal[MulDiv(nNumColors,x,w)];
  521. }
  522. }
  523. }
  524. else if(nDir==3)
  525. {
  526. for(y=0;y<h;y++)
  527. {
  528. int l,r;
  529. r=MulDiv((nNumColors/2),y,h);
  530. l=r+(nNumColors/2)-1;
  531. for(x=0;x<w;x++)
  532. {
  533. *(pGradBits+(y*w)+x)=PalVal[l+MulDiv((r-l),x,w)];
  534. }
  535. }
  536. }
  537. HBITMAP hBmp = CreateDIBitmap(hDC,&GradBitInfo.bmiHeader,CBM_INIT,
  538. pGradBits,&GradBitInfo,DIB_RGB_COLORS);
  539. free(pGradBits);
  540. return hBmp;
  541. }
  542. //static member for keyboard operation, you can used it in you parent window
  543. //it work with shortcut key
  544. LRESULT CMenuXP::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu)
  545. {
  546. UINT iCurrentItem = (UINT)-1; // guaranteed higher than any command ID
  547. CUIntArray arItemsMatched; // items that match the character typed
  548. UINT nItem = pMenu->GetMenuItemCount();
  549. for (UINT i=0; i< nItem; i++)
  550. {
  551. MENUITEMINFO info;
  552. memset(&info, 0, sizeof(info));
  553. info.cbSize = sizeof(info);
  554. info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE;
  555. ::GetMenuItemInfo(*pMenu, i, TRUE, &info);
  556. CMenuXPItem *pData = (CMenuXPItem *)info.dwItemData;
  557. if ((info.fType & MFT_OWNERDRAW) && pData && pData->IsMyData())
  558. {
  559. CString text = pData->m_strText;
  560. int iAmpersand = text.Find('&');
  561. if (iAmpersand >=0 && toupper(nChar)==toupper(text[iAmpersand+1]))
  562. arItemsMatched.Add(i);
  563. }
  564. if (info.fState & MFS_HILITE)
  565. iCurrentItem = i; // note index of current item
  566. }
  567. // arItemsMatched now contains indexes of items that match the char typed.
  568. //
  569. // * if none: beep
  570. // * if one: execute it
  571. // * if more than one: hilite next
  572. //
  573. UINT nFound = arItemsMatched.GetSize();
  574. if (nFound == 0)
  575. return 0;
  576. else if (nFound==1)
  577. return MAKELONG(arItemsMatched[0], MNC_EXECUTE);
  578. // more than one found--return 1st one past current selected item;
  579. UINT iSelect = 0;
  580. for (i=0; i < nFound; i++) {
  581. if (arItemsMatched[i] > iCurrentItem) {
  582. iSelect = i;
  583. break;
  584. }
  585. }
  586. return MAKELONG(arItemsMatched[iSelect], MNC_SELECT);
  587. }
  588. void CMenuXP::DrawMenuText(CDC& dc, CRect rc, CString text,
  589. COLORREF color)
  590. {
  591. CString left = text;
  592. CString right;
  593. int iTabPos = left.Find('\t');
  594. if (iTabPos >= 0) {
  595. right = left.Right(left.GetLength() - iTabPos - 1);
  596. left = left.Left(iTabPos);
  597. }
  598. dc.SetTextColor(color);
  599. dc.DrawText(left, &rc, DT_MYSTANDARD);
  600. if (iTabPos > 0)
  601. dc.DrawText(right, &rc, DT_MYSTANDARD|DT_RIGHT);
  602. }
  603. //find a popupmenu from a menuitem id
  604. CMenuXP *CMenuXP::FindSubMenuFromID(DWORD dwID)
  605. {
  606. CMenuXP *pSubMenu;
  607. CMenuXP *pResult;
  608. for (UINT i=0; i<GetMenuItemCount(); i++)
  609. {
  610. if (GetMenuItemID(i) == dwID)
  611. return this;
  612. }
  613. for (i=0; i<GetMenuItemCount(); i++)
  614. {
  615. pSubMenu = (CMenuXP *)GetSubMenu(i);
  616. if (pSubMenu)
  617. {
  618. pResult = pSubMenu->FindSubMenuFromID(dwID);
  619. if (pResult)
  620. return pResult;
  621. }
  622. }
  623. return NULL;
  624. }
  625. //Add a gradient sidebar, it must be the first item in a popupmenu
  626. BOOL CMenuXP::AddSideBar(CMenuXPSideBar *pItem)
  627. {
  628. ASSERT(pItem);
  629. m_bBreak = TRUE;
  630. m_bBreakBar = FALSE;
  631. return AppendMenu(MF_OWNERDRAW, pItem->m_dwID, (LPCTSTR)pItem);
  632. }
  633. //add a normal menuitem, an accelerator key could be specified, and the accel text will
  634. //be added automatically
  635. BOOL CMenuXP::AppendODMenu2(UINT nFlags, CMenuXPItem *pItem, ACCEL *pAccel)
  636. {
  637. ASSERT(pItem);
  638. nFlags |= MF_OWNERDRAW;
  639. if (m_bBreak)
  640. nFlags |= MF_MENUBREAK;
  641. if (m_bBreakBar)
  642. nFlags |= MF_MENUBARBREAK;
  643. m_bBreak = m_bBreakBar = FALSE;
  644. if (pAccel)
  645. {
  646. CBCGKeyHelper keyhelper(pAccel);
  647. CString strAccel;
  648. keyhelper.Format(strAccel);
  649. if (strAccel.GetLength()>0)
  650. {
  651. pItem->m_strText += _T("\t");
  652. pItem->m_strText += strAccel;
  653. }
  654. }
  655. return AppendMenu(nFlags, pItem->m_dwID, (LPCTSTR)pItem);
  656. }
  657. //Add a separator line
  658. BOOL CMenuXP::AppendSeparator(void)
  659. {
  660. m_bBreak = m_bBreakBar = FALSE;
  661. CMenuXPSeparator *pItem = new CMenuXPSeparator;
  662. return AppendMenu(MF_OWNERDRAW | MF_SEPARATOR, 0, (LPCTSTR)pItem);
  663. }
  664. //add a popup menu
  665. BOOL CMenuXP::AppendODPopup(UINT nFlags, CMenuXP *pPopup, CMenuXPItem *pItem)
  666. {
  667. ASSERT(pPopup);
  668. ASSERT(pItem);
  669. nFlags |= MF_OWNERDRAW;
  670. nFlags |= MF_POPUP;
  671. if (m_bBreak)
  672. nFlags |= MF_MENUBREAK;
  673. if (m_bBreakBar)
  674. nFlags |= MF_MENUBARBREAK;
  675. m_bBreak = m_bBreakBar = FALSE;
  676. return AppendMenu(nFlags, (UINT)pPopup->m_hMenu, (LPCTSTR)pItem);
  677. }
  678. //Change column, the next item added will be in the next column
  679. void CMenuXP::Break(void)
  680. {
  681. m_bBreak = TRUE;
  682. }
  683. //same as Break(), except that a break line will appear between the two columns
  684. void CMenuXP::BreakBar(void)
  685. {
  686. m_bBreakBar = TRUE;
  687. }