comctl32/button: Support displaying both image and text.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45727
Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2018-09-05 22:57:39 +08:00 committed by Alexandre Julliard
parent fa4183ef84
commit 1f323f31d6

View file

@ -98,7 +98,7 @@ typedef struct _BUTTON_INFO
} u;
} BUTTON_INFO;
static UINT BUTTON_CalcLabelRect( const BUTTON_INFO *infoPtr, HDC hdc, RECT *rc );
static UINT BUTTON_CalcLayoutRects( const BUTTON_INFO *infoPtr, HDC hdc, RECT *labelRc, RECT *imageRc, RECT *textRc );
static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
@ -615,7 +615,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
/* FIXME: check other BS_* handlers */
if (btn_type == BS_GROUPBOX)
InflateRect(&rc, -7, 1); /* GB_Paint does this */
BUTTON_CalcLabelRect(infoPtr, hdc, &rc);
BUTTON_CalcLayoutRects(infoPtr, hdc, &rc, NULL, NULL);
/* Clip by client rect bounds */
if (rc.right > client.right) rc.right = client.right;
if (rc.bottom > client.bottom) rc.bottom = client.bottom;
@ -855,95 +855,257 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
return 0;
}
/* If maxWidth is zero, rectangle width is unlimited */
static RECT BUTTON_GetTextRect(const BUTTON_INFO *infoPtr, HDC hdc, const WCHAR *text, LONG maxWidth)
{
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
LONG exStyle = GetWindowLongW(infoPtr->hwnd, GWL_EXSTYLE);
UINT dtStyle = BUTTON_BStoDT(style, exStyle);
HFONT hPrevFont;
RECT rect = {0};
rect.right = maxWidth;
hPrevFont = SelectObject(hdc, infoPtr->font);
/* Calculate height without DT_VCENTER and DT_BOTTOM to get the correct height */
DrawTextW(hdc, text, -1, &rect, (dtStyle & ~(DT_VCENTER | DT_BOTTOM)) | DT_CALCRECT);
if (hPrevFont) SelectObject(hdc, hPrevFont);
return rect;
}
static BOOL show_image_only(const BUTTON_INFO *infoPtr)
{
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
return (style & (BS_ICON | BS_BITMAP)) && infoPtr->u.image;
}
static BOOL show_image_and_text(const BUTTON_INFO *infoPtr)
{
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
UINT type = get_button_type(style);
return !(style & (BS_ICON | BS_BITMAP)) && infoPtr->u.image
&& (type == BS_PUSHBUTTON || type == BS_DEFPUSHBUTTON || type == BS_USERBUTTON || type == BS_SPLITBUTTON
|| type == BS_DEFSPLITBUTTON || type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK);
}
static BOOL show_image(const BUTTON_INFO *infoPtr)
{
return show_image_only(infoPtr) || show_image_and_text(infoPtr);
}
/* Get a bounding rectangle that is large enough to contain a image and a text side by side.
* Note: (left,top) of the result rectangle may not be (0,0), offset it by yourself if needed */
static RECT BUTTON_GetBoundingLabelRect(LONG style, const RECT *textRect, const RECT *imageRect)
{
RECT labelRect;
RECT rect = *imageRect;
INT textWidth = textRect->right - textRect->left;
INT textHeight = textRect->bottom - textRect->top;
INT imageWidth = imageRect->right - imageRect->left;
INT imageHeight = imageRect->bottom - imageRect->top;
if ((style & BS_CENTER) == BS_RIGHT)
OffsetRect(&rect, textWidth, 0);
else if ((style & BS_CENTER) == BS_LEFT)
OffsetRect(&rect, -imageWidth, 0);
else if ((style & BS_VCENTER) == BS_BOTTOM)
OffsetRect(&rect, 0, textHeight);
else if ((style & BS_VCENTER) == BS_TOP)
OffsetRect(&rect, 0, -imageHeight);
else
OffsetRect(&rect, -imageWidth, 0);
UnionRect(&labelRect, textRect, &rect);
return labelRect;
}
/* Position a rectangle inside a bounding rectangle according to button alignment flags */
static void BUTTON_PositionRect(LONG style, const RECT *outerRect, RECT *innerRect, const RECT *margin)
{
INT width = innerRect->right - innerRect->left;
INT height = innerRect->bottom - innerRect->top;
if ((style & WS_EX_RIGHT) && !(style & BS_CENTER)) style |= BS_CENTER;
if (!(style & BS_CENTER))
{
/* Push button's text is centered by default, all other types have left aligned text */
if (get_button_type(style) <= BS_DEFPUSHBUTTON)
style |= BS_CENTER;
else
style |= BS_LEFT;
}
if (!(style & BS_VCENTER))
{
/* Group box's text is top aligned by default */
if (get_button_type(style) == BS_GROUPBOX)
style |= BS_TOP;
}
switch (style & BS_CENTER)
{
case BS_CENTER:
innerRect->left = outerRect->left + (outerRect->right - outerRect->left - width) / 2;
innerRect->right = innerRect->left + width;
break;
case BS_RIGHT:
innerRect->right = outerRect->right - margin->right;
innerRect->left = innerRect->right - width;
break;
case BS_LEFT:
default:
innerRect->left = outerRect->left + margin->left;
innerRect->right = innerRect->left + width;
break;
}
switch (style & BS_VCENTER)
{
case BS_TOP:
innerRect->top = outerRect->top + margin->top;
innerRect->bottom = innerRect->top + height;
break;
case BS_BOTTOM:
innerRect->bottom = outerRect->bottom - margin->bottom;
innerRect->top = innerRect->bottom - height;
break;
case BS_VCENTER:
default:
innerRect->top = outerRect->top + (outerRect->bottom - outerRect->top - height) / 2;
innerRect->bottom = innerRect->top + height;
break;
}
}
static SIZE BUTTON_GetImageSize(const BUTTON_INFO *infoPtr)
{
ICONINFO iconInfo;
BITMAP bm = {0};
SIZE size = {0};
if (infoPtr->u.image)
{
if (infoPtr->image_type == IMAGE_ICON)
{
GetIconInfo(infoPtr->u.icon, &iconInfo);
GetObjectW(iconInfo.hbmColor, sizeof(bm), &bm);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
}
else if (infoPtr->image_type == IMAGE_BITMAP)
GetObjectW(infoPtr->u.bitmap, sizeof(bm), &bm);
size.cx = bm.bmWidth;
size.cy = bm.bmHeight;
}
return size;
}
/**********************************************************************
* BUTTON_CalcLabelRect
* BUTTON_CalcLayoutRects
*
* Calculates label's rectangle depending on button style.
* Calculates the rectangles of the button label(image and text) and its parts depending on a button's style.
*
* Returns flags to be passed to DrawText.
* Calculated rectangle doesn't take into account button state
* (pushed, etc.). If there is nothing to draw (no text/image) output
* rectangle is empty, and return value is (UINT)-1.
*
* PARAMS:
* infoPtr [I] Button pointer
* hdc [I] Handle to device context to draw to
* labelRc [I/O] Input the rect the label to be positioned in, and output the label rect
* imageRc [O] Optional, output the image rect
* textRc [O] Optional, output the text rect
*/
static UINT BUTTON_CalcLabelRect(const BUTTON_INFO *infoPtr, HDC hdc, RECT *rc)
static UINT BUTTON_CalcLayoutRects(const BUTTON_INFO *infoPtr, HDC hdc, RECT *labelRc, RECT *imageRc, RECT *textRc)
{
LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
LONG ex_style = GetWindowLongW( infoPtr->hwnd, GWL_EXSTYLE );
WCHAR *text = get_button_text(infoPtr);
ICONINFO iconInfo;
BITMAP bm = { 0 };
UINT dtStyle = BUTTON_BStoDT( style, ex_style );
RECT r = *rc;
INT n;
SIZE imageSize = BUTTON_GetImageSize(infoPtr);
UINT dtStyle = BUTTON_BStoDT(style, ex_style);
RECT labelRect, imageRect, textRect;
RECT emptyMargin = {0}, oneMargin = {1, 1, 1, 1};
LONG maxTextWidth;
/* Calculate label rectangle according to label type */
/* FIXME: Doesn't support showing both image and text yet */
if (infoPtr->u.image)
if ((imageSize.cx == 0 && imageSize.cy == 0) && (text == NULL || text[0] == '\0'))
{
if (infoPtr->image_type == IMAGE_ICON)
{
GetIconInfo(infoPtr->u.icon, &iconInfo);
GetObjectW(iconInfo.hbmColor, sizeof(bm), &bm);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
}
else if (infoPtr->image_type == IMAGE_BITMAP)
{
GetObjectW(infoPtr->u.bitmap, sizeof(bm), &bm);
}
r.right = r.left + bm.bmWidth;
r.bottom = r.top + bm.bmHeight;
}
else if (text && text[0])
{
HFONT hFont, hPrevFont = 0;
if ((hFont = infoPtr->font)) hPrevFont = SelectObject(hdc, hFont);
DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
if (hPrevFont) SelectObject(hdc, hPrevFont);
}
if ((infoPtr->u.image && bm.bmWidth == 0 && bm.bmHeight == 0)
|| (text == NULL || text[0] == '\0'))
{
rc->right = r.left;
rc->bottom = r.top;
SetRectEmpty(labelRc);
SetRectEmpty(imageRc);
SetRectEmpty(textRc);
heap_free(text);
return (UINT)-1;
}
heap_free(text);
/* Position label inside bounding rectangle according to
* alignment flags. (calculated rect is always left-top aligned).
* If label is aligned to any side - shift label in opposite
* direction to leave extra space for focus rectangle.
*/
switch (dtStyle & (DT_CENTER|DT_RIGHT))
SetRect(&imageRect, 0, 0, imageSize.cx, imageSize.cy);
/* Show image only */
if (show_image_only(infoPtr))
{
case DT_LEFT: r.left++; r.right++; break;
case DT_CENTER: n = r.right - r.left;
r.left = rc->left + ((rc->right - rc->left) - n) / 2;
r.right = r.left + n; break;
case DT_RIGHT: n = r.right - r.left;
r.right = rc->right - 1;
r.left = r.right - n;
break;
BUTTON_PositionRect(style, labelRc, &imageRect,
infoPtr->imagelist.himl ? &infoPtr->imagelist.margin : &emptyMargin);
labelRect = imageRect;
SetRectEmpty(&textRect);
}
else
{
/* Get text rect */
maxTextWidth = labelRc->right - labelRc->left;
textRect = BUTTON_GetTextRect(infoPtr, hdc, text, maxTextWidth);
heap_free(text);
/* Show image and text */
if (show_image_and_text(infoPtr))
{
RECT boundingLabelRect, boundingImageRect, boundingTextRect;
/* Get label rect */
/* Get a label bounding rect to position the label in the user specified label rect because text and
* image need to align together. */
boundingLabelRect = BUTTON_GetBoundingLabelRect(style, &textRect, &imageRect);
BUTTON_PositionRect(style, labelRc, &boundingLabelRect, &emptyMargin);
labelRect = boundingLabelRect;
/* Get image rect */
/* Split the label rect to two halves as two bounding rects for image and text */
boundingImageRect = labelRect;
if ((style & BS_CENTER) == BS_RIGHT)
boundingImageRect.left = boundingImageRect.right - imageSize.cx;
else if ((style & BS_CENTER) == BS_LEFT)
boundingImageRect.right = boundingImageRect.left + imageSize.cx;
else if ((style & BS_VCENTER) == BS_BOTTOM)
boundingImageRect.top = boundingImageRect.bottom - imageSize.cy;
else if ((style & BS_VCENTER) == BS_TOP)
boundingImageRect.bottom = boundingImageRect.top + imageSize.cy;
else
boundingImageRect.right = boundingImageRect.left + imageSize.cx;
BUTTON_PositionRect(style, &boundingImageRect, &imageRect, &emptyMargin);
/* Get text rect */
SubtractRect(&boundingTextRect, &labelRect, &boundingImageRect);
BUTTON_PositionRect(style, &boundingTextRect, &textRect, &oneMargin);
}
/* Show text only */
else
{
if (get_button_type(style) != BS_GROUPBOX)
BUTTON_PositionRect(style, labelRc, &textRect, &oneMargin);
else
/* GroupBox is always top aligned */
BUTTON_PositionRect((style & ~BS_VCENTER) | BS_TOP, labelRc, &textRect, &oneMargin);
labelRect = textRect;
SetRectEmpty(&imageRect);
}
}
switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
{
case DT_TOP: r.top++; r.bottom++; break;
case DT_VCENTER: n = r.bottom - r.top;
r.top = rc->top + ((rc->bottom - rc->top) - n) / 2;
r.bottom = r.top + n; break;
case DT_BOTTOM: n = r.bottom - r.top;
r.bottom = rc->bottom - 1;
r.top = r.bottom - n;
break;
}
CopyRect(labelRc, &labelRect);
CopyRect(imageRc, &imageRect);
CopyRect(textRc, &textRect);
*rc = r;
return dtStyle;
}
@ -962,19 +1124,25 @@ static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int
return TRUE;
}
/**********************************************************************
* BUTTON_DrawLabel
*
* Common function for drawing button label.
*
* FIXME:
* 1. When BS_SINGLELINE is specified and text contains '\t', '\n' or '\r' in the middle, they are rendered as
* squares now whereas they should be ignored.
* 2. When BS_MULTILINE is specified and text contains space in the middle, the space mistakenly be rendered as newline.
*/
static void BUTTON_DrawLabel(const BUTTON_INFO *infoPtr, HDC hdc, UINT dtFlags, const RECT *rc)
static void BUTTON_DrawLabel(const BUTTON_INFO *infoPtr, HDC hdc, UINT dtFlags, const RECT *imageRect,
const RECT *textRect)
{
DRAWSTATEPROC lpOutputProc = NULL;
LPARAM lp;
WPARAM wp = 0;
HBRUSH hbr = 0;
UINT flags = IsWindowEnabled(infoPtr->hwnd) ? DSS_NORMAL : DSS_DISABLED;
UINT imageFlags;
LONG state = infoPtr->state;
LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
WCHAR *text = NULL;
@ -990,35 +1158,34 @@ static void BUTTON_DrawLabel(const BUTTON_INFO *infoPtr, HDC hdc, UINT dtFlags,
flags |= DSS_MONO;
}
/* FIXME: Support drawing label with both image and text */
if (infoPtr->u.image != 0)
switch (infoPtr->image_type)
{
switch (infoPtr->image_type)
{
case IMAGE_ICON:
flags |= DST_ICON;
lp = (LPARAM)infoPtr->u.icon;
break;
case IMAGE_BITMAP:
flags |= DST_BITMAP;
lp = (LPARAM)infoPtr->u.bitmap;
break;
default:
return;
}
}
else
{
/* DST_COMPLEX -- is 0 */
lpOutputProc = BUTTON_DrawTextCallback;
if (!(text = get_button_text(infoPtr))) return;
lp = (LPARAM)text;
wp = dtFlags;
case IMAGE_ICON:
imageFlags = flags | DST_ICON;
lp = (LPARAM)infoPtr->u.icon;
break;
case IMAGE_BITMAP:
imageFlags = flags | DST_BITMAP;
lp = (LPARAM)infoPtr->u.bitmap;
break;
default:
return;
}
DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
rc->right - rc->left, rc->bottom - rc->top, flags);
heap_free( text );
if (show_image(infoPtr))
DrawStateW(hdc, hbr, lpOutputProc, lp, wp, imageRect->left, imageRect->top,
imageRect->right - imageRect->left, imageRect->bottom - imageRect->top, imageFlags);
if (show_image_only(infoPtr)) return;
/* DST_COMPLEX -- is 0 */
lpOutputProc = BUTTON_DrawTextCallback;
if (!(text = get_button_text(infoPtr))) return;
lp = (LPARAM)text;
wp = dtFlags;
DrawStateW(hdc, hbr, lpOutputProc, lp, wp, textRect->left, textRect->top, textRect->right - textRect->left,
textRect->bottom - textRect->top, flags);
heap_free(text);
}
/**********************************************************************
@ -1026,7 +1193,7 @@ static void BUTTON_DrawLabel(const BUTTON_INFO *infoPtr, HDC hdc, UINT dtFlags,
*/
static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
{
RECT rc, r;
RECT rc, labelRect, imageRect, textRect;
UINT dtFlags, uState;
HPEN hOldPen, hpen;
HBRUSH hOldBrush;
@ -1082,18 +1249,17 @@ static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
/* draw button label */
r = rc;
dtFlags = BUTTON_CalcLabelRect(infoPtr, hDC, &r);
labelRect = rc;
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
if (dtFlags == (UINT)-1L)
goto cleanup;
if (pushedState)
OffsetRect(&r, 1, 1);
if (pushedState) OffsetRect(&labelRect, 1, 1);
oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &r);
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
SetTextColor( hDC, oldTxtColor );
@ -1119,7 +1285,7 @@ draw_focus:
static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
{
RECT rbox, rtext, client;
RECT rbox, labelRect, imageRect, textRect, client;
HBRUSH hBrush;
int delta, text_offset, checkBoxWidth, checkBoxHeight;
UINT dtFlags;
@ -1137,7 +1303,7 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
}
GetClientRect(infoPtr->hwnd, &client);
rbox = rtext = client;
rbox = labelRect = client;
checkBoxWidth = 12 * GetDpiForWindow( infoPtr->hwnd ) / 96 + 1;
checkBoxHeight = 12 * GetDpiForWindow( infoPtr->hwnd ) / 96 + 1;
@ -1155,12 +1321,12 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT)
{
rtext.right -= checkBoxWidth + text_offset;
labelRect.right -= checkBoxWidth + text_offset;
rbox.left = rbox.right - checkBoxWidth;
}
else
{
rtext.left += checkBoxWidth + text_offset;
labelRect.left += checkBoxWidth + text_offset;
rbox.right = checkBoxWidth;
}
@ -1169,14 +1335,14 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
/* Draw label */
client = rtext;
dtFlags = BUTTON_CalcLabelRect(infoPtr, hDC, &rtext);
client = labelRect;
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
/* Only adjust rbox when rtext is valid */
if (dtFlags != (UINT)-1L)
{
rbox.top = rtext.top;
rbox.bottom = rtext.bottom;
rbox.top = labelRect.top;
rbox.bottom = labelRect.bottom;
}
/* Draw the check-box bitmap */
@ -1229,16 +1395,15 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
if (dtFlags == (UINT)-1L) /* Noting to draw */
return;
if (action == ODA_DRAWENTIRE)
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &rtext);
if (action == ODA_DRAWENTIRE) BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
/* ... and focus */
if (action == ODA_FOCUS || (state & BST_FOCUS))
{
rtext.left--;
rtext.right++;
IntersectRect(&rtext, &rtext, &client);
DrawFocusRect( hDC, &rtext );
labelRect.left--;
labelRect.right++;
IntersectRect(&labelRect, &labelRect, &client);
DrawFocusRect(hDC, &labelRect);
}
SelectClipRgn( hDC, hrgn );
if (hrgn) DeleteObject( hrgn );
@ -1274,7 +1439,7 @@ static void BUTTON_CheckAutoRadioButton( HWND hwnd )
static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
{
RECT rc, rcFrame;
RECT labelRect, imageRect, textRect, rcFrame;
HBRUSH hbr;
HFONT hFont;
UINT dtFlags;
@ -1290,16 +1455,16 @@ static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
if (!hbr) /* did the app forget to call defwindowproc ? */
hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
GetClientRect( infoPtr->hwnd, &rc);
rcFrame = rc;
hrgn = set_control_clipping( hDC, &rc );
GetClientRect(infoPtr->hwnd, &labelRect);
rcFrame = labelRect;
hrgn = set_control_clipping(hDC, &labelRect);
GetTextMetricsW (hDC, &tm);
rcFrame.top += (tm.tmHeight / 2) - 1;
DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
InflateRect(&rc, -7, 1);
dtFlags = BUTTON_CalcLabelRect(infoPtr, hDC, &rc);
InflateRect(&labelRect, -7, 1);
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
if (dtFlags != (UINT)-1)
{
@ -1309,11 +1474,15 @@ static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
*/
/* There is 1-pixel margin at the left, right, and bottom */
rc.left--; rc.right++; rc.bottom++;
FillRect(hDC, &rc, hbr);
rc.left++; rc.right--; rc.bottom--;
labelRect.left--;
labelRect.right++;
labelRect.bottom++;
FillRect(hDC, &labelRect, hbr);
labelRect.left++;
labelRect.right--;
labelRect.bottom--;
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &rc);
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
}
SelectClipRgn( hDC, hrgn );
if (hrgn) DeleteObject( hrgn );