Starting a Graphics Selection

Because graphics can be virtually any shape, they are potentially more difficult to select than simple text. The simplest approach to selecting graphics is to let the user “stretch” a selection rectangle so that it encloses the desired information.

This section explains how to use the “rubber rectangle” method of selecting graphics. You can use the messages WM_LBUTTONDOWN, WM_LBUTTONUP, and WM_MOUSEMOVE to create the rectangle. This lets the user create the selection by choosing a point, pressing the left button, and dragging to another point before releasing. While the user drags the mouse, the application can provide instant feedback by inverting the border of the rectangle described by the starting and current points.

For this method, you start the selection when you receive the message WM_LBUTTONDOWN. You need to do three things: capture the mouse input, save the starting (original) point, and save the current point, as follows:

BOOL bTrack = FALSE; /* these are global variables */

int OrgX = 0, OrgY = 0;

int PrevX = 0, PrevY = 0;

int X = 0, Y = 0;

.

.

.

1 case WM_LBUTTONDOWN:

bTrack = TRUE;

PrevX = LOWORD(lParam);

PrevY = HIWORD(lParam);

OrgX = LOWORD(lParam);

OrgY = HIWORD(lParam);

2 InvalidateRect (hWnd, NULL, TRUE);

UpdateWindow (hWnd);

/* Capture all input even if the mouse goes outside of window */

3 SetCapture(hWnd);

break;

1 When the application receives the WM_LBUTTONDOWN message, the bTrack variable is set to TRUE to indicate that a selection is in progress. As with any mouse message, the lParam parameter contains the current x- and y-coordinates of the mouse in the low- and high-order words, respectively. These are saved as the origin x and y values, OrgX and OrgY, as well as the previous values, PrevX and PrevY. The PrevX and PrevY variables will be updated immediately on the next WM_MOUSEMOVE message. The OrgX and OrgY variables remain unchanged and will be used to determine a corner of the bitmap to be copied. (The variables bTrack, OrgX, OrgY, PrevX, and PrevY must be global variables.)
2 To provide immediate visual feedback in response to the WM_LBUTTONDOWN message, the application invalidates the screen and notifies the window function that it needs to repaint the screen. It does this by calling InvalidateRect and UpdateWindow.
3 The SetCapture function directs all subsequent mouse input to the window even if the cursor moves outside of the window. This ensures that the selection process will continue uninterrupted.

Respond to the WM_PAINT message by redrawing the invalidated portions of the screen:

case WM_PAINT:

{

PAINTSTRUCT ps;

HDC hDC;

hDC = BeginPaint (hWnd, &ps);

if(OrgX != PrevX || OrgY != PrevY) {

MoveTo(hDC, OrgX, OrgY);

LineTo(hDC, OrgX, PrevY);

LineTo(hDC, PrevX, PrevY);

LineTo(hDC, PrevX, OrgY);

LineTo(hDC, OrgX, OrgY);

}

EndPaint (hWnd, &ps);

}

break;

In some applications, you might want to be able to extend an existing selection. One way to do this is to have the user hold the SHIFT key when making a selection. Since the wParam parameter contains a flag that specifies whether the SHIFT key is being pressed, it is easy to check for this, and to extend the selection as necessary. In this case, extending a selection means preserving its previous OrgX and OrgY values when you start it. To do this, change the WM_LBUTTONDOWN case so it looks like this:

case WM_LBUTTONDOWN:

bTrack = TRUE;

PrevX = LOWORD(lParam);

PrevY = HIWORD(lParam);

if(!(wParam & MK_SHIFT)) { /* If shift key is

not pressed */

OrgX = LOWORD(lParam);

OrgY = HIWORD(lParam);

}

InvalidateRect (hWnd, NULL, TRUE);

UpdateWindow (hWnd);

/* Capture all input even if the mouse goes

outside the window */

SetCapture(hWnd);

break;