20.6.1 Creating the Functions

You can create the library functions by following the description given in Chapter 6, “Cursors.” Simply copy the statements used to make the graphics selection into the corresponding functions. Also, to make the selection functions more flexible, add the additional block capability.

After you change it, the StartSelection function should look like this:

void FAR PASCAL StartSelection(hWnd, ptCurrent, lpSelectRect, fFlags)
HWND hWnd;
POINT ptCurrent;
LPRECT lpSelectRect;
int fFlags;
{
    if (lpSelectRect->left != lpSelectRect->right ||
            lpSelectRect->top != lpSelectRect->bottom)
        ClearSelection(hWnd, lpSelectRect, fFlags);

    lpSelectRect->right = ptCurrent.x;
    lpSelectRect->bottom = ptCurrent.y;

    /* If you are extending the box, invert the current rectangle. */

    if ((fFlags & SL_SPECIAL) == SL_EXTEND)
        ClearSelection(hWnd, lpSelectRect, fFlags);

    /* Otherwise, set origin to current location. */

    else {
        lpSelectRect->left = ptCurrent.x;
        lpSelectRect->top = ptCurrent.y;
    }
    SetCapture(hWnd);
}

This function receives four parameters: a window handle, hWnd; the current mouse location, ptCurrent; a long pointer to the selection rectangle, lpSelectRect; and the selection flags, fFlags.

The first step is to clear the selection if the selection rectangle is not empty. The IsRectEmpty function returns TRUE if the rectangle is empty. The StartSelection function clears the selection by calling the ClearSelection function, which is also in this library.

The next step is to initialize the selection rectangle. The StartSelection function extends the selection (leaving the upper-left corner of the selection unchanged), if the SS_EXTEND bit in the fFlags argument is set. Otherwise, it sets the upper-left and lower-right corners of the selection rectangle to the current mouse location. The SetCapture function directs all subsequent mouse input to the window even if the cursor moves outside of the window. This is to ensure that the selection process continues uninterrupted. To call this function, an application would use the following statements:

case WM_LBUTTONDOWN:

    fTrack = TRUE;    /* User has pressed the left button. */
    StartSelection(hWnd, MAKEPOINT(lParam), &Rect,
        (wParam & MK_SHIFT) ? SL_EXTEND | Shape : Shape);
    break;

After you change it, the UpdateSelection function should look like this:

void FAR PASCAL UpdateSelection(hWnd, ptCurrent, lpSelectRect, fFlags)
HWND hWnd;
POINT ptCurrent;
LPRECT lpSelectRect;
int fFlags;
{
    HDC hDC;
    short OldROP;

    hDC = GetDC(hWnd);

    switch (fFlags & SL_TYPE) {

        case SL_BOX:

            OldROP = SetROP2(hDC, R2_NOTXORPEN);
            MoveTo(hDC, lpSelectRect->left, lpSelectRect->top);
            LineTo(hDC, lpSelectRect->right, lpSelectRect->top);
            LineTo(hDC, lpSelectRect->right, lpSelectRect->bottom);
            LineTo(hDC, lpSelectRect->left, lpSelectRect->bottom);
            LineTo(hDC, lpSelectRect->left, lpSelectRect->top);
            LineTo(hDC, ptCurrent.x, lpSelectRect->top);
            LineTo(hDC, ptCurrent.x, ptCurrent.y);
            LineTo(hDC, lpSelectRect->left, ptCurrent.y);
            LineTo(hDC, lpSelectRect->left, lpSelectRect->top);
            SetROP2(hDC, OldROP);
            break;

        case SL_BLOCK:

            PatBlt(hDC,
                lpSelectRect->left,
                lpSelectRect->bottom,
                lpSelectRect->right - lpSelectRect->left,
                ptCurrent.y - lpSelectRect->bottom,
                DSTINVERT);
            PatBlt(hDC,
                lpSelectRect->right,
                lpSelectRect->top,
                ptCurrent.x - lpSelectRect->right,
                ptCurrent.y - lpSelectRect->top,
                DSTINVERT);
            break;
    }
    lpSelectRect->right = ptCurrent.x;
    lpSelectRect->bottom = ptCurrent.y;
    ReleaseDC(hWnd, hDC);
}

