home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / advanced / mtmdi / bounce.cpp next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  13.6 KB  |  453 lines

  1. // bounce.cpp : Defines the class behaviors for the Bounce child window.
  2. //
  3. // This is a part of the Microsoft Foundation Classes C++ library.
  4. // Copyright (C) 1992-1998 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Microsoft Foundation Classes Reference and related
  9. // electronic documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12.  
  13. #include "stdafx.h"
  14. #include "mdi.h"
  15. #include "bounce.h"
  16. #include "mtbounce.h"
  17.  
  18. #define ABS(x) ((x) < 0? -(x) : (x) > 0? (x) : 0)
  19.  
  20. BEGIN_MESSAGE_MAP(CBounceMDIChildWnd, CMDIChildWnd)
  21.     //{{AFX_MSG_MAP(CBounceMDIChildWnd)
  22.     ON_WM_SIZE()
  23.     //}}AFX_MSG_MAP
  24.     ON_MESSAGE(WM_USER_PREPARE_TO_CLOSE, OnPrepareToClose)
  25. END_MESSAGE_MAP()
  26.  
  27. /////////////////////////////////////////////////////////////////////////////
  28. // CBounceMDIChildWnd
  29.  
  30. CMenu CBounceMDIChildWnd::menu;        // menu for all BOUNCE windows
  31.  
  32. CBounceMDIChildWnd::CBounceMDIChildWnd()
  33. {
  34. }
  35.  
  36. BOOL CBounceMDIChildWnd::Create(LPCTSTR szTitle, LONG style /* = 0 */,
  37.     const RECT& rect /* = rectDefault */,
  38.     CMDIFrameWnd* parent /* = NULL */)
  39. {
  40.     // Setup the shared menu
  41.     if (menu.m_hMenu == NULL)
  42.         menu.LoadMenu(IDR_BOUNCE);
  43.     m_hMenuShared = menu.m_hMenu;
  44.  
  45.     if (!CMDIChildWnd::Create(NULL, szTitle, style, rect, parent))
  46.         return FALSE;
  47.  
  48.     // The default PostNcDestroy handler will delete this CBounceMDIChildWnd
  49.     // object when destroyed.  When Windows destroys the CBounceMDIChildWnd
  50.     // window, it will also destroy the CBounceWnd, which is the child
  51.     // window of the MDI child window, executing in the separate thread.
  52.     // Finally, when the child CBounceWnd window is destroyed, the
  53.     // CWinThread object will be automatically destroyed, as explained
  54.     // in the comment for CBounceThread::InitInstance in mtbounce.cpp.
  55.  
  56.     CBounceThread* pBounceThread = new CBounceThread(m_hWnd);
  57.     pBounceThread->CreateThread();
  58.  
  59.     return TRUE;
  60. }
  61.  
  62.  
  63. BOOL CBounceMDIChildWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  64.         AFX_CMDHANDLERINFO* pHandlerInfo)
  65. {
  66.     // The default CFrameWnd::OnCmdMsg function is responsible for
  67.     // dispatching menu commands to ON_COMMAND handlers and for
  68.     // initializing menus and toolbars via ON_UPDATE_COMMAND_UI
  69.     // handlers.  For the MDI child window, all such dispatching
  70.     // and command user interface initialization are done in the
  71.     // main application thread.  The CBounceWnd object processes
  72.     // user interface events in a separate thread.  This override,
  73.     // CBounceMDIChildWnd::OnCmdMsg, delegates command handling to
  74.     // the child CBounceWnd window.  It cannot do this delegation
  75.     // by simply calling CBounceWnd::OnCmdMsg, because doing so
  76.     // would defeat the design of handling CBounceWnd user interface
  77.     // events in the separate thread.  If CBounceMDIChildWnd::OnCmdMsg
  78.     // simply called CBounceWnd::OnCmdMsg, then the command handling
  79.     // would be processed in the same main application thread as that
  80.     // of the MDI child window.
  81.     //
  82.     // A common and safe way to make a call from one thread to another
  83.     // is to call SendMessage for a window whose message pump runs in
  84.     // a second thread.  That is how CBounceMDIChildWnd::OnCmdMsg is
  85.     // implemented.  All of the OnCmdMsg parameters are gathered into
  86.     // an application-defined (but re-usable) COnCmdMsg structure,
  87.     // that is sent by reference to the CBounceWnd window via the
  88.     // lParam of SendMessage.
  89.  
  90.  
  91.     CWnd* pBounceWnd = (CBounceWnd*)GetDlgItem(IDC_BOUNCE_WND);
  92.     if (pBounceWnd == NULL)
  93.         return FALSE; // child CBounceWnd not created yet.
  94.  
  95.     // It is safe to create the COnCmdMsg on the stack because
  96.     // it will not be deleted until the SendMessage call has
  97.     // synchronously executed.
  98.  
  99.     COnCmdMsg oncmdmsg;
  100.     oncmdmsg.m_nID =    nID;
  101.     oncmdmsg.m_nCode = nCode;
  102.     oncmdmsg.m_pExtra = pExtra;
  103.     oncmdmsg.m_pHandlerInfo = pHandlerInfo;
  104.  
  105.     // In addition to passing all of the OnCmdMsg parameters to
  106.     // the CBounceWnd window via the SendMessage lParam, it is also
  107.     // necessary to forward the implicit wParam originally passed
  108.     // in the WM_COMMAND message for the menu command.  The original
  109.     // WM_COMMAND wParam is not a parameter of OnCmdMsg, but it can
  110.     // be obtained by calling GetCurrentMessage.  The wParam is needed by
  111.     // the CBounceWnd::OnColor command handler to distinguish which
  112.     // menu item, i.e., which color, was selected from the Color menu.
  113.     //
  114.     // The original single thread MDI sample application, the
  115.     // CBounceWnd::OnColor command handler calls GetCurrentMessage
  116.     // to retrieve the wParam from the WM_COMMAND message.  To satisfy
  117.     // that implementation of CBounceWnd::OnColor, we call
  118.     // GetCurrentMessage() and forward the wParam in the user-defined
  119.     // WM_USER_ONCMDMSG message.  Alternatively, we could have
  120.     // called GetMessageMsg and passed the wParam as an additional
  121.     // member of the COnCmdMsg structure.
  122.  
  123.     return pBounceWnd->SendMessage(WM_USER_ONCMDMSG,
  124.         GetCurrentMessage()->wParam, (LPARAM)&oncmdmsg);
  125. }
  126.  
  127. BOOL CBounceMDIChildWnd::DestroyWindow()
  128. {
  129.     OnPrepareToClose();
  130.  
  131.     return CMDIChildWnd::DestroyWindow();
  132. }
  133.  
  134. LRESULT CBounceMDIChildWnd::OnPrepareToClose(WPARAM, LPARAM)
  135. {
  136.     // Wait for the bounce thread to auto-destroy its CWinThread object
  137.     // before continuing.  This avoids a possible false memory leak
  138.     // detection in debug mode when exiting the application.  The false
  139.     // memory leak might be reported if the main application thread
  140.     // terminates sooner than the bounce thread auto-destroys its own
  141.     // CWinThread object.  See also comments for CBounceWnd::OnNcDestroy.
  142.  
  143.     CWnd* pBounceWnd = (CBounceWnd*)GetDlgItem(IDC_BOUNCE_WND);
  144.     pBounceWnd->SendMessage(WM_USER_PREPARE_TO_CLOSE);
  145.  
  146.     WaitForSingleObject(CBounceThread::m_hEventBounceThreadKilled, INFINITE);
  147.  
  148.     return 0;
  149. }
  150.  
  151. /////////////////////////////////////////////////////////////////////////////
  152. // CBounceWnd
  153.  
  154. BEGIN_MESSAGE_MAP(CBounceWnd, CWnd)
  155.     //{{AFX_MSG_MAP(CBounceWnd)
  156.     ON_WM_CREATE()
  157.     ON_WM_SIZE()
  158.     ON_WM_TIMER()
  159.     ON_UPDATE_COMMAND_UI(IDM_RED, OnUpdateColor)
  160.     ON_COMMAND(IDM_BLACK, OnColor)
  161.     ON_COMMAND(IDM_CUSTOM, OnCustomColor)
  162.     ON_UPDATE_COMMAND_UI(IDM_SLOW, OnUpdateSlow)
  163.     ON_COMMAND(IDM_SLOW, OnSlow)
  164.     ON_UPDATE_COMMAND_UI(IDM_FAST, OnUpdateFast)
  165.     ON_COMMAND(IDM_FAST, OnFast)
  166.     ON_WM_LBUTTONDOWN()
  167.     ON_COMMAND(IDM_RED, OnColor)
  168.     ON_COMMAND(IDM_GREEN, OnColor)
  169.     ON_COMMAND(IDM_BLUE, OnColor)
  170.     ON_COMMAND(IDM_WHITE, OnColor)
  171.     ON_UPDATE_COMMAND_UI(IDM_WHITE, OnUpdateColor)
  172.     ON_UPDATE_COMMAND_UI(IDM_GREEN, OnUpdateColor)
  173.     ON_UPDATE_COMMAND_UI(IDM_CUSTOM, OnUpdateColor)
  174.     ON_UPDATE_COMMAND_UI(IDM_BLUE, OnUpdateColor)
  175.     ON_UPDATE_COMMAND_UI(IDM_BLACK, OnUpdateColor)
  176.     ON_WM_DESTROY()
  177.     //}}AFX_MSG_MAP
  178.     ON_MESSAGE(WM_USER_ONCMDMSG, OnDelegatedCmdMsg)
  179.     ON_MESSAGE(WM_USER_PREPARE_TO_CLOSE, OnPrepareToClose)
  180. END_MESSAGE_MAP()
  181.  
  182. IMPLEMENT_DYNAMIC(CBounceWnd, CWnd)
  183.  
  184. /////////////////////////////////////////////////////////////////////////////
  185. // CBounceWnd creation
  186.  
  187. BOOL CBounceWnd::Create(LPCTSTR szTitle, LONG style, const RECT& rect, CWnd* parent)
  188. {
  189.     // Register a custom WndClass and create a window.
  190.     // This must be done because CBounceWnd has a custom cursor, and
  191.     //  no icon.
  192.     LPCTSTR lpszBounceClass =
  193.         AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
  194.             LoadCursor(NULL, IDC_UPARROW),
  195.             (HBRUSH)(COLOR_WINDOW+1),
  196.             NULL);
  197.  
  198.     return CWnd::Create(lpszBounceClass, szTitle, style, rect, parent,
  199.         IDC_BOUNCE_WND);
  200. }
  201.  
  202. CBounceWnd::CBounceWnd()
  203. {
  204.     m_nIDColor = IDM_BLACK;
  205.     m_clrBall = RGB(0,0,0);
  206.     m_bFastSpeed = FALSE;
  207. }
  208.  
  209. void CBounceWnd::OnDestroy()
  210. {
  211.     // The following code forces a condition where the exiting application will
  212.     // report a memory leak of the bounce CWinThread object if the application
  213.     // does not wait for the bounce thread to auto-destroy its CWinThread object.
  214.     // See also CMDIChildBounceWnd::OnNcDestroy.
  215.  
  216. #ifdef _DEBUG
  217.     TRACE0("This is an intentional 1-second delay in terminating the bounce thread\n");
  218.     Sleep(1000);
  219. #endif
  220.     CWnd::OnDestroy();
  221. }
  222.  
  223. // Set up the ball parameters and start a timer.
  224. int CBounceWnd::OnCreate(LPCREATESTRUCT /* p */)
  225. {
  226.     if (!SetTimer(1, 100 /*start slow*/, NULL))
  227.     {
  228.         MessageBox(_T("Not enough timers available for this window."),
  229.                 _T("MDI:Bounce"), MB_ICONEXCLAMATION | MB_OK);
  230.  
  231.         // signal creation failure...
  232.         return -1;
  233.     }
  234.  
  235.     CDC* pDC = GetDC();
  236.     m_ptPixel.x = pDC->GetDeviceCaps(ASPECTX);
  237.     m_ptPixel.y = pDC->GetDeviceCaps(ASPECTY);
  238.     ReleaseDC(pDC);
  239.  
  240.     // Note that we could call MakeNewBall here (which should be called
  241.     // whenever the ball's speed, color or size has been changed), but the
  242.     // OnSize member function is always called after the OnCreate. Since
  243.     // the OnSize member has to call MakeNewBall anyway, we don't here.
  244.  
  245.     return 0;
  246. }
  247.  
  248. // MakeNewBall:
  249. // Whenever a parameter changes which would affect the speed or appearance
  250. // of the ball, call this to regenerate the ball bitmap.
  251. //
  252. void CBounceWnd::MakeNewBall()
  253. {
  254.     m_sizeTotal.cx = (m_sizeRadius.cx + ABS(m_sizeMove.cx)) << 1;
  255.     m_sizeTotal.cy = (m_sizeRadius.cy + ABS(m_sizeMove.cy)) << 1;
  256.  
  257.     if (m_bmBall.m_hObject != NULL)
  258.         m_bmBall.DeleteObject();        // get rid of old bitmap
  259.  
  260.     CClientDC dc(this);
  261.     CDC dcMem;
  262.     dcMem.CreateCompatibleDC(&dc);
  263.  
  264.     m_bmBall.CreateCompatibleBitmap(&dc, m_sizeTotal.cx, m_sizeTotal.cy);
  265.     ASSERT(m_bmBall.m_hObject != NULL);
  266.  
  267.     CBitmap* pOldBitmap = dcMem.SelectObject(&m_bmBall);
  268.  
  269.     // draw a rectangle in the background (window) color
  270.     CRect rect(0, 0, m_sizeTotal.cx, m_sizeTotal.cy);
  271.     CBrush brBackground(::GetSysColor(COLOR_WINDOW));
  272.     dcMem.FillRect(rect, &brBackground);
  273.  
  274.     CBrush brCross(HS_DIAGCROSS, 0L);
  275.     CBrush* pOldBrush = dcMem.SelectObject(&brCross);
  276.  
  277.     dcMem.SetBkColor(m_clrBall);
  278.     dcMem.Ellipse(ABS(m_sizeMove.cx), ABS(m_sizeMove.cy),
  279.         m_sizeTotal.cx - ABS(m_sizeMove.cx),
  280.         m_sizeTotal.cy - ABS(m_sizeMove.cy));
  281.  
  282.     dcMem.SelectObject(pOldBrush);
  283.     dcMem.SelectObject(pOldBitmap);
  284.     dcMem.DeleteDC();
  285. }
  286.  
  287. // The ball's size and displacement change according to the window size.
  288. void CBounceWnd::OnSize(UINT nType, int cx, int cy)
  289. {
  290.     LONG lScale;
  291.  
  292.     m_ptCenter.x = cx >> 1;
  293.     m_ptCenter.y = cy >> 1;
  294.     m_ptCenter.x += cx >> 3; // make the ball a little off-center
  295.  
  296.     lScale = min((LONG)cx * m_ptPixel.x,
  297.         (LONG)cy * m_ptPixel.y) >> 4;
  298.     m_sizeRadius.cx = (int)(lScale / m_ptPixel.x);
  299.     m_sizeRadius.cy = (int)(lScale / m_ptPixel.y);
  300.     m_sizeMove.cx = max(1, m_sizeRadius.cy >> 2);
  301.     m_sizeMove.cy = max(1, m_sizeRadius.cy >> 2);
  302.  
  303.     MakeNewBall();
  304.  
  305.     CWnd::OnSize(nType, cx, cy);
  306. }
  307.  
  308. /////////////////////////////////////////////////////////////////////////////
  309. // CBounceWnd commands
  310.  
  311. LRESULT CBounceWnd::OnDelegatedCmdMsg(WPARAM, LPARAM lParam)
  312. {
  313.     COnCmdMsg* pOnCmdMsg = (COnCmdMsg*)lParam;
  314.     return CWnd::OnCmdMsg(pOnCmdMsg->m_nID, pOnCmdMsg->m_nCode, pOnCmdMsg->m_pExtra,
  315.             pOnCmdMsg->m_pHandlerInfo);
  316. }
  317.  
  318. LRESULT CBounceWnd::OnPrepareToClose(WPARAM, LPARAM)
  319. {
  320.     DestroyWindow();
  321.     return 0;
  322. }
  323.  
  324. void CBounceWnd::OnUpdateColor(CCmdUI* pCmdUI)
  325. {
  326.     pCmdUI->SetCheck(pCmdUI->m_nID == m_nIDColor);
  327. }
  328.  
  329. void CBounceWnd::OnColor()
  330. {
  331.     m_nIDColor = LOWORD(GetCurrentMessage()->wParam);
  332.     m_clrBall = colorArray[m_nIDColor - IDM_BLACK];
  333.  
  334.     // Force the client area text to be repainted in the new color
  335.     MakeNewBall();
  336.     Invalidate();
  337. }
  338.  
  339. void CBounceWnd::OnCustomColor()
  340. {
  341.     CColorDialog dlgColor(m_clrBall);
  342.     if (dlgColor.DoModal() == IDOK)
  343.     {
  344.         m_clrBall = dlgColor.GetColor();
  345.         m_nIDColor = IDM_CUSTOM;
  346.         MakeNewBall();
  347.         Invalidate();
  348.     }
  349. }
  350.  
  351. // Change the ball's speed
  352. void CBounceWnd::ChangeSpeed()
  353. {
  354.     // re-create the timer
  355.     KillTimer(1);
  356.     if (!SetTimer(1, m_bFastSpeed ? 0 : 100, NULL))
  357.     {
  358.         MessageBox(_T("Not enough timers available for this window."),
  359.                 _T("MDI:Bounce"), MB_ICONEXCLAMATION | MB_OK);
  360.         DestroyWindow();
  361.     }
  362. }
  363.  
  364. // Animate the ball.
  365. void CBounceWnd::OnTimer(UINT /* wParam */)
  366. {
  367.     if (m_bmBall.m_hObject == NULL)
  368.         return;     // no bitmap for the ball
  369.  
  370.     CRect rcClient;
  371.     GetClientRect(rcClient);
  372.  
  373.     CClientDC dc(this);
  374.     CBitmap* pbmOld = NULL;
  375.  
  376.     CDC dcMem;
  377.     dcMem.CreateCompatibleDC(&dc);
  378.     pbmOld = dcMem.SelectObject(&m_bmBall);
  379.  
  380.     dc.BitBlt(m_ptCenter.x - m_sizeTotal.cx / 2,
  381.             m_ptCenter.y - m_sizeTotal.cy / 2,
  382.             m_sizeTotal.cx, m_sizeTotal.cy,
  383.             &dcMem, 0, 0, SRCCOPY);
  384.  
  385.     m_ptCenter += m_sizeMove;
  386.  
  387.     if ((m_ptCenter.x + m_sizeRadius.cx >= rcClient.right) ||
  388.         (m_ptCenter.x - m_sizeRadius.cx <= 0))
  389.     {
  390.         m_sizeMove.cx = -m_sizeMove.cx;
  391.     }
  392.  
  393.     if ((m_ptCenter.y + m_sizeRadius.cy >= rcClient.bottom) ||
  394.         (m_ptCenter.y - m_sizeRadius.cy <= 0))
  395.     {
  396.         m_sizeMove.cy = -m_sizeMove.cy;
  397.     }
  398.  
  399.     dcMem.SelectObject(pbmOld);
  400.     dcMem.DeleteDC();
  401. }
  402.  
  403. void CBounceWnd::OnUpdateSlow(CCmdUI* pCmdUI)
  404. {
  405.     pCmdUI->SetCheck(!m_bFastSpeed);
  406. }
  407.  
  408. void CBounceWnd::OnSlow()
  409. {
  410.     m_bFastSpeed = FALSE;
  411.     ChangeSpeed();
  412. }
  413.  
  414. void CBounceWnd::OnUpdateFast(CCmdUI* pCmdUI)
  415. {
  416.     pCmdUI->SetCheck(m_bFastSpeed);
  417. }
  418.  
  419. void CBounceWnd::OnFast()
  420. {
  421.     m_bFastSpeed = TRUE;
  422.     ChangeSpeed();
  423. }
  424.  
  425. void CBounceMDIChildWnd::OnSize(UINT nType, int cx, int cy)
  426. {
  427.     CMDIChildWnd::OnSize(nType, cx, cy);
  428.  
  429.     CWnd* pBounceWnd = GetDlgItem(IDC_BOUNCE_WND);
  430.     if (pBounceWnd == NULL)
  431.         return; // child CBounceWnd not created yet
  432.  
  433.     CRect rect;
  434.     GetClientRect(&rect);
  435.     pBounceWnd->SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(),
  436.         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  437. }
  438.  
  439. void CBounceWnd::OnLButtonDown(UINT, CPoint point)
  440. {
  441.     Invalidate();
  442.  
  443.     CRect rcClient;
  444.     GetClientRect(rcClient);
  445.  
  446.     point.x = min(point.x, rcClient.right - 2*m_sizeRadius.cx);
  447.     point.x = max(point.x, 2*m_sizeRadius.cx);
  448.     point.y = min(point.y, rcClient.bottom - 2*m_sizeRadius.cx);
  449.     point.y = max(point.y, 2*m_sizeRadius.cy);
  450.  
  451.     m_ptCenter = point;
  452. }
  453.