28.1 Mouse messages

Processing a mouse message

Windows generates a number of mouse-related messages, including WM_MOUSEMOVE and WM_ONLBUTTONDOWN. MFC handles these two with message handlers normally called OnMouseMove and OnLButtonDown.

The programmer is free to decide which class is to hold the handler methods. One way to do this is to open the Class View and to right click on the name of class that is supposed to handle the mouse message. In Visual Studio.NET you then select Properties... and click on the Message button in the Properties dialog. (In Version 6.0, you right click the class and select Add Windows Message Handler....)

In any case, the default message handler you'll get for WM_MOUSEMOVE will look like this.

void CPopView::OnMouseMove(UINT nFlags, CPoint point) 
    // TODO: Add your message handler code here and/or call default 
    CView::OnMouseMove(nFlags, point); 

We override this as follows, telling the view to pass the mouse click right on to the game object.

void CPopView::OnLButtonDown(UINT nFlags, CPoint point) 
//My Code. RR. 
    SetCapture(); /* This is so that as long as the mouse button is 
        down, this window gets messages from the mouse even when the 
        mouse is outside the window. */ 
    pgame()->onLButtonDown(this, nFlags, point); 
/* Don't need to figure out the game world pos, it's set as pgame()
    >pbiota()->_cursorpos in OnSetCursor. */ 

Our special cGame::onLButtonDown (cPopView *pview, UINT nFlags, cPointpoint) code figures out which critter you should think of as being closest to the click with a line like this: cCritter* pTouched = _pbiota->pickClosest (pview->pgraphics()->pixelToSightLine(point.x, point.y));. And then, depending on the type of cursor currently active, the cGame does something to the pTouched critter. See the source later in this chapter for details.

We handle the OnMouseMove case in a different way. The cPopView::SetCursor has code to figure out the game world position closest to the pick point that also lies in the plane of the player critter.

void CPopView::OnMouseMove(UINT nFlags, CPoint point) 
    /* Normally I track the cursor position in OnSetCursor, but this 
        method doesn't get called during dragging, so I need to do it 
        here */ 
    if ((nFlags & MK_LBUTTON) || (nFlags & MK_RBUTTON) ) //You're 
    pgame()->onMouseMove(this, nFlags, point); 

Calling the OnDraw method

Often a mouse action changes the world in such a way that we need to redisplay it. This isn't an issue in our animated Pop framework, where the world is constantly being redisplayed. But in other programs it is an issue. The way to force a redisplay of all view windows from a given view's method is a line of the form pDoc->UpdateAllViews(NULL). You never try to call OnDraw directly, as this is a severe Windows no-no. One reason is that we'd have to do too much work in getting the right kind of CDC *pDC ready for the OnDraw argument, but there's some other considerations as well (which we won't go into).

No, the way to have, say, an OnMouseMove call OnDraw is to use an indirect approach. We call the Invalidate() function. But in order to keep our Document-View architecture smoothly functioning, we're not going to let OnMouseMove call Invalidate directly. Instead we're going to go all around Robin Hood's barn and do this.

  • CPopView::OnMouseMove might kill off a critter say, and then call

  • CPopDoc::UpdateAllViews, which calls

  • CPopView::OnUpdate, which calls

  • CPopView::Invalidate, which calls

  • CPopView::OnDraw to draw the new state of affairs.

The pDoc->UpdateAllViews(NULL) line in the code has the default effect of telling the CPopDoc to send an UpdateView call to each of its views.

What about the CPopView::OnUpdate method? As it turns out, the default CView behavior for the method is just what you'd want; to call the Invalidate() method.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference