Microsoft KB Archive/314394

= HOWTO: Perform Rubber Band Selection Without ROP Codes =

Article ID: 314394

Article Last Modified on 4/3/2006

-

APPLIES TO


 * Microsoft Platform Software Development Kit-January 2000 Edition
 * Microsoft Windows XP Professional
 * Microsoft Windows XP Professional for Itanium-based systems

-



This article was previously published under Q314394



SUMMARY
There are times when it is not desirable to use raster operation (ROP) codes to perform a rubber band selection. This article describes an alternative approach to using a rubber band selection that does not use ROP codes.



MORE INFORMATION
The following code captures and replaces the edges of the rubber band selection rectangle rather than using ROP codes to invert and restore them.

The code is written to retrieve a selection rectangle from the user in response to a WM_LBUTTONDOWN message. You can left-click and hold the mouse button while dragging the cursor to adjust the size of the selection area. typedef struct { HDC    hdcMem;     // For capturing/restoring edge bitmaps HBRUSH hbr;        // For painting the selection rectangle HBITMAP hbmLeft;   // Holds the original bits of left side HBITMAP hbmTop;    // Holds the original bits of top side HBITMAP hbmRight;  // Holds the original bits of right side HBITMAP hbmBottom; // Holds the original bits of bottom side int    iThickness; // Thickness of the edge border } RUBBERBANDINFO, *LPRUBBERBANDINFO;

/**** NormalizeRect *******************************************\ \****************************************************************/ void NormalizeRect (LPRECT prc) {   int temp;
 * Makes sure that the top and left values of a rectangle are    *
 * lower than the values of the right and bottom.                *
 * lower than the values of the right and bottom.                *

// Swap left and right. if (prc->right < prc->left) { temp      = prc->right; prc->right = prc->left; prc->left = temp; }   // Swap top and bottom. if (prc->bottom < prc->top) { temp       = prc->bottom; prc->bottom = prc->top; prc->top   = temp; } }

/**** RBInitialize ********************************************\ \****************************************************************/ LPRUBBERBANDINFO RBInitialize(HDC hdcScr, int iThickness) {   LPRUBBERBANDINFO lprbi; LOGBRUSH lb = {BS_PATTERN8X8, 0, HS_BDIAGONAL}; HBITMAP hbmBrush; HDC hdcBrush; int x,y;
 * Initializes a RUBBERBANDINFO structure in preparation for    *
 * rubber banding.                                              *
 * rubber banding.                                              *

// Limit thickness to 1 to 8 pixels. if ((iThickness > 8) || (iThickness < 1)) return FALSE;

// Allocate space for the structure. lprbi = (LPRUBBERBANDINFO)GlobalAlloc(GPTR, sizeof(RUBBERBANDINFO)); if (!lprbi) return NULL;

// Make an interesting 8x8 bitmap pattern (diagonal red/white    // stripes) to create a brush with. hbmBrush = CreateCompatibleBitmap(hdcScr, 8, 8); hdcBrush = CreateCompatibleDC(hdcScr); SelectObject(hdcBrush, hbmBrush); for (x=0; x<8; x++) { for (y=0; y<8; y++) { if (((x+y)/4) & 1) SetPixel(hdcBrush, x,y, RGB(255,255,255)); // White else SetPixel(hdcBrush, x,y, RGB(255,0,0));     // Red }   }    DeleteDC(hdcBrush); // Clean up

// Create a brush from our pattern bitmap. lb.lbHatch = (DWORD)hbmBrush; lprbi->hbr = CreateBrushIndirect(&lb); DeleteObject(hbmBrush); // Clean up

// Store the thickness of the edges of the rubber band rectangle. lprbi->iThickness = iThickness;

// Create bitmaps to store the original contents of the screen // that will be covered by the edges of the rubber band rectangle. lprbi->hbmLeft  = CreateCompatibleBitmap(hdcScr, iThickness, GetSystemMetrics(SM_CYVIRTUALSCREEN)); lprbi->hbmRight = CreateCompatibleBitmap(hdcScr, iThickness, GetSystemMetrics(SM_CYVIRTUALSCREEN)); lprbi->hbmTop   = CreateCompatibleBitmap(hdcScr, GetSystemMetrics(SM_CXVIRTUALSCREEN), iThickness); lprbi->hbmBottom = CreateCompatibleBitmap(hdcScr, GetSystemMetrics(SM_CXVIRTUALSCREEN), iThickness);

// Create an HDC to use to capture and display our edge bitmaps. lprbi->hdcMem   = CreateCompatibleDC(hdcScr);

return lprbi; }

/**** RBDrawBorder ********************************************\ \****************************************************************/ BOOL RBDrawBorder(HDC hdcScr, LPRUBBERBANDINFO lprbi, LPRECT lprc) {   HBRUSH hbrOld; POINT pt; int iCounter;
 * Draws the border (edges) of the current selection region     *
 * as indicated by the lprc parameter with the attributes       *
 * specified in the RUBBERBANDINFO.                             *
 * specified in the RUBBERBANDINFO.                             *

// Make sure we were not passed NULLs. if (!lprbi || !lprc || !hdcScr) return FALSE;

// This is used to offset the brush origin to   // make a quick-and-dirty animated selection border. iCounter = (GetTickCount >> 5) & 7; hbrOld = (HBRUSH)SelectObject(hdcScr, lprbi->hbr);

// Capture the part of the screen that our edges will overwrite. SelectObject(lprbi->hdcMem, lprbi->hbmLeft); BitBlt(lprbi->hdcMem, 0,0, lprbi->iThickness, lprc->bottom-lprc->top, hdcScr, lprc->left, lprc->top, SRCCOPY); SelectObject(lprbi->hdcMem, lprbi->hbmRight); BitBlt(lprbi->hdcMem, 0,0, lprbi->iThickness, lprc->bottom-lprc->top, hdcScr, lprc->right-lprbi->iThickness, lprc->top, SRCCOPY);

SelectObject(lprbi->hdcMem, lprbi->hbmTop); BitBlt(lprbi->hdcMem, 0,0, lprc->right-lprc->left, lprbi->iThickness, hdcScr, lprc->left, lprc->top, SRCCOPY);

SelectObject(lprbi->hdcMem, lprbi->hbmBottom); BitBlt(lprbi->hdcMem, 0,0, lprc->right-lprc->left, lprbi->iThickness, hdcScr, lprc->left, lprc->bottom-lprbi->iThickness, SRCCOPY); // Set the brush origin to get an animated edge pattern during dragging. SetBrushOrgEx(hdcScr, iCounter, iCounter, &pt);

// Draw the sides of the rectangle. PatBlt(hdcScr, lprc->left, lprc->top, lprbi->iThickness, lprc->bottom-lprc->top, PATCOPY); // Left side PatBlt(hdcScr, lprc->right-lprbi->iThickness, lprc->top, lprbi->iThickness, lprc->bottom-lprc->top, PATCOPY);  // Right side PatBlt(hdcScr, lprc->left, lprc->top, lprc->right-lprc->left, lprbi->iThickness, PATCOPY); // Top side PatBlt(hdcScr, lprc->left, lprc->bottom-lprbi->iThickness, lprc->right-lprc->left, lprbi->iThickness, PATCOPY); // Bottom side

// Restore the previous brush. SelectObject(hdcScr, hbrOld);

// Restore the previous brush origin. SetBrushOrgEx(hdcScr, pt.x, pt.y, NULL);

return TRUE; }

/**** RBRestoreBorder *****************************************\ \****************************************************************/ BOOL RBRestoreBorder(HDC hdcScr, LPRUBBERBANDINFO lprbi, LPRECT lprc) {   // Make sure we were not passed NULLs. if (!lprbi || !lprc || !hdcScr) return FALSE;
 * Restore the original screen contents that were overwritten by *
 * the edges of our selection rectangle                         *
 * the edges of our selection rectangle                         *

SelectObject(lprbi->hdcMem, lprbi->hbmLeft); BitBlt(hdcScr, lprc->left, lprc->top, lprbi->iThickness, lprc->bottom-lprc->top, lprbi->hdcMem, 0,0, SRCCOPY); SelectObject(lprbi->hdcMem, lprbi->hbmRight); BitBlt(hdcScr, lprc->right-lprbi->iThickness, lprc->top, lprbi->iThickness, lprc->bottom-lprc->top, lprbi->hdcMem, 0,0, SRCCOPY);

SelectObject(lprbi->hdcMem, lprbi->hbmTop); BitBlt(hdcScr, lprc->left, lprc->top, lprc->right-lprc->left, lprbi->iThickness, lprbi->hdcMem, 0,0, SRCCOPY);

SelectObject(lprbi->hdcMem, lprbi->hbmBottom); BitBlt(hdcScr, lprc->left, lprc->bottom-lprbi->iThickness, lprc->right-lprc->left, lprbi->iThickness, lprbi->hdcMem, 0,0, SRCCOPY);

return TRUE; }

/**** RBClose *************************************************\ \****************************************************************/ BOOL RBClose(LPRUBBERBANDINFO lprbi) {   if (!lprbi) return FALSE; // Clean up the objects that we created. DeleteDC(lprbi->hdcMem); DeleteObject(lprbi->hbmLeft); DeleteObject(lprbi->hbmRight); DeleteObject(lprbi->hbmTop); DeleteObject(lprbi->hbmBottom); DeleteObject(lprbi->hbr);
 * Clean up the objects that we created and free up the memory  *
 * that we allocated.                                           *
 * that we allocated.                                           *

GlobalFree(lprbi); return TRUE; }

/**** RBTrackMouse ********************************************\ \****************************************************************/ BOOL RBTrackMouse (HWND hwnd, POINT pt, LPRECT lprect) {   HDC   hdc; MSG  msg; LPRUBBERBANDINFO lprbi; POINT ptOrigin; RECT rcBounds;
 * Tracks the mouse movement and draws a selection rectangle    *
 * indicating the area between the initial button down point    *
 * and the current track position. This function exits and      *
 * returns the rectangle when the mouse button is released.     *
 * returns the rectangle when the mouse button is released.     *

// Make sure our parameters are valid. if (!IsWindow(hwnd) || !lprect) return FALSE;

// Get the size of the client area so we can constrain // our selection area. GetClientRect(hwnd, &rcBounds);

// Get a display context to draw in. hdc = GetDC(hwnd); if (!hdc) return FALSE;

// Initialize rubber banding for our display context. lprbi = RBInitialize(hdc, 8); if (!lprbi) { ReleaseDC(hwnd, hdc); return FALSE; }

// Capture mouse movement. SetCapture(hwnd);

// Get mouse coordinates relative to origin of window. ptOrigin.x = (short int)GetScrollPos(hwnd,SB_HORZ); ptOrigin.y = (short int)GetScrollPos(hwnd,SB_VERT);

pt.x += ptOrigin.x;   pt.y += ptOrigin.y;

// Set the origin for the displayable area. SetWindowOrgEx(hdc, ptOrigin.x, ptOrigin.y, NULL);

// Initialize clip rectangle to the point. lprect->left  = pt.x;    lprect->top    = pt.y;    lprect->right  = pt.x;    lprect->bottom = pt.y;

// Eat mouse messages until a WM_LBUTTONUP is occurs. Meanwhile // continue to draw a rubber banding rectangle and display its dimensions. while (TRUE) { WaitMessage; if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) { // Restore the previous border. RBRestoreBorder(hdc, lprbi, lprect); lprect->left  = pt.x;            lprect->top    = pt.y;            lprect->right  = (short)LOWORD(msg.lParam) + ptOrigin.x;            lprect->bottom = (short)HIWORD(msg.lParam) + ptOrigin.y;            NormalizeRect(lprect);

if (lprect->left < rcBounds.left)    lprect->left = rcBounds.left; if (lprect->top < rcBounds.top)      lprect->top = rcBounds.top; if (lprect->right > rcBounds.right)  lprect->right = rcBounds.right; if (lprect->bottom > rcBounds.bottom) lprect->bottom = rcBounds.bottom;

// Draw the current border. RBDrawBorder(hdc, lprbi, lprect); // Bail when the user releases the left mouse button. if (msg.message == WM_LBUTTONUP) { RBRestoreBorder(hdc, lprbi, lprect); break; }       }        else continue; }

ReleaseCapture; ReleaseDC(hwnd,hdc);

RBClose(lprbi);

return TRUE; }

/**** GetSelectionArea ****************************************\ \****************************************************************/ BOOL GetSelectionArea(HWND hWnd, LPRECT lprc) {   POINT pt; // Get the mouse-down point. GetCursorPos(&pt);
 * This function displays a rubber banding rectangle that       *
 * allows the user to select a rectangle in the client area     *
 * of a window. Called in response to a WM_LBUTTONDOWN.         *
 * of a window. Called in response to a WM_LBUTTONDOWN.         *

// Convert it into client coordinates. ScreenToClient(hWnd, &pt);

// Get a rectangle from the user. return RBTrackMouse(hWnd, pt, lprc);

}

Keywords: kbdswgdi2003swept kbgdi kbhowto KB314394

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.