CBmpMenu.cpp
上传用户:sycq158
上传日期:2008-10-22
资源大小:15361k
文件大小:35k
源码类别:

游戏

开发平台:

Visual C++

  1. //********************************************************
  2. //                                                            
  3. // FILENAME : CBmpMenu.cpp
  4. // CONTENTS : Defines the class behaviors for the CBmpMenu class.
  5. //
  6. //  Copyright (c), 1999-2000.   
  7. // All rights reserved   
  8. //                                                        
  9. //  Author(s):  Dipti Deogade
  10. //                                                        
  11. //*********************************************************
  12. //** REVISION LIST ****************************************
  13. //     
  14. // Number Date Author Description
  15. //
  16. //
  17. //*********************************************************
  18. #include "stdafx.h"
  19. #include "CBmpMenu.h"
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25. /////////////////////////////////////////////////////////////////////////////
  26. // CBmpMenu
  27. // constructor
  28. CBmpMenu::CBmpMenu(int nBitmapW, BOOL bShowBmp4SubMenu, HBITMAP hBitmap, BOOL bStretchBmp)
  29. {
  30. //check if hbitmap is a valid handle
  31. BITMAP bitmap;
  32. if(GetObject(hBitmap, sizeof(bitmap), &bitmap))
  33. {
  34. m_hBitmap   = hBitmap;
  35. }
  36. else
  37. {
  38. m_hBitmap = NULL;
  39. }
  40. //initialize internal data
  41. m_bShowBmpAll = bShowBmp4SubMenu;
  42. m_pOwnerWnd = m_pToolbar = 0;
  43. m_bSubMenu = FALSE;
  44. m_pSubMenuWnd = 0;
  45. m_bStretchBmp = bStretchBmp;
  46. m_nTBOffSet = nBitmapW;
  47. }
  48. CBmpMenu::~CBmpMenu()
  49. {
  50. }
  51. IMPLEMENT_DYNAMIC( CBmpMenu, CWnd )
  52. BEGIN_MESSAGE_MAP(CBmpMenu, CWnd)
  53. //{{AFX_MSG_MAP(CBmpMenu)
  54. ON_WM_PAINT()
  55. //}}AFX_MSG_MAP
  56. END_MESSAGE_MAP()
  57. /////////////////////////////////////////////////////////////////////////////
  58. // CBmpMenu message handlers
  59. BOOL
  60. CBmpMenu :: Attach( HMENU hMenu )
  61. {
  62. return CMenu::Attach(hMenu);
  63. }
  64. HMENU
  65. CBmpMenu :: Detach()
  66. {
  67. return CMenu::Detach();
  68. }
  69. BOOL CBmpMenu::DestroyWindow() 
  70. {
  71. //Send WM_EXITMENULOOP to the owner window
  72. m_pOwnerWnd->SendMessage(WM_EXITMENULOOP, FALSE);
  73. //destroy the current window and set focus to parent if parent is of the same class
  74. if(::IsWindow(GetParent()->GetSafeHwnd()) && 
  75. GetParent()->IsKindOf(RUNTIME_CLASS(CBmpMenu)))
  76. {
  77. GetParent()->SetFocus();
  78. }
  79. return CWnd::DestroyWindow();
  80. }
  81. BOOL
  82. CBmpMenu :: TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pParentWnd, CRect* pItemRect)
  83. {
  84. //if this is a main menu, then parent window is same as owner window
  85. if(!m_bSubMenu)
  86. {
  87. m_pOwnerWnd = pParentWnd;
  88. }
  89. //Send menu messages to owner window
  90. if(m_pOwnerWnd && IsWindow(m_pOwnerWnd->m_hWnd))
  91. m_pOwnerWnd->SendMessage(m_bSubMenu?WM_INITMENUPOPUP:WM_INITMENU, (WPARAM)m_hMenu, 0);
  92. else
  93. return FALSE;
  94. //create the main window...child of this will be a toolbar which acts as a kind of menu
  95. if(!CreateEx(WS_EX_DLGMODALFRAME|WS_EX_PALETTEWINDOW|WS_EX_CONTROLPARENT
  96. , 0, 0, WS_POPUP|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_BORDER|WS_CHILD, 
  97. CRect(0,0,0,0), pParentWnd, 0))
  98. return FALSE;
  99. //set the data of parent popup menu
  100. if(m_bSubMenu)
  101. {
  102. ((CBmpMenu*)GetParent())->m_pSubMenuWnd = this;
  103. }
  104. //create toolbar
  105. m_pToolbar = new MenuToolBar;
  106. //We add a custom draw toolbar which will act as a menu
  107. m_pToolbar->CreateEx(this, TBSTYLE_FLAT|TBSTYLE_LIST|TBSTYLE_CUSTOMERASE|TBSTYLE_WRAPABLE,
  108. WS_CLIPCHILDREN|CCS_NODIVIDER|CCS_NORESIZE|CCS_NOPARENTALIGN|CBRS_TOOLTIPS);
  109. //Initialize toolbar data and place the menu window on screen
  110. InitToolBarData(m_pToolbar, CPoint(x,y), pItemRect);
  111. //show the menu window without activating it....before that make the owner window as foreground window
  112. if(!m_bSubMenu)
  113. {
  114. m_pOwnerWnd->SetForegroundWindow();
  115. }
  116. ShowWindow(SW_SHOWNA);
  117. //capture keyboard input
  118. SetFocus();
  119. //if this is a submenu and user had opened it by pressing right arrow key
  120. if(m_bSubMenu & MENU_SELECTFIRSTITEM)
  121. {
  122. m_pToolbar->SendMessage(TB_SETHOTITEM, 0, 0);
  123. }
  124. m_bSubMenu &= ~MENU_SELECTFIRSTITEM;
  125. //send a notification message to owner
  126. if(m_pOwnerWnd && IsWindow(m_pOwnerWnd->m_hWnd))
  127. m_pOwnerWnd->SendMessage(WM_ENTERMENULOOP, (WPARAM)m_hMenu, (LPARAM)m_hWnd);
  128. //run the modal loop so that the menu window acts as a kind of dialog
  129. RunModalLoop();
  130. //delete allocated memory and cleanup other stuff
  131. Cleanup();
  132. return TRUE;
  133. }
  134. //wp gives a point on which left mouse button was clicked for the parent menu
  135. void CBmpMenu::PopupSubMenu(WPARAM wp, BOOL bSelectFirstItem)
  136. {
  137. CPoint point(LOWORD(wp), HIWORD(wp));
  138. //Get the button on which left mouse button was clicked
  139. int nBtnIndex = (m_pToolbar->GetToolBarCtrl()).HitTest(&point);
  140. TBBUTTON tbb;
  141. (m_pToolbar->GetToolBarCtrl()).GetButton(nBtnIndex, &tbb);
  142. //Get window rectangle of this button
  143. CRect rect;
  144. (m_pToolbar->GetToolBarCtrl()).GetItemRect(nBtnIndex, &rect);
  145. (m_pToolbar->GetToolBarCtrl()).ClientToScreen(&rect);
  146. //Construct popup menu
  147. CBmpMenu oSubMenu(m_bShowBmpAll?m_nTBOffSet:0, m_bShowBmpAll, m_bShowBmpAll?m_hBitmap:0, m_bStretchBmp);
  148. //intialize data of popup menu
  149. oSubMenu.m_pOwnerWnd = m_pOwnerWnd;
  150. //When user created an instance of CBmpMenu, automatically a handle was associated with root menu
  151. //when he called loadMenu or CreatePopupMenu. But we are not doing any of it for submenus..so just attach
  152. //the menu handle of the submenu to this submenu window
  153. oSubMenu.Attach(((MENUITEMINFO*)(tbb.dwData))->hSubMenu);
  154. //Set data to indicate whther first item should be set as a hot item
  155. oSubMenu.m_bSubMenu = TRUE | bSelectFirstItem;
  156. //Add the blank space with to get bounds rect of this button in root menu
  157. rect.left -= m_nTBOffSet;
  158. //Show the submenu window
  159. oSubMenu.TrackPopupMenu(0, 0, 0, this, &rect);
  160. //detach the menu handle
  161. oSubMenu.Detach();
  162. }
  163. void CBmpMenu::InitToolBarData(CToolBar *pToolBar, CPoint pt, CRect* pItemRect)
  164. {
  165. //enable tooltips
  166. pToolBar->EnableToolTips(TRUE);
  167. //set toolbar buutons bitmap size first. This should be equal to checkmark dimensions
  168. //button size will be automatically calculated by toolbar
  169. pToolBar->SendMessage(TB_SETBITMAPSIZE, 0, MAKELPARAM(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)));
  170. //for each menu item...add corrsponding buttons to the toolbar
  171. for(int i=0; i<(int)GetMenuItemCount(); i++)
  172. {
  173. MENUITEMINFO menuInfo, *pMenuInfo;
  174. TBBUTTON buttonInfo;
  175. char *pszBuffer;
  176. //initialize
  177. menuInfo.cbSize = sizeof(menuInfo);
  178. menuInfo.fMask = MIIM_CHECKMARKS|MIIM_DATA|MIIM_ID|MIIM_STATE|MIIM_SUBMENU|MIIM_TYPE;
  179. pszBuffer = new char[MAX_PATH]; //buffer for button text
  180. menuInfo.cch = MAX_PATH;
  181. menuInfo.dwTypeData = pszBuffer;
  182. ZeroMemory(&buttonInfo, sizeof(buttonInfo));
  183. GetMenuItemInfo(i, &menuInfo, TRUE);
  184. if(menuInfo.dwTypeData == 0)
  185. delete pszBuffer;
  186. //store the app defined data pointer
  187. pMenuInfo = new MENUITEMINFO;
  188. *pMenuInfo = menuInfo;
  189. buttonInfo.dwData = (ULONG)pMenuInfo;
  190. //set button info from menuInfo...default state is enabled
  191. buttonInfo.fsState = TBSTATE_ENABLED|TBSTATE_WRAP;
  192. //check if its a separator
  193. if(menuInfo.fType & MFT_SEPARATOR)
  194. {
  195. buttonInfo.fsStyle = TBSTYLE_SEP;
  196. }
  197. //check if the menu item is disabled or grayed
  198. if((menuInfo.fState & MF_GRAYED) || (MF_DISABLED & menuInfo.fState))
  199. buttonInfo.fsState = 0;
  200. //set button command id
  201. buttonInfo.idCommand = menuInfo.wID;
  202. //add the button
  203. (pToolBar->GetToolBarCtrl()).AddButtons(1, &buttonInfo);
  204. //Set button text
  205. if(menuInfo.dwTypeData)
  206. {
  207. pToolBar->SetButtonText(i, pszBuffer);
  208. }
  209. }
  210. //Get the button width and add width of arrow mark to be drawn for popup menus
  211. int nWidth = LOWORD(pToolBar->GetToolBarCtrl().GetButtonSize());
  212. nWidth += GetSystemMetrics(SM_CXMENUCHECK);
  213. //add width for showing arrows for submenus
  214. pToolBar->GetToolBarCtrl().SetButtonWidth(nWidth, nWidth);
  215. //set the toolbar button size
  216. CRect rect1, rect2;
  217. (pToolBar->GetToolBarCtrl()).GetItemRect(0, &rect1);
  218. //if all the buttons have dropdown arrow...the toolbar returns extra height equal to one row
  219. (pToolBar->GetToolBarCtrl()).GetItemRect(GetMenuItemCount()-1, &rect2);
  220. //set the total toolbar size
  221. //offset of 3 pixels to either side of the toolbar
  222. //If another control(bitmap or slider) is to be placed by the side of toolbar then we have to take 
  223. //into account the width of that control=m_nTBOffSet
  224. //Also we need to check that this window rect lies in the screen rect
  225. PositionMenuWindow(pt, pItemRect, CRect(0, 0, rect1.Width()+6+m_nTBOffSet, rect2.bottom-rect1.top+6));
  226. //place the toolbar window in the menu window
  227. pToolBar->MoveWindow(m_nTBOffSet, 0, rect1.Width()+6, rect2.bottom-rect1.top+6);
  228. //Show the toolbar window without activating it
  229. pToolBar->ShowWindow(SW_SHOWNOACTIVATE);
  230. }
  231. LRESULT CBmpMenu::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  232. {
  233. // TODO: Add your specialized code here and/or call the base class
  234. CWnd* pWnd = 0;
  235. BOOL bFlag = FALSE;
  236. switch(message)
  237. {
  238. case WM_ACTIVATE:
  239. {
  240. //Close all menu windows, if another window is being activated because of a mouse click outside menu window
  241. CWnd oWnd;
  242. if(lParam && IsWindow(HWND(lParam)) && (LOWORD(wParam) == WA_INACTIVE))
  243. if(!oWnd.FromHandle(HWND(lParam))->IsKindOf(RUNTIME_CLASS(CBmpMenu)))
  244. DestroyRootMenu();
  245. if(!lParam)
  246. DestroyRootMenu();
  247. return 0L;
  248. }
  249. break;
  250. case WM_SYSKEYDOWN:
  251. //destroy root menu when alt or any syskey is pressed
  252. DestroyRootMenu();
  253. break;
  254. case WM_KEYDOWN:
  255. //pass the keydown event to toolbar
  256. //If a submenu is being shown for this menu, then key down should be handled by submenu window
  257. //else pass it to toolbar
  258. if(m_pSubMenuWnd)
  259. m_pSubMenuWnd->SendMessage(WM_KEYDOWN, wParam, lParam);
  260. else
  261. m_pToolbar->SendMessage(WM_KEYDOWN, wParam, lParam);
  262. return 0L;
  263. case WM_RESETDATA:
  264. //User defined message to be sent to toolbar to reinitialize internal data
  265. m_pToolbar->SendMessage(WM_RESETDATA, 0, 0);
  266. return 0L;
  267. case WM_COMMAND:
  268. //pass on control specific messages(if any) to owner window
  269. m_pOwnerWnd->PostMessage(WM_COMMAND, wParam, lParam);
  270. return 0L;
  271. case WM_POPUPSUBMENU:
  272. //popup the submenu
  273. PopupSubMenu(wParam, lParam);
  274. return 0L;
  275. }
  276. return CWnd::DefWindowProc(message, wParam, lParam);
  277. }
  278. //destroys all menu window
  279. void CBmpMenu::DestroyRootMenu()
  280. {
  281. if(m_hWnd && IsWindow(m_hWnd))
  282. {
  283. CWnd *pParent = GetParent();
  284. if(pParent && IsWindow(pParent->m_hWnd))
  285. {
  286. if(pParent->IsKindOf(RUNTIME_CLASS(CBmpMenu)))
  287. ((CBmpMenu*)pParent)->DestroyRootMenu();
  288. }
  289. if(m_hWnd && IsWindow(m_hWnd))
  290. {
  291. DestroySubMenus();
  292. //end the modal loop started using RunModalLoop in TrackPopupMenu function
  293. EndModalLoop(0);
  294. m_pSubMenuWnd = 0;
  295. }
  296. }
  297. }
  298. //destroy all the submenu window associated with this menu
  299. void CBmpMenu::DestroySubMenus()
  300. {
  301. if(m_pSubMenuWnd)
  302. {
  303. m_pSubMenuWnd->DestroySubMenus();
  304. //end the modal loop started using RunModalLoop in TrackPopupMenu function
  305. m_pSubMenuWnd->EndModalLoop(0);
  306. m_pSubMenuWnd = 0;
  307. }
  308. }
  309. void CBmpMenu::Cleanup()
  310. {
  311. //remove all allocated memory
  312. if(m_pToolbar && IsWindow(m_pToolbar->m_hWnd))
  313. {
  314. for(int i = 0; i<(m_pToolbar->GetToolBarCtrl()).GetButtonCount(); i++)
  315. {
  316. TBBUTTON tbb;
  317. (m_pToolbar->GetToolBarCtrl()).GetButton(i, &tbb);
  318. if(tbb.dwData)
  319. {
  320. if(((MENUITEMINFO*)(tbb.dwData))->dwTypeData)
  321. delete (char*)(((MENUITEMINFO*)(tbb.dwData))->dwTypeData);
  322. delete (MENUITEMINFO*)(tbb.dwData);
  323. tbb.dwData = 0;
  324. }
  325. }
  326. //destroy our windows
  327. m_pToolbar->DestroyWindow();
  328. delete m_pToolbar;
  329. m_pToolbar = 0;
  330. }
  331. if(IsWindow(m_hWnd))
  332. DestroyWindow();
  333. }
  334. BOOL CBmpMenu::PreCreateWindow(CREATESTRUCT& cs) 
  335. {
  336. // TODO: Add your specialized code here and/or call the base class
  337. BOOL bRet = CWnd::PreCreateWindow(cs);
  338. WNDCLASS wc; 
  339. ::GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wc);
  340. //register new class with same info but different class
  341. wc.lpszClassName = cs.lpszClass = "BitmapMenu";
  342. AfxRegisterClass(&wc);
  343. return bRet;
  344. }
  345. void CBmpMenu::OnPaint() 
  346. {
  347. CPaintDC dc(this); // device context for painting
  348. CRect rect;
  349. GetClientRect(&rect);
  350. //Fill the blank space with whiteness
  351. dc.FillSolidRect(&rect, GetSysColor(COLOR_BTNHIGHLIGHT));
  352. //draw the vertical bitmap if required
  353. if(m_hBitmap)
  354. {
  355. CDC memDC;
  356. memDC.CreateCompatibleDC(&dc);
  357. HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC.GetSafeHdc(), m_hBitmap);
  358. BITMAP bitmap;
  359. GetObject(m_hBitmap, sizeof(bitmap), &bitmap);
  360. //draw the bitmap
  361. if(m_bStretchBmp)
  362. dc.StretchBlt(0, 0, m_nTBOffSet, rect.Height(), &memDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, 
  363. SRCCOPY);
  364. else
  365. {
  366. //draw using pattern brush
  367. HBRUSH hPatternBr = CreatePatternBrush(m_hBitmap);
  368. RECT rect1={0, 0, m_nTBOffSet, rect.Height()};
  369. FillRect(dc.GetSafeHdc(), &rect1, hPatternBr);
  370. DeleteObject(hPatternBr);
  371. }
  372. //restore objects
  373. memDC.SelectObject(hOldBmp);
  374. memDC.DeleteDC();
  375. }
  376. }
  377. //Used to place the menu window
  378. void CBmpMenu::PositionMenuWindow(CPoint pt, CRect* pItemRect, CRect menuRect)
  379. {
  380. CRect deskRect;
  381. GetDesktopWindow()->GetWindowRect(&deskRect);
  382. //Check if this is a submenu...then we need to check either right top or right bottom point of menuRect
  383. if(pItemRect)
  384. {
  385. if(PositionSubMenu(CPoint(pItemRect->right, pItemRect->top), menuRect, TRUE, TRUE) == FALSE)
  386. {
  387. if(PositionSubMenu(CPoint(pItemRect->right, pItemRect->bottom), menuRect, TRUE, FALSE) == FALSE)
  388. {
  389. if(PositionSubMenu(CPoint(pItemRect->left, pItemRect->top), menuRect, FALSE, TRUE) == FALSE)
  390. {
  391. PositionSubMenu(CPoint(pItemRect->left, pItemRect->bottom), menuRect, FALSE, FALSE);
  392. }
  393. }
  394. }
  395. return;
  396. }
  397. //we need to check which position is best for showing menu
  398. //check for left top alignment with pt
  399. if((pt.x+menuRect.Width() < deskRect.right) &&
  400. (pt.y+menuRect.Height() < deskRect.bottom))
  401. {
  402. MoveWindow(pt.x, pt.y, menuRect.Width(), menuRect.Height());
  403. }
  404. else //right top
  405. if((pt.x-menuRect.Width() > deskRect.left) && 
  406. (pt.y+menuRect.Height() < deskRect.bottom))
  407. {
  408. MoveWindow(pt.x-menuRect.Width(), pt.y, menuRect.Width(), menuRect.Height());
  409. }
  410. else
  411. //check for left bottom alignment with pt
  412. if((pt.x+menuRect.Width() < deskRect.right) && 
  413. (pt.y-menuRect.Height() > deskRect.top))
  414. {
  415. MoveWindow(pt.x, pt.y-menuRect.Height(), menuRect.Width(), menuRect.Height());
  416. }
  417. else
  418. //check for right bottom alignment with pt
  419. if((pt.x-menuRect.Width() > deskRect.left) && 
  420. (pt.y-menuRect.Height() > deskRect.top))
  421. {
  422. MoveWindow(pt.x-menuRect.Width(), pt.y-menuRect.Height(), menuRect.Width(), menuRect.Height());
  423. }
  424. else //left top is default
  425. MoveWindow(pt.x, pt.y, menuRect.Width(), menuRect.Height());
  426. }
  427. BOOL
  428. CBmpMenu::PositionSubMenu(CPoint pt, CRect menuRect, BOOL bRtAlign, BOOL bDnAlign)
  429. {
  430. CRect deskRect;
  431. GetDesktopWindow()->GetWindowRect(&deskRect);
  432. if(bRtAlign && bDnAlign)
  433. {
  434. if((pt.x+menuRect.Width() < deskRect.right) &&
  435. (pt.y+menuRect.Height() < deskRect.bottom))
  436. {
  437. MoveWindow(pt.x, pt.y, menuRect.Width(), menuRect.Height());
  438. }
  439. else
  440. {
  441. return FALSE;
  442. }
  443. }
  444. if(bRtAlign && ! bDnAlign)
  445. {
  446. if((pt.x+menuRect.Width() < deskRect.right) && 
  447. (pt.y-menuRect.Height() > deskRect.top))
  448. {
  449. MoveWindow(pt.x, pt.y-menuRect.Height(), menuRect.Width(), menuRect.Height());
  450. }
  451. else
  452. {
  453. return FALSE;
  454. }
  455. }
  456. if(!bRtAlign && bDnAlign)
  457. {
  458. if((pt.x-menuRect.Width() > deskRect.left) && 
  459. (pt.y+menuRect.Height() < deskRect.bottom))
  460. {
  461. MoveWindow(pt.x-menuRect.Width(), pt.y, menuRect.Width(), menuRect.Height());
  462. }
  463. else
  464. {
  465. return FALSE;
  466. }
  467. }
  468. if(!bRtAlign && !bDnAlign)
  469. {
  470. if((pt.x-menuRect.Width() > deskRect.left) && 
  471. (pt.y-menuRect.Height() > deskRect.top))
  472. {
  473. MoveWindow(pt.x-menuRect.Width(), pt.y-menuRect.Height(), menuRect.Width(), menuRect.Height());
  474. }
  475. else
  476. {
  477. return FALSE;
  478. }
  479. }
  480. return TRUE;
  481. }
  482. /////////////////////////////////////////////////////////////////////////////
  483. // MenuToolBar
  484. MenuToolBar::MenuToolBar()
  485. {
  486. m_nLastLBDownIndex = -1;
  487. m_nLastHoverIndex  = -1;
  488. m_oHoverPt.x = -1;
  489. m_oHoverPt.y = -1;
  490. m_nSelectedItem = -1;
  491. }
  492. MenuToolBar::~MenuToolBar()
  493. {
  494. m_oMenuFont.DeleteObject();
  495. }
  496. BEGIN_MESSAGE_MAP(MenuToolBar, CToolBar)
  497. //{{AFX_MSG_MAP(MenuToolBar)
  498. ON_WM_CREATE()
  499. ON_WM_LBUTTONDOWN()
  500. ON_WM_KEYDOWN()
  501. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW , OnCustomDrawNotify)
  502. ON_WM_MOUSEMOVE()
  503. ON_WM_CHAR()
  504. //}}AFX_MSG_MAP
  505. ON_MESSAGE(WM_LBUTTONDOWNAFTER, OnPostLbuttonMsg)
  506. END_MESSAGE_MAP()
  507. /////////////////////////////////////////////////////////////////////////////
  508. // MenuToolBar message handlers
  509. void MenuToolBar::OnCustomDrawNotify(LPARAM lParam, LRESULT* pResult )
  510. {
  511. LPNMTBCUSTOMDRAW lpNMCustomDraw = (LPNMTBCUSTOMDRAW) lParam;
  512. if(!lParam)
  513. return;
  514. if(lpNMCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)
  515. {
  516. *pResult = CDRF_NOTIFYITEMDRAW ; //we need CDDS_ITEMPREPAINT notifications
  517. return;
  518. }
  519. if(lpNMCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
  520. {
  521. MENUITEMINFO menuInfo = *(MENUITEMINFO*)(lpNMCustomDraw->nmcd.lItemlParam);
  522. CRect rcItem = lpNMCustomDraw->nmcd.rc;
  523. CDC dc;
  524. CFont* pOldFont;
  525. dc.Attach(lpNMCustomDraw->nmcd.hdc);
  526. //Check if this is a hot item
  527. if(lpNMCustomDraw->nmcd.uItemState & CDIS_HOT)
  528. {
  529. lpNMCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  530. lpNMCustomDraw->clrHighlightHotTrack = GetSysColor(COLOR_HIGHLIGHT);
  531. //check if last hot item was same as this hot item. If not then we need to send notification
  532. //to owner window
  533. if(m_nSelectedItem != (int)lpNMCustomDraw->nmcd.dwItemSpec)
  534. {
  535. int ndx = lpNMCustomDraw->nmcd.dwItemSpec;
  536. //for submenus we need to send index of this item rather than Command Id
  537. if(menuInfo.hSubMenu)
  538. {
  539. ndx = CommandToIndex(lpNMCustomDraw->nmcd.dwItemSpec);
  540. menuInfo.fState = MF_POPUP;
  541. }
  542. //Send WM_MENUSELECT notification message to owner window
  543. ((CBmpMenu*)GetParent())->m_pOwnerWnd->SendMessage(WM_MENUSELECT, 
  544. MAKEWPARAM(ndx, menuInfo.fState), 
  545. (LPARAM)((CBmpMenu*)GetParent())->m_hMenu);
  546. }
  547. //store th hot item index
  548. m_nSelectedItem = lpNMCustomDraw->nmcd.dwItemSpec;
  549. }
  550. else
  551. {
  552. lpNMCustomDraw->clrText = GetSysColor(COLOR_MENUTEXT);
  553. lpNMCustomDraw->clrHighlightHotTrack = GetSysColor(COLOR_MENU);
  554. }
  555. if(menuInfo.fState & MF_GRAYED)
  556. {
  557. lpNMCustomDraw->clrText = GetSysColor(COLOR_GRAYTEXT);
  558. //this is required for checkmark color
  559. dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
  560. }
  561. else
  562. dc.SetTextColor(GetSysColor(COLOR_MENUTEXT));
  563. //leave a one pixel gap
  564. rcItem.left++;
  565. //draw the background rectangle first
  566. dc.FillSolidRect(rcItem, lpNMCustomDraw->clrHighlightHotTrack);
  567. //Select menu font
  568. pOldFont = dc.SelectObject(&m_oMenuFont);
  569. //check if we need to draw the checkmark for this menu item
  570. if(menuInfo.fState & MF_CHECKED)
  571. {
  572. CRect CheckRect = rcItem;
  573. CheckRect.right = CheckRect.left + CheckRect.Height();
  574. //draw the checkmarked image
  575. Draw3DCheckmark(dc, CheckRect, lpNMCustomDraw->nmcd.uItemState & CDIS_HOT, 
  576. menuInfo.hbmpChecked, TRUE, menuInfo.fState & MF_GRAYED);
  577. }
  578. else
  579. {
  580. //if unchecked bmp is provided
  581. if(menuInfo.hbmpUnchecked)
  582. {
  583. CRect CheckRect = rcItem;
  584. CheckRect.right = CheckRect.left + CheckRect.Height();
  585. //draw the UnCheckmarked image
  586. Draw3DCheckmark(dc, CheckRect, lpNMCustomDraw->nmcd.uItemState & CDIS_HOT, 
  587. menuInfo.hbmpUnchecked, FALSE, menuInfo.fState & MF_GRAYED);
  588. }
  589. }
  590. //draw the background rectangle for this button and then draw menu text
  591. dc.SetTextColor(lpNMCustomDraw->clrText);
  592. dc.SetBkMode(TRANSPARENT);
  593. //calculate text rectangle. The width of Checkmark has to be added
  594. rcItem.left += rcItem.Height() + 2;
  595. //Check if the item is disabled or grayed. Then we need to draw embossed text
  596. if((menuInfo.fState & MF_GRAYED) && !(lpNMCustomDraw->nmcd.uItemState & CDIS_HOT))
  597. {
  598. rcItem.OffsetRect(1,1);
  599. dc.SetTextColor(GetSysColor(COLOR_3DHILIGHT));
  600. dc.DrawText(menuInfo.dwTypeData, rcItem, DT_SINGLELINE|DT_LEFT|DT_VCENTER);
  601. dc.SetTextColor(lpNMCustomDraw->clrText);
  602. rcItem.OffsetRect(-1,-1);
  603. }
  604. //draw the text
  605. dc.DrawText(menuInfo.dwTypeData, rcItem, DT_SINGLELINE|DT_LEFT|DT_VCENTER);
  606. dc.SelectObject(pOldFont);
  607. //draw the popup arrow mark
  608. if(menuInfo.hSubMenu)
  609. {
  610. rcItem.left = rcItem.right-GetSystemMetrics(SM_CXMENUCHECK)*3/4;
  611. rcItem.right = rcItem.left + GetSystemMetrics(SM_CXMENUCHECK);
  612. CRect arrowRect = rcItem;
  613. arrowRect.top += (rcItem.Height()-GetSystemMetrics(SM_CXMENUCHECK))/2;
  614. arrowRect.bottom = arrowRect.top + GetSystemMetrics(SM_CXMENUCHECK);
  615. arrowRect.right -= GetSystemMetrics(SM_CXMENUCHECK)*3/10;
  616. arrowRect.left += GetSystemMetrics(SM_CXMENUCHECK)*3/10;
  617. arrowRect.top += GetSystemMetrics(SM_CXMENUCHECK)*3/10;
  618. arrowRect.bottom -= GetSystemMetrics(SM_CXMENUCHECK)*3/10;
  619. POINT points[3];
  620. points[0].x = arrowRect.left;
  621. points[0].y = arrowRect.top;
  622. points[1].x = arrowRect.left+arrowRect.Width()/2;
  623. points[1].y = arrowRect.top+arrowRect.Height()/2;
  624. points[2].x = points[0].x;
  625. points[2].y = points[1].y + arrowRect.Height()/2;
  626. CPen oPen(PS_SOLID, 1, lpNMCustomDraw->clrText), *pOldPen;
  627. pOldPen=dc.SelectObject(&oPen);
  628. dc.MoveTo(points[0]);
  629. dc.LineTo(points[1]);
  630. dc.LineTo(points[2]);
  631. dc.LineTo(points[0]);
  632. dc.SelectObject(pOldPen);
  633. CBrush oBrush(lpNMCustomDraw->clrText), *pOldBrush;
  634. pOldBrush = dc.SelectObject(&oBrush);
  635. dc.FloodFill(points[0].x +1, points[0].y +2, lpNMCustomDraw->clrText);
  636. dc.SelectObject(pOldBrush);
  637. }
  638. //detach DC
  639. dc.Detach();
  640. *pResult = CDRF_SKIPDEFAULT;
  641. }
  642. else
  643. *pResult = 0;
  644. }
  645. //////////////////
  646. // Draw 3D checkmark
  647. BOOL MenuToolBar::Draw3DCheckmark(CDC& dc, CRect rc,
  648. BOOL bSelected, HBITMAP hbmCheck, BOOL bDrawSunkenBdr, BOOL bGrayImage)
  649. {
  650. // get checkmark bitmap if none, use Windows standard
  651. HBITMAP hbm = hbmCheck;
  652. if (!hbmCheck) {
  653. CBitmap bm;
  654. bm.LoadOEMBitmap(OBM_CHECK);
  655. hbm = (HBITMAP)bm.Detach();
  656. if(!hbm)
  657. return FALSE;
  658. }
  659. // center bitmap in caller's rectangle
  660. BITMAP bm;
  661. ::GetObject(hbm, sizeof(bm), &bm);
  662. int cx = bm.bmWidth;
  663. int cy = bm.bmHeight;
  664. CRect rcDest = rc;
  665. CPoint p(0,0);
  666. CSize delta(CPoint((rc.Width() - cx)/2, (rc.Height() - cy)/2));
  667. if (rc.Width() > cx)
  668. rcDest = CRect(rc.TopLeft() + delta, CSize(cx, cy));
  669. else
  670. p -= delta;
  671. //draw background rectangle first
  672. if(hbmCheck && bDrawSunkenBdr)
  673. dc.FillSolidRect(rc, GetSysColor(COLOR_MENU));
  674. // select checkmark into memory DC
  675. CDC memdc;
  676. memdc.CreateCompatibleDC(&dc);
  677. //change the background colors of bitmap
  678. if(hbmCheck)
  679. {
  680. hbm = GetSysColorBitmap(dc.GetSafeHdc(), hbm, bGrayImage, bSelected);
  681. }
  682. HBITMAP hOldBM = (HBITMAP)::SelectObject(memdc, hbm);
  683. // set BG color based on selected state
  684. COLORREF colorOld = dc.SetBkColor(GetSysColor((bSelected && !bGrayImage)?COLOR_HIGHLIGHT:COLOR_MENU));
  685. dc.BitBlt(rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(),
  686. &memdc, p.x, p.y, SRCCOPY);
  687. dc.SetBkColor(colorOld);
  688. ::SelectObject(memdc, hOldBM); //restore
  689. //Delete hbm object..it is either created by GetSysColorBitmap function or LoadOEMBitmap
  690. DeleteObject(hbm);
  691. memdc.DeleteDC();
  692. //Check if we need to draw sunken border
  693. if(bDrawSunkenBdr)
  694. {
  695. dc.DrawEdge(rc, BDR_SUNKENOUTER, BF_RECT);
  696. //draw background
  697. CBrush brush;
  698. CBitmap bmp;
  699. WORD      Bits[8] = { 0x0055, 0x00aa, 0x0055, 0x00aa,
  700.  0x0055, 0x00aa, 0x0055, 0x00aa };
  701. bmp.CreateBitmap( 8, 8, 1, 1, &Bits );
  702. //hatched background
  703. brush.CreatePatternBrush(&bmp);
  704. //if item is selected, then we draw plain background
  705. CBrush brush1(GetSysColor(COLOR_MENU));
  706. CBrush* oldBr = dc.SelectObject(bSelected ? &brush1 : &brush);
  707. //the text color is used by pattern brush
  708. dc.SetTextColor(GetSysColor(COLOR_3DHIGHLIGHT));
  709. dc.SetBkColor(GetSysColor(COLOR_MENU));
  710. if(!hbmCheck)
  711. dc.ExtFloodFill(rc.left+1, rc.top+1, GetSysColor(bSelected?COLOR_HIGHLIGHT:COLOR_MENU), FLOODFILLSURFACE);
  712. else
  713. dc.ExtFloodFill(rc.left+1, rc.top+1, GetSysColor(COLOR_MENU), FLOODFILLSURFACE);
  714. //cleanup
  715. dc.SelectObject(oldBr);
  716. brush.DeleteObject();
  717. brush1.DeleteObject();
  718. bmp.DeleteObject();
  719. }
  720. return TRUE;
  721. }
  722. int MenuToolBar::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  723. {
  724. lpCreateStruct->style &= ~WS_VISIBLE;
  725. if (CToolBar::OnCreate(lpCreateStruct) == -1)
  726. return -1;
  727. ModifyStyleEx(0, WS_EX_TOOLWINDOW);
  728. GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
  729. NONCLIENTMETRICS nc;
  730. ZeroMemory(&nc, sizeof(nc));
  731. nc.cbSize = sizeof(nc);
  732. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &nc, 0);
  733. m_oMenuFont.CreateFontIndirect(&nc.lfMenuFont);
  734. SetFont(&m_oMenuFont);
  735. return 0;
  736. }
  737. void MenuToolBar::OnLButtonDown(UINT nFlags, CPoint point) 
  738. {
  739. //Calculate button index and post a user define message to ourselves..so that this function returns immediately
  740. int nBtnIndex = GetToolBarCtrl().HitTest(&point);
  741. TBBUTTON tbb;
  742. GetToolBarCtrl().GetButton(nBtnIndex, &tbb);
  743. //if this button is disabled or grayed down...then we simply return
  744. BOOL bRet = (((MENUITEMINFO*)(tbb.dwData))->fState & MF_GRAYED) || 
  745. (((MENUITEMINFO*)(tbb.dwData))->fState & MF_DISABLED);
  746. if(bRet)
  747. return;
  748. if(nBtnIndex >= 0)
  749. {
  750. if(m_nLastLBDownIndex != nBtnIndex)
  751. {
  752. //If already another popup menu was opened, then we need to close that window
  753. ((CBmpMenu*)GetParent())->DestroySubMenus();
  754. PostMessage(WM_LBUTTONDOWNAFTER, nFlags, MAKELPARAM(point.x, point.y));
  755. m_nLastLBDownIndex = nBtnIndex;
  756. }
  757. }
  758. }
  759. void MenuToolBar::OnPostLbuttonMsg(UINT nFlags, LPARAM lp)
  760. {
  761. //claculate the button index
  762. CPoint point(LOWORD(lp), HIWORD(lp));
  763. int nBtnIndex = GetToolBarCtrl().HitTest(&point);
  764. TBBUTTON tbb;
  765. GetToolBarCtrl().GetButton(nBtnIndex, &tbb);
  766. //if the click was on a button corresponding to popup item, then we need to open up popup menu 
  767. //and don't pass the message up windows hirarchy.
  768. if(tbb.dwData && ((MENUITEMINFO*)(tbb.dwData))->hSubMenu)
  769. {
  770. ((CBmpMenu*)GetParent())->SendMessage(WM_POPUPSUBMENU, lp, (MENU_SELECTFIRSTITEM & nFlags)?MENU_SELECTFIRSTITEM:0);
  771. return;
  772. }
  773. //If this is a valid button and no submenu then send wm_command message to owner and close all menus
  774. ((CBmpMenu*)GetParent())->m_pOwnerWnd->PostMessage(WM_COMMAND, MAKEWPARAM(tbb.idCommand, 0), 0);
  775. ((CBmpMenu*)GetParent())->DestroyRootMenu();
  776. }
  777. void MenuToolBar::OnMouseMove(UINT nFlags, CPoint point) 
  778. {
  779. // TODO: Add your message handler code here and/or call default
  780. int nBtnIndex = GetToolBarCtrl().HitTest(&point);
  781. //skip mouse move message if this button is disabled or grayed or inactive
  782. TBBUTTON tbb;
  783. GetToolBarCtrl().GetButton(nBtnIndex, &tbb);
  784. //if this button is disabled or grayed down...then we simply return
  785. BOOL bRet = (((MENUITEMINFO*)(tbb.dwData))->fState & MF_GRAYED) || 
  786. (((MENUITEMINFO*)(tbb.dwData))->fState & MF_DISABLED);
  787. if(bRet)
  788. return;
  789. //skip WM_MOUSEMOVE message if the last mouse position is same as curent one.
  790. static CPoint pt(0,0);
  791. if(point != pt)
  792. pt = point;
  793. else
  794. return;
  795. if(nBtnIndex >= 0)
  796. {
  797. if(m_nLastHoverIndex != nBtnIndex)
  798. {
  799. m_nLastHoverIndex = nBtnIndex;
  800. m_oHoverPt = point;
  801. //Hovetrack only for submenu items
  802. TRACKMOUSEEVENT tme = {sizeof(tme), TME_HOVER, m_hWnd, 0};
  803. _TrackMouseEvent(&tme);
  804. }
  805. }
  806. CToolBar::OnMouseMove(nFlags, point);
  807. }
  808. LRESULT MenuToolBar::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  809. {
  810. // TODO: Add your specialized code here and/or call the base class
  811. //Garb WM_MOUSEHOVER message
  812. if(message == WM_MOUSEHOVER)
  813. {
  814. if(m_nLastHoverIndex >= 0)
  815. {
  816. TBBUTTON tbb;
  817. GetToolBarCtrl().GetButton(m_nLastHoverIndex, &tbb);
  818. if(tbb.dwData)
  819. {
  820. //pass on left mouse down event for submenu items
  821. if(((MENUITEMINFO*)(tbb.dwData))->hSubMenu)
  822. {
  823. SendMessage(WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(m_oHoverPt.x, m_oHoverPt.y));
  824. SendMessage(WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(m_oHoverPt.x, m_oHoverPt.y));
  825. }
  826. else //delete any submenus present for this menu
  827. {
  828. ((CBmpMenu*)GetParent())->DestroySubMenus();
  829. m_nLastLBDownIndex = -1; //reset after destroying submenus
  830. }
  831. }
  832. }
  833. }
  834. if(message == WM_RESETDATA)
  835. m_nLastLBDownIndex = -1;
  836. return CToolBar::DefWindowProc(message, wParam, lParam);
  837. }
  838. //Grab Key down events
  839. BOOL MenuToolBar::KeyboardFilter(UINT nChar, UINT nRepCnt, UINT nFlags) 
  840. {
  841. //On escape key, we close all menu windows
  842. if(nChar==VK_ESCAPE)
  843. {
  844. ((CBmpMenu*)GetParent())->DestroyRootMenu();
  845. return TRUE;
  846. }
  847. //On left arrow...we need to close the current submenu and go to parent
  848. if(nChar == VK_LEFT)
  849. {
  850. CWnd *pWnd = GetParent()->GetParent();
  851. if(pWnd && IsWindow(pWnd->m_hWnd) && pWnd->IsKindOf(RUNTIME_CLASS(CBmpMenu)))
  852. {
  853. ((CBmpMenu*)pWnd)->DestroySubMenus();
  854. (CBmpMenu*)pWnd->SendMessage(WM_RESETDATA, 0, 0);
  855. }
  856. return TRUE;
  857. }
  858. if((nChar == VK_RIGHT) || (nChar == VK_RETURN))
  859. {
  860. if(m_nSelectedItem != -1)
  861. {
  862. int ndx = CommandToIndex(m_nSelectedItem);
  863. if(ndx >= 0)
  864. {
  865. TBBUTTON tbb;
  866. GetToolBarCtrl().GetButton(ndx, &tbb);
  867. CRect rect;
  868. GetToolBarCtrl().GetItemRect(ndx, &rect);
  869. if(tbb.dwData)
  870. {
  871. //pass on left mouse down event for submenu items..also need to select the first submenu item
  872. if((((MENUITEMINFO*)(tbb.dwData))->hSubMenu) || (nChar == VK_RETURN))
  873. {
  874. m_nLastLBDownIndex = -1;
  875. SendMessage(WM_LBUTTONDOWN, MK_LBUTTON|MENU_SELECTFIRSTITEM, MAKELPARAM(rect.left+1, rect.top+1));
  876. SendMessage(WM_LBUTTONUP, MK_LBUTTON|MENU_SELECTFIRSTITEM, MAKELPARAM(rect.left+1, rect.top+1));
  877. }
  878. }
  879. }
  880. }
  881. return TRUE;
  882. }
  883. //pass on for further processing
  884. OnChar(nChar, nRepCnt, nFlags);
  885. return FALSE;
  886. }
  887. void MenuToolBar::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  888. {
  889. // TODO: Add your message handler code here and/or call default
  890. if(KeyboardFilter(nChar,nRepCnt,nFlags)) 
  891. return;
  892. CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  893. }
  894. // User typed a char into menu. Look for item with & preceeding the char typed.
  895. void MenuToolBar::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
  896. {
  897. // TODO: Add your message handler code here and/or call default
  898. //////////////////
  899. UINT iCurrentItem = (UINT)-1; // guaranteed higher than any command ID
  900. CUIntArray arItemsMatched; // items that match the character typed
  901. UINT nItem = GetToolBarCtrl().GetButtonCount();
  902. for (UINT i=0; i< nItem; i++) 
  903. {
  904. // get menu info
  905. TBBUTTON tbb;
  906. GetToolBarCtrl().GetButton(i, &tbb);
  907. if(tbb.dwData)
  908. {
  909. if(((MENUITEMINFO*)(tbb.dwData))->dwTypeData)
  910. {
  911. CString text = ((MENUITEMINFO*)(tbb.dwData))->dwTypeData;
  912. int iAmpersand = text.Find('&');
  913. if (iAmpersand >=0 && toupper(nChar)==toupper(text[iAmpersand+1]))
  914. arItemsMatched.Add(i);
  915. }
  916. if ((UINT)SendMessage(TB_GETHOTITEM, 0, 0) == i)
  917. iCurrentItem = i; // note index of current item
  918. }
  919. }
  920. // arItemsMatched now contains indexes of items that match the char typed.
  921. UINT nFound = arItemsMatched.GetSize();
  922. if (nFound == 0)
  923. {
  924. //notify owner window and take corresponding action
  925. UINT lRet = (((CBmpMenu*)GetParent())->m_pOwnerWnd)->SendMessage(WM_MENUCHAR, MAKEWPARAM(nChar, MF_POPUP), 
  926. (LPARAM)((CBmpMenu*)GetParent())->m_hMenu);
  927. switch(HIWORD(lRet))
  928. {
  929. case MNC_CLOSE:
  930. ((CBmpMenu*)GetParent())->DestroyRootMenu();
  931. break;
  932. case MNC_SELECT:
  933. //select the menu item
  934. break;
  935. case MNC_EXECUTE:
  936. (((CBmpMenu*)GetParent())->m_pOwnerWnd)->SendMessage(WM_COMMAND, LOWORD(lRet), 0);
  937. break;
  938. }
  939. return;
  940. }
  941. else if (nFound==1)
  942. {
  943. CRect rect;
  944. GetToolBarCtrl().GetItemRect(arItemsMatched[0], &rect);
  945. SendMessage(TB_SETHOTITEM, arItemsMatched[0], 0);
  946. m_nLastLBDownIndex = -1;
  947. SendMessage(WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(rect.left+1, rect.top+1));
  948. SendMessage(WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(rect.left+1, rect.top+1));
  949. return;
  950. }
  951. // more than one found--return 1st one past current selected item;
  952. UINT iSelect = 0;
  953. for (i=0; i < nFound; i++) {
  954. if (arItemsMatched[i] > iCurrentItem) {
  955. iSelect = i;
  956. break;
  957. }
  958. }
  959. SendMessage(TB_SETHOTITEM, iSelect, 0);
  960. }
  961. HBITMAP 
  962. GetSysColorBitmap(HDC hDC, HBITMAP hSourceBitmap, BOOL bMono, BOOL bSelected)
  963. {
  964. struct _COLORMAPTABLE{
  965. COLORREF rgb;
  966. int nSysColor;
  967. };
  968. _COLORMAPTABLE _ColorMap[]=
  969. {
  970. // mapping from color in DIB to system color
  971. { RGB(0x00, 0x00, 0x00),  COLOR_BTNTEXT },       // black
  972. { RGB(0x80, 0x80, 0x80),  COLOR_BTNSHADOW },     // dark gray
  973. { RGB(0xC0, 0xC0, 0xC0),  COLOR_MENU },       // bright gray
  974. { RGB(0xFF, 0xFF, 0xFF),  COLOR_BTNHIGHLIGHT }   // white
  975. };
  976. HBITMAP hOldSourceBitmap, hOldDestBitmap, hDestBitmap;
  977. HDC hMemSrc, hMemDest;
  978. int height, width;
  979. int i, j;
  980. BITMAP SrcBitmap;
  981. if(bSelected && !bMono)
  982. _ColorMap[2].nSysColor= COLOR_HIGHLIGHT;
  983. else
  984. _ColorMap[2].nSysColor= COLOR_MENU;
  985. // Step 1: Create a memory DC for the source and destination bitmaps
  986. //         compatible with the device used.
  987. hMemSrc = CreateCompatibleDC(hDC);
  988. hMemDest= CreateCompatibleDC(hDC);
  989. // Step 2: Get the height and width of the source bitmap.
  990. GetObject(hSourceBitmap, sizeof(BITMAP), (LPSTR)&SrcBitmap);
  991. width = SrcBitmap.bmWidth;
  992. height = SrcBitmap.bmHeight;
  993. // Step 3: Select the source bitmap into the source DC. Create a
  994. //         destination bitmap, and select it into the destination DC.
  995. hOldSourceBitmap = (HBITMAP)SelectObject(hMemSrc, hSourceBitmap);
  996. hDestBitmap = CreateBitmap(height, width, (bMono)?1:SrcBitmap.bmPlanes,
  997. (bMono)?1:SrcBitmap.bmBitsPixel, NULL);
  998. if (hDestBitmap)
  999. {
  1000. hOldDestBitmap = (HBITMAP)SelectObject(hMemDest, hDestBitmap);
  1001. // Step 4: Copy the pixels from the source to the destination.
  1002. for (i = 0; i < width; ++i)
  1003. {
  1004. for (j = 0; j < height; ++j)
  1005. {
  1006. //Get the color of source bitmap
  1007. COLORREF rgb = GetPixel(hMemSrc, i, j);
  1008. SetPixel(hMemDest, i, j, rgb);
  1009. if(!bMono)
  1010. {
  1011. //check if we need to change this color
  1012. for(int k=0; k<sizeof(_ColorMap)/sizeof(_ColorMap[0]); k++)
  1013. {
  1014. if(rgb == _ColorMap[k].rgb)
  1015. {
  1016. SetPixel(hMemDest, i, j, GetSysColor(_ColorMap[k].nSysColor));
  1017. break;
  1018. }
  1019. }
  1020. }
  1021. }
  1022. }
  1023. }
  1024. // Step 5: Destroy the DCs.
  1025. SelectObject(hMemSrc, hOldSourceBitmap);
  1026. SelectObject(hMemDest, hOldDestBitmap);
  1027. DeleteDC(hMemDest);
  1028. DeleteDC(hMemSrc);
  1029. // Step 6: Return the rotated bitmap.
  1030. return(hDestBitmap);
  1031. }