home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / MTMDI.PAK / BOUNCE.CPP next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  13.7 KB  |  457 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-1995 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.         #ifdef _MAC
  195.             LoadCursor(NULL, IDC_ARROW),  // Mac does not support custom cursor.
  196.         #else
  197.             LoadCursor(NULL, IDC_UPARROW),
  198.         #endif
  199.             (HBRUSH)(COLOR_WINDOW+1),
  200.             NULL);
  201.  
  202.     return CWnd::Create(lpszBounceClass, szTitle, style, rect, parent,
  203.         IDC_BOUNCE_WND);
  204. }
  205.  
  206. CBounceWnd::CBounceWnd()
  207. {
  208.     m_nIDColor = IDM_BLACK;
  209.     m_clrBall = RGB(0,0,0);
  210.     m_bFastSpeed = FALSE;
  211. }
  212.  
  213. void CBounceWnd::OnDestroy()
  214. {
  215.     // The following code forces a condition where the exiting application will
  216.     // report a memory leak of the bounce CWinThread object if the application
  217.     // does not wait for the bounce thread to auto-destroy its CWinThread object.
  218.     // See also CMDIChildBounceWnd::OnNcDestroy.
  219.  
  220. #ifdef _DEBUG
  221.     TRACE0("This is an intentional 1-second delay in terminating the bounce thread\n");
  222.     Sleep(1000);
  223. #endif
  224.     CWnd::OnDestroy();
  225. }
  226.  
  227. // Set up the ball parameters and start a timer.
  228. int CBounceWnd::OnCreate(LPCREATESTRUCT /* p */)
  229. {
  230.     if (!SetTimer(1, 100 /*start slow*/, NULL))
  231.     {
  232.         MessageBox(_T("Not enough timers available for this window."),
  233.                 _T("MDI:Bounce"), MB_ICONEXCLAMATION | MB_OK);
  234.  
  235.         // signal creation failure...
  236.         return -1;
  237.     }
  238.  
  239.     CDC* pDC = GetDC();
  240.     m_ptPixel.x = pDC->GetDeviceCaps(ASPECTX);
  241.     m_ptPixel.y = pDC->GetDeviceCaps(ASPECTY);
  242.     ReleaseDC(pDC);
  243.  
  244.     // Note that we could call MakeNewBall here (which should be called
  245.     // whenever the ball's speed, color or size has been changed), but the
  246.     // OnSize member function is always called after the OnCreate. Since
  247.     // the OnSize member has to call MakeNewBall anyway, we don't here.
  248.  
  249.     return 0;
  250. }
  251.  
  252. // MakeNewBall:
  253. // Whenever a parameter changes which would affect the speed or appearance
  254. // of the ball, call this to regenerate the ball bitmap.
  255. //
  256. void CBounceWnd::MakeNewBall()
  257. {
  258.     m_sizeTotal.cx = (m_sizeRadius.cx + ABS(m_sizeMove.cx)) << 1;
  259.     m_sizeTotal.cy = (m_sizeRadius.cy + ABS(m_sizeMove.cy)) << 1;
  260.  
  261.     if (m_bmBall.m_hObject != NULL)
  262.         m_bmBall.DeleteObject();        // get rid of old bitmap
  263.  
  264.     CClientDC dc(this);
  265.     CDC dcMem;
  266.     dcMem.CreateCompatibleDC(&dc);
  267.  
  268.     m_bmBall.CreateCompatibleBitmap(&dc, m_sizeTotal.cx, m_sizeTotal.cy);
  269.     ASSERT(m_bmBall.m_hObject != NULL);
  270.  
  271.     CBitmap* pOldBitmap = dcMem.SelectObject(&m_bmBall);
  272.  
  273.     // draw a rectangle in the background (window) color
  274.     CRect rect(0, 0, m_sizeTotal.cx, m_sizeTotal.cy);
  275.     CBrush brBackground(::GetSysColor(COLOR_WINDOW));
  276.     dcMem.FillRect(rect, &brBackground);
  277.  
  278.     CBrush brCross(HS_DIAGCROSS, 0L);
  279.     CBrush* pOldBrush = dcMem.SelectObject(&brCross);
  280.  
  281.     dcMem.SetBkColor(m_clrBall);
  282.     dcMem.Ellipse(ABS(m_sizeMove.cx), ABS(m_sizeMove.cy),
  283.         m_sizeTotal.cx - ABS(m_sizeMove.cx),
  284.         m_sizeTotal.cy - ABS(m_sizeMove.cy));
  285.  
  286.     dcMem.SelectObject(pOldBrush);
  287.     dcMem.SelectObject(pOldBitmap);
  288.     dcMem.DeleteDC();
  289. }
  290.  
  291. // The ball's size and displacement change according to the window size.
  292. void CBounceWnd::OnSize(UINT nType, int cx, int cy)
  293. {
  294.     LONG lScale;
  295.  
  296.     m_ptCenter.x = cx >> 1;
  297.     m_ptCenter.y = cy >> 1;
  298.     m_ptCenter.x += cx >> 3; // make the ball a little off-center
  299.  
  300.     lScale = min((LONG)cx * m_ptPixel.x,
  301.         (LONG)cy * m_ptPixel.y) >> 4;
  302.     m_sizeRadius.cx = (int)(lScale / m_ptPixel.x);
  303.     m_sizeRadius.cy = (int)(lScale / m_ptPixel.y);
  304.     m_sizeMove.cx = max(1, m_sizeRadius.cy >> 2);
  305.     m_sizeMove.cy = max(1, m_sizeRadius.cy >> 2);
  306.  
  307.     MakeNewBall();
  308.  
  309.     CWnd::OnSize(nType, cx, cy);
  310. }
  311.  
  312. /////////////////////////////////////////////////////////////////////////////
  313. // CBounceWnd commands
  314.  
  315. LRESULT CBounceWnd::OnDelegatedCmdMsg(WPARAM, LPARAM lParam)
  316. {
  317.     COnCmdMsg* pOnCmdMsg = (COnCmdMsg*)lParam;
  318.     return CWnd::OnCmdMsg(pOnCmdMsg->m_nID, pOnCmdMsg->m_nCode, pOnCmdMsg->m_pExtra,
  319.             pOnCmdMsg->m_pHandlerInfo);
  320. }
  321.  
  322. LRESULT CBounceWnd::OnPrepareToClose(WPARAM, LPARAM)
  323. {
  324.     DestroyWindow();
  325.     return 0;
  326. }
  327.  
  328. void CBounceWnd::OnUpdateColor(CCmdUI* pCmdUI)
  329. {
  330.     pCmdUI->SetCheck(pCmdUI->m_nID == m_nIDColor);
  331. }
  332.  
  333. void CBounceWnd::OnColor()
  334. {
  335.     m_nIDColor = LOWORD(GetCurrentMessage()->wParam);
  336.     m_clrBall = colorArray[m_nIDColor - IDM_BLACK];
  337.  
  338.     // Force the client area text to be repainted in the new color
  339.     MakeNewBall();
  340.     Invalidate();
  341. }
  342.  
  343. void CBounceWnd::OnCustomColor()
  344. {
  345.     CColorDialog dlgColor(m_clrBall);
  346.     if (dlgColor.DoModal() == IDOK)
  347.     {
  348.         m_clrBall = dlgColor.GetColor();
  349.         m_nIDColor = IDM_CUSTOM;
  350.         MakeNewBall();
  351.         Invalidate();
  352.     }
  353. }
  354.  
  355. // Change the ball's speed
  356. void CBounceWnd::ChangeSpeed()
  357. {
  358.     // re-create the timer
  359.     KillTimer(1);
  360.     if (!SetTimer(1, m_bFastSpeed ? 0 : 100, NULL))
  361.     {
  362.         MessageBox(_T("Not enough timers available for this window."),
  363.                 _T("MDI:Bounce"), MB_ICONEXCLAMATION | MB_OK);
  364.         DestroyWindow();
  365.     }
  366. }
  367.  
  368. // Animate the ball.
  369. void CBounceWnd::OnTimer(UINT /* wParam */)
  370. {
  371.     if (m_bmBall.m_hObject == NULL)
  372.         return;     // no bitmap for the ball
  373.  
  374.     CRect rcClient;
  375.     GetClientRect(rcClient);
  376.  
  377.     CClientDC dc(this);
  378.     CBitmap* pbmOld = NULL;
  379.  
  380.     CDC dcMem;
  381.     dcMem.CreateCompatibleDC(&dc);
  382.     pbmOld = dcMem.SelectObject(&m_bmBall);
  383.  
  384.     dc.BitBlt(m_ptCenter.x - m_sizeTotal.cx / 2,
  385.             m_ptCenter.y - m_sizeTotal.cy / 2,
  386.             m_sizeTotal.cx, m_sizeTotal.cy,
  387.             &dcMem, 0, 0, SRCCOPY);
  388.  
  389.     m_ptCenter += m_sizeMove;
  390.  
  391.     if ((m_ptCenter.x + m_sizeRadius.cx >= rcClient.right) ||
  392.         (m_ptCenter.x - m_sizeRadius.cx <= 0))
  393.     {
  394.         m_sizeMove.cx = -m_sizeMove.cx;
  395.     }
  396.  
  397.     if ((m_ptCenter.y + m_sizeRadius.cy >= rcClient.bottom) ||
  398.         (m_ptCenter.y - m_sizeRadius.cy <= 0))
  399.     {
  400.         m_sizeMove.cy = -m_sizeMove.cy;
  401.     }
  402.  
  403.     dcMem.SelectObject(pbmOld);
  404.     dcMem.DeleteDC();
  405. }
  406.  
  407. void CBounceWnd::OnUpdateSlow(CCmdUI* pCmdUI)
  408. {
  409.     pCmdUI->SetCheck(!m_bFastSpeed);
  410. }
  411.  
  412. void CBounceWnd::OnSlow()
  413. {
  414.     m_bFastSpeed = FALSE;
  415.     ChangeSpeed();
  416. }
  417.  
  418. void CBounceWnd::OnUpdateFast(CCmdUI* pCmdUI)
  419. {
  420.     pCmdUI->SetCheck(m_bFastSpeed);
  421. }
  422.  
  423. void CBounceWnd::OnFast()
  424. {
  425.     m_bFastSpeed = TRUE;
  426.     ChangeSpeed();
  427. }
  428.  
  429. void CBounceMDIChildWnd::OnSize(UINT nType, int cx, int cy)
  430. {
  431.     CMDIChildWnd::OnSize(nType, cx, cy);
  432.  
  433.     CWnd* pBounceWnd = GetDlgItem(IDC_BOUNCE_WND);
  434.     if (pBounceWnd == NULL)
  435.         return; // child CBounceWnd not created yet
  436.  
  437.     CRect rect;
  438.     GetClientRect(&rect);
  439.     pBounceWnd->SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(),
  440.         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  441. }
  442.  
  443. void CBounceWnd::OnLButtonDown(UINT, CPoint point)
  444. {
  445.     Invalidate();
  446.  
  447.     CRect rcClient;
  448.     GetClientRect(rcClient);
  449.  
  450.     point.x = min(point.x, rcClient.right - 2*m_sizeRadius.cx);
  451.     point.x = max(point.x, 2*m_sizeRadius.cx);
  452.     point.y = min(point.y, rcClient.bottom - 2*m_sizeRadius.cx);
  453.     point.y = max(point.y, 2*m_sizeRadius.cy);
  454.  
  455.     m_ptCenter = point;
  456. }
  457.