As the user makes the selection, the UpdateSelection function provides feedback about the user's progress. For the box selection, the function first clears the current box by drawing over it, and then draws the new box. This requires eight calls to the LineTo function.

To update a block selection, the UpdateSelection function inverts the rectangle by using the PatBlt function. To avoid flicker while the user selects, UpdateSelection inverts only the portions of the rectangle that are different from the previous selection rectangle. This means the function inverts two separate pieces of the screen. It “assumes” that the only area that needs inverting is the area between the previous and current mouse locations. The following figure shows the typical coordinates for describing the areas being inverted:

To set the origin of the area to be inverted, the first PatBlt call inverts the leftmost rectangle by using lpSelectRect->left (the location of the x-coordinate of the mouse when its button was first pressed) and lpSelectRect->bottom (the most recent update of the y-coordinate location). The width of the first area is determined by subtracting lpSelectRect->left from lpSelectRect->right (the most recent update of the x-coordinate location). The height of this area is determined by subtracting lpSelectRect->bottom from ptCurrent.y (the current y-coordinate location).

The second PatBlt call inverts the rightmost rectangle by using lpSelectRect-> right and lpSelectRect->top to set the origin of the area to be inverted. The width of this second area is determined by subtracting lpSelectRect->bottom from ptCurrent.x. The height of the area is determined by subtracting lpSelectRect->top from ptCurrent.y.

When the selection updating is complete, the values lpSelectRect->right and lpSelectRect->bottom are updated by assigning them the current values contained in ptCurrent.

To update a box selection, the application should call the UpdateSelection function as follows:

case WM_MOUSEMOVE:

    if (fTrack)
        UpdateSelection(hWnd, MAKEPOINT(lParam), &Rect, Shape);
    break;

After you change it, the EndSelection function should look like this:

void FAR PASCAL EndSelection(ptCurrent, lpSelectRect)
POINT ptCurrent;
LPRECT lpSelectRect;
{
    lpSelectRect->right = ptCurrent.x;
    lpSelectRect->bottom = ptCurrent.y;
    ReleaseCapture();
}

The EndSelection function saves the current mouse position in the selection rectangle. The ReleaseCapture function is required because a corresponding SetCapture function was called. In general, you should release the mouse immediately after mouse capture is no longer needed.

Finally, when the user releases the left mouse button, the application should call the EndSelection function to save the final point:

case WM_LBUTTONUP:

    if (fTrack)
        EndSelection(MAKEPOINT(lParam), &Rect);
    fTrack = FALSE;
    break;

After you change it, the ClearSelection function should look like this:

void FAR PASCAL ClearSelection(hWnd, lpSelectRect, fFlags)
HWND hWnd;
LPRECT lpSelectRect;
int fFlags;
{
    HDC hDC;
    short OldROP;

    hDC = GetDC(hWnd);
    switch (fFlags & SL_TYPE) {

        case SL_BOX:

            OldROP = SetROP2(hDC, R2_NOTXORPEN);
            MoveTo(hDC, lpSelectRect->left, lpSelectRect->top);
            LineTo(hDC, lpSelectRect->right, lpSelectRect->top);
            LineTo(hDC, lpSelectRect->right, lpSelectRect->bottom);
            LineTo(hDC, lpSelectRect->left, lpSelectRect->bottom);
            LineTo(hDC, lpSelectRect->left, lpSelectRect->top);
            SetROP2(hDC, OldROP);
            break;



        case SL_BLOCK:

            PatBlt(hDC,
                lpSelectRect->left,
                lpSelectRect->top,
                lpSelectRect->right - lpSelectRect->left,
                lpSelectRect->bottom - lpSelectRect->top,
                DSTINVERT);
            break;
    }
    ReleaseDC(hWnd, hDC);
}

Clearing a box selection means removing it from the screen. You can remove the outline by drawing over it with the XOR pen. Clearing a block selection means restoring the inverted screen to its previous state. You can restore the inverted screen by inverting the entire selection.