home *** CD-ROM | disk | FTP | other *** search
- // bounce.cpp : Defines the class behaviors for the Bounce child window.
- //
- // This is a part of the Microsoft Foundation Classes C++ library.
- // Copyright (C) 1992-1998 Microsoft Corporation
- // All rights reserved.
- //
- // This source code is only intended as a supplement to the
- // Microsoft Foundation Classes Reference and related
- // electronic documentation provided with the library.
- // See these sources for detailed information regarding the
- // Microsoft Foundation Classes product.
-
- #include "stdafx.h"
- #include "mdi.h"
- #include "bounce.h"
- #include "mtbounce.h"
-
- #define ABS(x) ((x) < 0? -(x) : (x) > 0? (x) : 0)
-
- BEGIN_MESSAGE_MAP(CBounceMDIChildWnd, CMDIChildWnd)
- //{{AFX_MSG_MAP(CBounceMDIChildWnd)
- ON_WM_SIZE()
- //}}AFX_MSG_MAP
- ON_MESSAGE(WM_USER_PREPARE_TO_CLOSE, OnPrepareToClose)
- END_MESSAGE_MAP()
-
- /////////////////////////////////////////////////////////////////////////////
- // CBounceMDIChildWnd
-
- CMenu CBounceMDIChildWnd::menu; // menu for all BOUNCE windows
-
- CBounceMDIChildWnd::CBounceMDIChildWnd()
- {
- }
-
- BOOL CBounceMDIChildWnd::Create(LPCTSTR szTitle, LONG style /* = 0 */,
- const RECT& rect /* = rectDefault */,
- CMDIFrameWnd* parent /* = NULL */)
- {
- // Setup the shared menu
- if (menu.m_hMenu == NULL)
- menu.LoadMenu(IDR_BOUNCE);
- m_hMenuShared = menu.m_hMenu;
-
- if (!CMDIChildWnd::Create(NULL, szTitle, style, rect, parent))
- return FALSE;
-
- // The default PostNcDestroy handler will delete this CBounceMDIChildWnd
- // object when destroyed. When Windows destroys the CBounceMDIChildWnd
- // window, it will also destroy the CBounceWnd, which is the child
- // window of the MDI child window, executing in the separate thread.
- // Finally, when the child CBounceWnd window is destroyed, the
- // CWinThread object will be automatically destroyed, as explained
- // in the comment for CBounceThread::InitInstance in mtbounce.cpp.
-
- CBounceThread* pBounceThread = new CBounceThread(m_hWnd);
- pBounceThread->CreateThread();
-
- return TRUE;
- }
-
-
- BOOL CBounceMDIChildWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
- AFX_CMDHANDLERINFO* pHandlerInfo)
- {
- // The default CFrameWnd::OnCmdMsg function is responsible for
- // dispatching menu commands to ON_COMMAND handlers and for
- // initializing menus and toolbars via ON_UPDATE_COMMAND_UI
- // handlers. For the MDI child window, all such dispatching
- // and command user interface initialization are done in the
- // main application thread. The CBounceWnd object processes
- // user interface events in a separate thread. This override,
- // CBounceMDIChildWnd::OnCmdMsg, delegates command handling to
- // the child CBounceWnd window. It cannot do this delegation
- // by simply calling CBounceWnd::OnCmdMsg, because doing so
- // would defeat the design of handling CBounceWnd user interface
- // events in the separate thread. If CBounceMDIChildWnd::OnCmdMsg
- // simply called CBounceWnd::OnCmdMsg, then the command handling
- // would be processed in the same main application thread as that
- // of the MDI child window.
- //
- // A common and safe way to make a call from one thread to another
- // is to call SendMessage for a window whose message pump runs in
- // a second thread. That is how CBounceMDIChildWnd::OnCmdMsg is
- // implemented. All of the OnCmdMsg parameters are gathered into
- // an application-defined (but re-usable) COnCmdMsg structure,
- // that is sent by reference to the CBounceWnd window via the
- // lParam of SendMessage.
-
-
- CWnd* pBounceWnd = (CBounceWnd*)GetDlgItem(IDC_BOUNCE_WND);
- if (pBounceWnd == NULL)
- return FALSE; // child CBounceWnd not created yet.
-
- // It is safe to create the COnCmdMsg on the stack because
- // it will not be deleted until the SendMessage call has
- // synchronously executed.
-
- COnCmdMsg oncmdmsg;
- oncmdmsg.m_nID = nID;
- oncmdmsg.m_nCode = nCode;
- oncmdmsg.m_pExtra = pExtra;
- oncmdmsg.m_pHandlerInfo = pHandlerInfo;
-
- // In addition to passing all of the OnCmdMsg parameters to
- // the CBounceWnd window via the SendMessage lParam, it is also
- // necessary to forward the implicit wParam originally passed
- // in the WM_COMMAND message for the menu command. The original
- // WM_COMMAND wParam is not a parameter of OnCmdMsg, but it can
- // be obtained by calling GetCurrentMessage. The wParam is needed by
- // the CBounceWnd::OnColor command handler to distinguish which
- // menu item, i.e., which color, was selected from the Color menu.
- //
- // The original single thread MDI sample application, the
- // CBounceWnd::OnColor command handler calls GetCurrentMessage
- // to retrieve the wParam from the WM_COMMAND message. To satisfy
- // that implementation of CBounceWnd::OnColor, we call
- // GetCurrentMessage() and forward the wParam in the user-defined
- // WM_USER_ONCMDMSG message. Alternatively, we could have
- // called GetMessageMsg and passed the wParam as an additional
- // member of the COnCmdMsg structure.
-
- return pBounceWnd->SendMessage(WM_USER_ONCMDMSG,
- GetCurrentMessage()->wParam, (LPARAM)&oncmdmsg);
- }
-
- BOOL CBounceMDIChildWnd::DestroyWindow()
- {
- OnPrepareToClose();
-
- return CMDIChildWnd::DestroyWindow();
- }
-
- LRESULT CBounceMDIChildWnd::OnPrepareToClose(WPARAM, LPARAM)
- {
- // Wait for the bounce thread to auto-destroy its CWinThread object
- // before continuing. This avoids a possible false memory leak
- // detection in debug mode when exiting the application. The false
- // memory leak might be reported if the main application thread
- // terminates sooner than the bounce thread auto-destroys its own
- // CWinThread object. See also comments for CBounceWnd::OnNcDestroy.
-
- CWnd* pBounceWnd = (CBounceWnd*)GetDlgItem(IDC_BOUNCE_WND);
- pBounceWnd->SendMessage(WM_USER_PREPARE_TO_CLOSE);
-
- WaitForSingleObject(CBounceThread::m_hEventBounceThreadKilled, INFINITE);
-
- return 0;
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // CBounceWnd
-
- BEGIN_MESSAGE_MAP(CBounceWnd, CWnd)
- //{{AFX_MSG_MAP(CBounceWnd)
- ON_WM_CREATE()
- ON_WM_SIZE()
- ON_WM_TIMER()
- ON_UPDATE_COMMAND_UI(IDM_RED, OnUpdateColor)
- ON_COMMAND(IDM_BLACK, OnColor)
- ON_COMMAND(IDM_CUSTOM, OnCustomColor)
- ON_UPDATE_COMMAND_UI(IDM_SLOW, OnUpdateSlow)
- ON_COMMAND(IDM_SLOW, OnSlow)
- ON_UPDATE_COMMAND_UI(IDM_FAST, OnUpdateFast)
- ON_COMMAND(IDM_FAST, OnFast)
- ON_WM_LBUTTONDOWN()
- ON_COMMAND(IDM_RED, OnColor)
- ON_COMMAND(IDM_GREEN, OnColor)
- ON_COMMAND(IDM_BLUE, OnColor)
- ON_COMMAND(IDM_WHITE, OnColor)
- ON_UPDATE_COMMAND_UI(IDM_WHITE, OnUpdateColor)
- ON_UPDATE_COMMAND_UI(IDM_GREEN, OnUpdateColor)
- ON_UPDATE_COMMAND_UI(IDM_CUSTOM, OnUpdateColor)
- ON_UPDATE_COMMAND_UI(IDM_BLUE, OnUpdateColor)
- ON_UPDATE_COMMAND_UI(IDM_BLACK, OnUpdateColor)
- ON_WM_DESTROY()
- //}}AFX_MSG_MAP
- ON_MESSAGE(WM_USER_ONCMDMSG, OnDelegatedCmdMsg)
- ON_MESSAGE(WM_USER_PREPARE_TO_CLOSE, OnPrepareToClose)
- END_MESSAGE_MAP()
-
- IMPLEMENT_DYNAMIC(CBounceWnd, CWnd)
-
- /////////////////////////////////////////////////////////////////////////////
- // CBounceWnd creation
-
- BOOL CBounceWnd::Create(LPCTSTR szTitle, LONG style, const RECT& rect, CWnd* parent)
- {
- // Register a custom WndClass and create a window.
- // This must be done because CBounceWnd has a custom cursor, and
- // no icon.
- LPCTSTR lpszBounceClass =
- AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
- LoadCursor(NULL, IDC_UPARROW),
- (HBRUSH)(COLOR_WINDOW+1),
- NULL);
-
- return CWnd::Create(lpszBounceClass, szTitle, style, rect, parent,
- IDC_BOUNCE_WND);
- }
-
- CBounceWnd::CBounceWnd()
- {
- m_nIDColor = IDM_BLACK;
- m_clrBall = RGB(0,0,0);
- m_bFastSpeed = FALSE;
- }
-
- void CBounceWnd::OnDestroy()
- {
- // The following code forces a condition where the exiting application will
- // report a memory leak of the bounce CWinThread object if the application
- // does not wait for the bounce thread to auto-destroy its CWinThread object.
- // See also CMDIChildBounceWnd::OnNcDestroy.
-
- #ifdef _DEBUG
- TRACE0("This is an intentional 1-second delay in terminating the bounce thread\n");
- Sleep(1000);
- #endif
- CWnd::OnDestroy();
- }
-
- // Set up the ball parameters and start a timer.
- int CBounceWnd::OnCreate(LPCREATESTRUCT /* p */)
- {
- if (!SetTimer(1, 100 /*start slow*/, NULL))
- {
- MessageBox(_T("Not enough timers available for this window."),
- _T("MDI:Bounce"), MB_ICONEXCLAMATION | MB_OK);
-
- // signal creation failure...
- return -1;
- }
-
- CDC* pDC = GetDC();
- m_ptPixel.x = pDC->GetDeviceCaps(ASPECTX);
- m_ptPixel.y = pDC->GetDeviceCaps(ASPECTY);
- ReleaseDC(pDC);
-
- // Note that we could call MakeNewBall here (which should be called
- // whenever the ball's speed, color or size has been changed), but the
- // OnSize member function is always called after the OnCreate. Since
- // the OnSize member has to call MakeNewBall anyway, we don't here.
-
- return 0;
- }
-
- // MakeNewBall:
- // Whenever a parameter changes which would affect the speed or appearance
- // of the ball, call this to regenerate the ball bitmap.
- //
- void CBounceWnd::MakeNewBall()
- {
- m_sizeTotal.cx = (m_sizeRadius.cx + ABS(m_sizeMove.cx)) << 1;
- m_sizeTotal.cy = (m_sizeRadius.cy + ABS(m_sizeMove.cy)) << 1;
-
- if (m_bmBall.m_hObject != NULL)
- m_bmBall.DeleteObject(); // get rid of old bitmap
-
- CClientDC dc(this);
- CDC dcMem;
- dcMem.CreateCompatibleDC(&dc);
-
- m_bmBall.CreateCompatibleBitmap(&dc, m_sizeTotal.cx, m_sizeTotal.cy);
- ASSERT(m_bmBall.m_hObject != NULL);
-
- CBitmap* pOldBitmap = dcMem.SelectObject(&m_bmBall);
-
- // draw a rectangle in the background (window) color
- CRect rect(0, 0, m_sizeTotal.cx, m_sizeTotal.cy);
- CBrush brBackground(::GetSysColor(COLOR_WINDOW));
- dcMem.FillRect(rect, &brBackground);
-
- CBrush brCross(HS_DIAGCROSS, 0L);
- CBrush* pOldBrush = dcMem.SelectObject(&brCross);
-
- dcMem.SetBkColor(m_clrBall);
- dcMem.Ellipse(ABS(m_sizeMove.cx), ABS(m_sizeMove.cy),
- m_sizeTotal.cx - ABS(m_sizeMove.cx),
- m_sizeTotal.cy - ABS(m_sizeMove.cy));
-
- dcMem.SelectObject(pOldBrush);
- dcMem.SelectObject(pOldBitmap);
- dcMem.DeleteDC();
- }
-
- // The ball's size and displacement change according to the window size.
- void CBounceWnd::OnSize(UINT nType, int cx, int cy)
- {
- LONG lScale;
-
- m_ptCenter.x = cx >> 1;
- m_ptCenter.y = cy >> 1;
- m_ptCenter.x += cx >> 3; // make the ball a little off-center
-
- lScale = min((LONG)cx * m_ptPixel.x,
- (LONG)cy * m_ptPixel.y) >> 4;
- m_sizeRadius.cx = (int)(lScale / m_ptPixel.x);
- m_sizeRadius.cy = (int)(lScale / m_ptPixel.y);
- m_sizeMove.cx = max(1, m_sizeRadius.cy >> 2);
- m_sizeMove.cy = max(1, m_sizeRadius.cy >> 2);
-
- MakeNewBall();
-
- CWnd::OnSize(nType, cx, cy);
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // CBounceWnd commands
-
- LRESULT CBounceWnd::OnDelegatedCmdMsg(WPARAM, LPARAM lParam)
- {
- COnCmdMsg* pOnCmdMsg = (COnCmdMsg*)lParam;
- return CWnd::OnCmdMsg(pOnCmdMsg->m_nID, pOnCmdMsg->m_nCode, pOnCmdMsg->m_pExtra,
- pOnCmdMsg->m_pHandlerInfo);
- }
-
- LRESULT CBounceWnd::OnPrepareToClose(WPARAM, LPARAM)
- {
- DestroyWindow();
- return 0;
- }
-
- void CBounceWnd::OnUpdateColor(CCmdUI* pCmdUI)
- {
- pCmdUI->SetCheck(pCmdUI->m_nID == m_nIDColor);
- }
-
- void CBounceWnd::OnColor()
- {
- m_nIDColor = LOWORD(GetCurrentMessage()->wParam);
- m_clrBall = colorArray[m_nIDColor - IDM_BLACK];
-
- // Force the client area text to be repainted in the new color
- MakeNewBall();
- Invalidate();
- }
-
- void CBounceWnd::OnCustomColor()
- {
- CColorDialog dlgColor(m_clrBall);
- if (dlgColor.DoModal() == IDOK)
- {
- m_clrBall = dlgColor.GetColor();
- m_nIDColor = IDM_CUSTOM;
- MakeNewBall();
- Invalidate();
- }
- }
-
- // Change the ball's speed
- void CBounceWnd::ChangeSpeed()
- {
- // re-create the timer
- KillTimer(1);
- if (!SetTimer(1, m_bFastSpeed ? 0 : 100, NULL))
- {
- MessageBox(_T("Not enough timers available for this window."),
- _T("MDI:Bounce"), MB_ICONEXCLAMATION | MB_OK);
- DestroyWindow();
- }
- }
-
- // Animate the ball.
- void CBounceWnd::OnTimer(UINT /* wParam */)
- {
- if (m_bmBall.m_hObject == NULL)
- return; // no bitmap for the ball
-
- CRect rcClient;
- GetClientRect(rcClient);
-
- CClientDC dc(this);
- CBitmap* pbmOld = NULL;
-
- CDC dcMem;
- dcMem.CreateCompatibleDC(&dc);
- pbmOld = dcMem.SelectObject(&m_bmBall);
-
- dc.BitBlt(m_ptCenter.x - m_sizeTotal.cx / 2,
- m_ptCenter.y - m_sizeTotal.cy / 2,
- m_sizeTotal.cx, m_sizeTotal.cy,
- &dcMem, 0, 0, SRCCOPY);
-
- m_ptCenter += m_sizeMove;
-
- if ((m_ptCenter.x + m_sizeRadius.cx >= rcClient.right) ||
- (m_ptCenter.x - m_sizeRadius.cx <= 0))
- {
- m_sizeMove.cx = -m_sizeMove.cx;
- }
-
- if ((m_ptCenter.y + m_sizeRadius.cy >= rcClient.bottom) ||
- (m_ptCenter.y - m_sizeRadius.cy <= 0))
- {
- m_sizeMove.cy = -m_sizeMove.cy;
- }
-
- dcMem.SelectObject(pbmOld);
- dcMem.DeleteDC();
- }
-
- void CBounceWnd::OnUpdateSlow(CCmdUI* pCmdUI)
- {
- pCmdUI->SetCheck(!m_bFastSpeed);
- }
-
- void CBounceWnd::OnSlow()
- {
- m_bFastSpeed = FALSE;
- ChangeSpeed();
- }
-
- void CBounceWnd::OnUpdateFast(CCmdUI* pCmdUI)
- {
- pCmdUI->SetCheck(m_bFastSpeed);
- }
-
- void CBounceWnd::OnFast()
- {
- m_bFastSpeed = TRUE;
- ChangeSpeed();
- }
-
- void CBounceMDIChildWnd::OnSize(UINT nType, int cx, int cy)
- {
- CMDIChildWnd::OnSize(nType, cx, cy);
-
- CWnd* pBounceWnd = GetDlgItem(IDC_BOUNCE_WND);
- if (pBounceWnd == NULL)
- return; // child CBounceWnd not created yet
-
- CRect rect;
- GetClientRect(&rect);
- pBounceWnd->SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(),
- SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
- }
-
- void CBounceWnd::OnLButtonDown(UINT, CPoint point)
- {
- Invalidate();
-
- CRect rcClient;
- GetClientRect(rcClient);
-
- point.x = min(point.x, rcClient.right - 2*m_sizeRadius.cx);
- point.x = max(point.x, 2*m_sizeRadius.cx);
- point.y = min(point.y, rcClient.bottom - 2*m_sizeRadius.cx);
- point.y = max(point.y, 2*m_sizeRadius.cy);
-
- m_ptCenter = point;
- }
-