37.2.2 Example: Drawing Lines With the Mouse

The example in this section demonstrates how to track the mouse cursor. It contains portions of a window procedure that allows the user to draw lines in a window's client area by dragging the mouse.

When the window procedure receives a WM_LBUTTONDOWN message, it captures the mouse and saves the coordinates of the mouse cursor, using the coordinates as the starting point of the line. It also uses the ClipCursor function to confine the mouse cursor to the client area during the line drawing operation.

During the first WM_MOUSEMOVE message, the window procedure draws a line from the starting point to the current position of the mouse cursor. During subsequent WM_MOUSEMOVE messages, the window procedure erases the previous line by drawing over it with an inverted pen color, then draws a new line from the starting point to the new position of the mouse cursor.

The WM_LBUTTONUP message signals the end of the drawing operation. The window procedure releases the mouse capture and frees the mouse from the client area.

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

UINT wParam;

LONG lParam;

{

HDC hdc; /* handle of device context */

RECT rcClient; /* client area rectangle*/

POINT ptClientUL; /* client upper-left corner*/

POINT ptClientLR; /* client lower-right corner */

static POINTS ptsBegin; /* beginning point */

static POINTS ptsEnd; /* new end point */

static POINTS ptsPrevEnd; /* previous end point */

static BOOL fPrevLine = FALSE; /* previous line exist flag */

switch (uMsg) {

case WM_LBUTTONDOWN:

/* Capture mouse input. */

SetCapture(hwndMain);

/*

* Retrieve the screen coordinates of the client area

* and convert the screen coordinates into client

* coordinates.

*/

GetClientRect(hwndMain, &rcClient);

ptClientUL.x = rcClient.left;

ptClientUL.y = rcClient.top;

ptClientLR.x = rcClient.right + 1;

ptClientLR.y = rcClient.bottom + 1;

ClientToScreen(hwndMain, &ptClientUL);

ClientToScreen(hwndMain, &ptClientLR);

/*

* Copy the client coordinates of the client area back

* rcClient structure. Confine the mouse cursor to the

* client area by passing the rcClient structure to the

* ClipCursor function.

*/

SetRect(&rcClient, ptClientUL.x, ptClientUL.y,

ptClientLR.x, ptClientLR.y);

ClipCursor(&rcClient);

/*

* Convert the coordinates of the mouse cursor into a

* POINTS structure. The is the beginning point of the

* line that is drawn during WM_MOUSEMOVE message.

*/

ptsBegin = MAKEPOINTS(lParam);

return 0;

case WM_MOUSEMOVE:

/*

* When mouse movement occurs, the left mouse button

* must be down for lines to be drawn.

*/

if (wParam & MK_LBUTTON) {

/*

* Retrieve a device context (DC) for the client

* area.

*/

hdc = GetDC(hwndMain);

/*

* The following function ensures that pixels of

* the previously drawn line are set to white and

* the pixels of the new line are set to black.

*/

SetROP2(hdc, R2_NOTXORPEN);

/*

* If a line was drawn during an earlier

* WM_MOUSEMOVE message, draw over it. This erases

* the line by setting the color of its pixels to

* white.

*/

if (fPrevLine) {

MoveToEx(hdc, ptsBegin.x, ptsBegin.y,

(LPPOINT) NULL);

LineTo(hdc, ptsPrevEnd.x, ptsPrevEnd.y);

}

/*

* Convert the current mouse coordinates to a

* POINTS structure, then draw a new line.

*/

ptsEnd = MAKEPOINTS(lParam);

MoveToEx(hdc, ptsBegin.x, ptsBegin.y,

(LPPOINT) NULL);

LineTo(hdc, ptsEnd.x, ptsEnd.y);

/*

* Set the previous line flag, save the ending

* point of the new line, and release the DC.

*/

fPrevLine = TRUE;

ptsPrevEnd = ptsEnd;

ReleaseDC(hwndMain, hdc);

}

break;

case WM_LBUTTONUP:

/*

* The user has finished drawing the line. Reset the

* previous line flag, release the mouse cursor, and

* release the mouse capture.

*/

fPrevLine = FALSE;

ClipCursor(NULL);

ReleaseCapture();

return 0;

case WM_DESTROY:

PostQuitMessage(0);

break;

.

. /* Process other messages. */

.