qcap: Use BasePinImpl_QueryInterface().
[wine.git] / dlls / uxtheme / draw.c
blob3dcc58205b09d0fe96857bd551769ca0a8fad0df
1 /*
2 * Win32 5.1 Theme drawing
4 * Copyright (C) 2003 Kevin Koltzau
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdlib.h>
22 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "wingdi.h"
28 #include "vfwmsgs.h"
29 #include "uxtheme.h"
30 #include "tmschema.h"
32 #include "msstyles.h"
33 #include "uxthemedll.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
39 /***********************************************************************
40 * Defines and global variables
43 extern ATOM atDialogThemeEnabled;
45 /***********************************************************************/
47 /***********************************************************************
48 * EnableThemeDialogTexture (UXTHEME.@)
50 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
52 static const WCHAR szTab[] = { 'T','a','b',0 };
53 BOOL res;
55 TRACE("(%p,0x%08x\n", hwnd, dwFlags);
56 res = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
57 UlongToHandle(dwFlags|0x80000000));
58 /* 0x80000000 serves as a "flags set" flag */
59 if (!res)
60 return HRESULT_FROM_WIN32(GetLastError());
61 if (dwFlags & ETDT_USETABTEXTURE)
62 return SetWindowTheme (hwnd, NULL, szTab);
63 else
64 return SetWindowTheme (hwnd, NULL, NULL);
67 /***********************************************************************
68 * IsThemeDialogTextureEnabled (UXTHEME.@)
70 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
72 DWORD dwDialogTextureFlags;
73 TRACE("(%p)\n", hwnd);
75 dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
76 if (dwDialogTextureFlags == 0)
77 /* Means EnableThemeDialogTexture wasn't called for this dialog */
78 return TRUE;
80 return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
83 /***********************************************************************
84 * DrawThemeParentBackground (UXTHEME.@)
86 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
88 RECT rt;
89 POINT org;
90 HWND hParent;
91 HRGN clip = NULL;
92 int hasClip = -1;
94 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
95 hParent = GetParent(hwnd);
96 if(!hParent)
97 hParent = hwnd;
98 if(prc) {
99 rt = *prc;
100 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
102 clip = CreateRectRgn(0,0,1,1);
103 hasClip = GetClipRgn(hdc, clip);
104 if(hasClip == -1)
105 TRACE("Failed to get original clipping region\n");
106 else
107 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
109 else {
110 GetClientRect(hwnd, &rt);
111 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
114 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
116 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
117 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
119 SetViewportOrgEx(hdc, org.x, org.y, NULL);
120 if(prc) {
121 if(hasClip == 0)
122 SelectClipRgn(hdc, NULL);
123 else if(hasClip == 1)
124 SelectClipRgn(hdc, clip);
125 DeleteObject(clip);
127 return S_OK;
131 /***********************************************************************
132 * DrawThemeBackground (UXTHEME.@)
134 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
135 int iStateId, const RECT *pRect,
136 const RECT *pClipRect)
138 DTBGOPTS opts;
139 opts.dwSize = sizeof(DTBGOPTS);
140 opts.dwFlags = 0;
141 if(pClipRect) {
142 opts.dwFlags |= DTBG_CLIPRECT;
143 opts.rcClip = *pClipRect;
145 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
148 /***********************************************************************
149 * UXTHEME_SelectImage
151 * Select the image to use
153 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
155 PTHEME_PROPERTY tp;
156 int imageselecttype = IST_NONE;
157 int i;
158 int image;
159 if(glyph)
160 image = TMT_GLYPHIMAGEFILE;
161 else
162 image = TMT_IMAGEFILE;
164 if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
165 return tp;
166 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
168 if(imageselecttype == IST_DPI) {
169 int reqdpi = 0;
170 int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
171 for(i=4; i>=0; i--) {
172 reqdpi = 0;
173 if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
174 if(reqdpi != 0 && screendpi >= reqdpi) {
175 TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
176 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
180 /* If an image couldn't be selected, choose the first one */
181 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
183 else if(imageselecttype == IST_SIZE) {
184 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
185 POINT reqsize;
186 for(i=4; i>=0; i--) {
187 PTHEME_PROPERTY fileProp =
188 MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
189 if (!fileProp) continue;
190 if(FAILED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
191 /* fall back to size of Nth image */
192 WCHAR szPath[MAX_PATH];
193 int imagelayout = IL_HORIZONTAL;
194 int imagecount = 1;
195 BITMAP bmp;
196 HBITMAP hBmp;
197 BOOL hasAlpha;
199 lstrcpynW(szPath, fileProp->lpValue, min(fileProp->dwValueLen+1, ARRAY_SIZE(szPath)));
200 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
201 if(!hBmp) continue;
203 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
204 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
206 GetObjectW(hBmp, sizeof(bmp), &bmp);
207 if(imagelayout == IL_VERTICAL) {
208 reqsize.x = bmp.bmWidth;
209 reqsize.y = bmp.bmHeight/imagecount;
211 else {
212 reqsize.x = bmp.bmWidth/imagecount;
213 reqsize.y = bmp.bmHeight;
216 if(reqsize.x <= size.x && reqsize.y <= size.y) {
217 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
218 return fileProp;
221 /* If an image couldn't be selected, choose the smallest one */
222 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
224 return NULL;
227 /***********************************************************************
228 * UXTHEME_LoadImage
230 * Load image for part/state
232 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
233 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
235 int imagelayout = IL_HORIZONTAL;
236 int imagecount = 1;
237 int imagenum;
238 BITMAP bmp;
239 WCHAR szPath[MAX_PATH];
240 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
241 if(!tp) {
242 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
243 return E_PROP_ID_UNSUPPORTED;
245 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, ARRAY_SIZE(szPath)));
246 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
247 if(!*hBmp) {
248 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
249 return HRESULT_FROM_WIN32(GetLastError());
252 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
253 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
255 imagenum = max (min (imagecount, iStateId), 1) - 1;
256 GetObjectW(*hBmp, sizeof(bmp), &bmp);
258 if(imagecount < 1) imagecount = 1;
260 if(imagelayout == IL_VERTICAL) {
261 int height = bmp.bmHeight/imagecount;
262 bmpRect->left = 0;
263 bmpRect->right = bmp.bmWidth;
264 bmpRect->top = imagenum * height;
265 bmpRect->bottom = bmpRect->top + height;
267 else {
268 int width = bmp.bmWidth/imagecount;
269 bmpRect->left = imagenum * width;
270 bmpRect->right = bmpRect->left + width;
271 bmpRect->top = 0;
272 bmpRect->bottom = bmp.bmHeight;
274 return S_OK;
277 /***********************************************************************
278 * UXTHEME_StretchBlt
280 * Pseudo TransparentBlt/StretchBlt
282 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
283 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
284 INT transparent, COLORREF transcolor)
286 static const BLENDFUNCTION blendFunc =
288 AC_SRC_OVER, /* BlendOp */
289 0, /* BlendFlag */
290 255, /* SourceConstantAlpha */
291 AC_SRC_ALPHA /* AlphaFormat */
294 BOOL ret = TRUE;
295 int old_stretch_mode;
296 POINT old_brush_org;
298 old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
299 SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
301 if (transparent == ALPHABLEND_BINARY) {
302 /* Ensure we don't pass any negative values to TransparentBlt */
303 ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
304 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
305 transcolor);
306 } else if ((transparent == ALPHABLEND_NONE) ||
307 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
308 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
309 blendFunc))
311 ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
312 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
313 SRCCOPY);
316 SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
317 SetStretchBltMode(hdcDst, old_stretch_mode);
319 return ret;
322 /***********************************************************************
323 * UXTHEME_Blt
325 * Simplify sending same width/height for both source and dest
327 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
328 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
329 INT transparent, COLORREF transcolor)
331 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
332 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
333 transparent, transcolor);
336 /***********************************************************************
337 * UXTHEME_SizedBlt
339 * Stretches or tiles, depending on sizingtype.
341 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
342 int nWidthDst, int nHeightDst,
343 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
344 int nWidthSrc, int nHeightSrc,
345 int sizingtype,
346 INT transparent, COLORREF transcolor)
348 if (sizingtype == ST_TILE)
350 HDC hdcTemp;
351 BOOL result = FALSE;
353 if (!nWidthSrc || !nHeightSrc) return TRUE;
355 /* For destination width/height less than or equal to source
356 width/height, do not bother with memory bitmap optimization */
357 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
359 int bltWidth = min (nWidthDst, nWidthSrc);
360 int bltHeight = min (nHeightDst, nHeightSrc);
362 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
363 hdcSrc, nXOriginSrc, nYOriginSrc,
364 transparent, transcolor);
367 /* Create a DC with a bitmap consisting of a tiling of the source
368 bitmap, with standard GDI functions. This is faster than an
369 iteration with UXTHEME_Blt(). */
370 hdcTemp = CreateCompatibleDC(hdcSrc);
371 if (hdcTemp != 0)
373 HBITMAP bitmapTemp;
374 HBITMAP bitmapOrig;
375 int nWidthTemp, nHeightTemp;
376 int xOfs, xRemaining;
377 int yOfs, yRemaining;
378 int growSize;
380 /* Calculate temp dimensions of integer multiples of source dimensions */
381 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
382 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
383 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
384 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
386 /* Initial copy of bitmap */
387 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
389 /* Extend bitmap in the X direction. Growth of width is exponential */
390 xOfs = nWidthSrc;
391 xRemaining = nWidthTemp - nWidthSrc;
392 growSize = nWidthSrc;
393 while (xRemaining > 0)
395 growSize = min(growSize, xRemaining);
396 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
397 xOfs += growSize;
398 xRemaining -= growSize;
399 growSize *= 2;
402 /* Extend bitmap in the Y direction. Growth of height is exponential */
403 yOfs = nHeightSrc;
404 yRemaining = nHeightTemp - nHeightSrc;
405 growSize = nHeightSrc;
406 while (yRemaining > 0)
408 growSize = min(growSize, yRemaining);
409 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
410 yOfs += growSize;
411 yRemaining -= growSize;
412 growSize *= 2;
415 /* Use temporary hdc for source */
416 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
417 hdcTemp, 0, 0,
418 transparent, transcolor);
420 SelectObject(hdcTemp, bitmapOrig);
421 DeleteObject(bitmapTemp);
423 DeleteDC(hdcTemp);
424 return result;
426 else
428 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
429 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
430 transparent, transcolor);
434 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
435 * depend on whether the image has full alpha or whether it is
436 * color-transparent or just opaque. */
437 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
438 BOOL hasImageAlpha, INT* transparent,
439 COLORREF* transparentcolor, BOOL glyph)
441 if (hasImageAlpha)
443 *transparent = ALPHABLEND_FULL;
444 *transparentcolor = RGB (255, 0, 255);
446 else
448 BOOL trans = FALSE;
449 GetThemeBool(hTheme, iPartId, iStateId,
450 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
451 if(trans) {
452 *transparent = ALPHABLEND_BINARY;
453 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
454 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
455 transparentcolor))) {
456 /* If image is transparent, but no color was specified, use magenta */
457 *transparentcolor = RGB(255, 0, 255);
460 else
461 *transparent = ALPHABLEND_NONE;
465 /***********************************************************************
466 * UXTHEME_DrawImageGlyph
468 * Draw an imagefile glyph
470 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
471 int iStateId, RECT *pRect,
472 const DTBGOPTS *pOptions)
474 HRESULT hr;
475 HBITMAP bmpSrc = NULL;
476 HDC hdcSrc = NULL;
477 HGDIOBJ oldSrc = NULL;
478 RECT rcSrc;
479 INT transparent = 0;
480 COLORREF transparentcolor;
481 int valign = VA_CENTER;
482 int halign = HA_CENTER;
483 POINT dstSize;
484 POINT srcSize;
485 POINT topleft;
486 BOOL hasAlpha;
488 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
489 &bmpSrc, &rcSrc, &hasAlpha);
490 if(FAILED(hr)) return hr;
491 hdcSrc = CreateCompatibleDC(hdc);
492 if(!hdcSrc) {
493 hr = HRESULT_FROM_WIN32(GetLastError());
494 return hr;
496 oldSrc = SelectObject(hdcSrc, bmpSrc);
498 dstSize.x = pRect->right-pRect->left;
499 dstSize.y = pRect->bottom-pRect->top;
500 srcSize.x = rcSrc.right-rcSrc.left;
501 srcSize.y = rcSrc.bottom-rcSrc.top;
503 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
504 &transparentcolor, TRUE);
505 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
506 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
508 topleft.x = pRect->left;
509 topleft.y = pRect->top;
510 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
511 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
512 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
513 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
515 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
516 hdcSrc, rcSrc.left, rcSrc.top,
517 transparent, transparentcolor)) {
518 hr = HRESULT_FROM_WIN32(GetLastError());
521 SelectObject(hdcSrc, oldSrc);
522 DeleteDC(hdcSrc);
523 return hr;
526 /***********************************************************************
527 * UXTHEME_DrawImageGlyph
529 * Draw glyph on top of background, if appropriate
531 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
532 int iStateId, RECT *pRect,
533 const DTBGOPTS *pOptions)
535 int glyphtype = GT_NONE;
537 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
539 if(glyphtype == GT_IMAGEGLYPH) {
540 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
542 else if(glyphtype == GT_FONTGLYPH) {
543 /* I don't know what a font glyph is, I've never seen it used in any themes */
544 FIXME("Font glyph\n");
546 return S_OK;
549 /***********************************************************************
550 * get_image_part_size
552 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
554 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
555 int iStateId, RECT *prc, THEMESIZE eSize,
556 POINT *psz)
558 HRESULT hr = S_OK;
559 HBITMAP bmpSrc;
560 RECT rcSrc;
561 BOOL hasAlpha;
563 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
564 &bmpSrc, &rcSrc, &hasAlpha);
565 if (FAILED(hr)) return hr;
567 switch (eSize)
569 case TS_DRAW:
570 if (prc != NULL)
572 RECT rcDst;
573 POINT dstSize;
574 POINT srcSize;
575 int sizingtype = ST_STRETCH;
576 BOOL uniformsizing = FALSE;
578 rcDst = *prc;
580 dstSize.x = rcDst.right-rcDst.left;
581 dstSize.y = rcDst.bottom-rcDst.top;
582 srcSize.x = rcSrc.right-rcSrc.left;
583 srcSize.y = rcSrc.bottom-rcSrc.top;
585 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
586 if(uniformsizing) {
587 /* Scale height and width equally */
588 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
590 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
591 rcDst.bottom = rcDst.top + dstSize.y;
593 else
595 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
596 rcDst.right = rcDst.left + dstSize.x;
600 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
601 if(sizingtype == ST_TRUESIZE) {
602 int truesizestretchmark = 100;
604 if(dstSize.x < 0 || dstSize.y < 0) {
605 BOOL mirrorimage = TRUE;
606 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
607 if(mirrorimage) {
608 if(dstSize.x < 0) {
609 rcDst.left += dstSize.x;
610 rcDst.right += dstSize.x;
612 if(dstSize.y < 0) {
613 rcDst.top += dstSize.y;
614 rcDst.bottom += dstSize.y;
618 /* Whatever TrueSizeStretchMark does - it does not seem to
619 * be what's outlined below. It appears as if native
620 * uxtheme always stretches if dest is smaller than source
621 * (ie as if TrueSizeStretchMark==100 with the code below) */
622 #if 0
623 /* Only stretch when target exceeds source by truesizestretchmark percent */
624 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
625 #endif
626 if(dstSize.x < 0 || dstSize.y < 0 ||
627 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
628 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
629 memcpy (psz, &dstSize, sizeof (SIZE));
631 else {
632 memcpy (psz, &srcSize, sizeof (SIZE));
635 else
637 psz->x = abs(dstSize.x);
638 psz->y = abs(dstSize.y);
640 break;
642 /* else fall through */
643 case TS_MIN:
644 /* FIXME: couldn't figure how native uxtheme computes min size */
645 case TS_TRUE:
646 psz->x = rcSrc.right - rcSrc.left;
647 psz->y = rcSrc.bottom - rcSrc.top;
648 break;
650 return hr;
653 /***********************************************************************
654 * UXTHEME_DrawImageBackground
656 * Draw an imagefile background
658 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
659 int iStateId, RECT *pRect,
660 const DTBGOPTS *pOptions)
662 HRESULT hr = S_OK;
663 HBITMAP bmpSrc, bmpSrcResized = NULL;
664 HGDIOBJ oldSrc;
665 HDC hdcSrc, hdcOrigSrc = NULL;
666 RECT rcSrc;
667 RECT rcDst;
668 POINT dstSize;
669 POINT srcSize;
670 POINT drawSize;
671 int sizingtype = ST_STRETCH;
672 INT transparent;
673 COLORREF transparentcolor = 0;
674 BOOL hasAlpha;
676 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
677 &bmpSrc, &rcSrc, &hasAlpha);
678 if(FAILED(hr)) return hr;
679 hdcSrc = CreateCompatibleDC(hdc);
680 if(!hdcSrc) {
681 hr = HRESULT_FROM_WIN32(GetLastError());
682 return hr;
684 oldSrc = SelectObject(hdcSrc, bmpSrc);
686 rcDst = *pRect;
688 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
689 &transparentcolor, FALSE);
691 dstSize.x = rcDst.right-rcDst.left;
692 dstSize.y = rcDst.bottom-rcDst.top;
693 srcSize.x = rcSrc.right-rcSrc.left;
694 srcSize.y = rcSrc.bottom-rcSrc.top;
696 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
697 if(sizingtype == ST_TRUESIZE) {
698 int valign = VA_CENTER, halign = HA_CENTER;
700 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
701 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
702 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
704 if (halign == HA_CENTER)
705 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
706 else if (halign == HA_RIGHT)
707 rcDst.left = rcDst.right - drawSize.x;
708 if (valign == VA_CENTER)
709 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
710 else if (valign == VA_BOTTOM)
711 rcDst.top = rcDst.bottom - drawSize.y;
712 rcDst.right = rcDst.left + drawSize.x;
713 rcDst.bottom = rcDst.top + drawSize.y;
714 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
715 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
716 transparent, transparentcolor))
717 hr = HRESULT_FROM_WIN32(GetLastError());
719 else {
720 HDC hdcDst = NULL;
721 MARGINS sm;
722 POINT org;
724 dstSize.x = abs(dstSize.x);
725 dstSize.y = abs(dstSize.y);
727 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
729 /* Resize source image if destination smaller than margins */
730 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
731 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
732 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
733 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
734 srcSize.y = dstSize.y;
737 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
738 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
739 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
740 srcSize.x = dstSize.x;
743 hdcOrigSrc = hdcSrc;
744 hdcSrc = CreateCompatibleDC(NULL);
745 bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
746 SelectObject(hdcSrc, bmpSrcResized);
748 UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
749 rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
751 rcSrc.left = 0;
752 rcSrc.top = 0;
753 rcSrc.right = srcSize.x;
754 rcSrc.bottom = srcSize.y;
757 hdcDst = hdc;
758 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
760 /* Upper left corner */
761 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
762 hdcSrc, rcSrc.left, rcSrc.top,
763 transparent, transparentcolor)) {
764 hr = HRESULT_FROM_WIN32(GetLastError());
765 goto draw_error;
767 /* Upper right corner */
768 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
769 sm.cxRightWidth, sm.cyTopHeight,
770 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
771 transparent, transparentcolor)) {
772 hr = HRESULT_FROM_WIN32(GetLastError());
773 goto draw_error;
775 /* Lower left corner */
776 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
777 sm.cxLeftWidth, sm.cyBottomHeight,
778 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
779 transparent, transparentcolor)) {
780 hr = HRESULT_FROM_WIN32(GetLastError());
781 goto draw_error;
783 /* Lower right corner */
784 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
785 sm.cxRightWidth, sm.cyBottomHeight,
786 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
787 transparent, transparentcolor)) {
788 hr = HRESULT_FROM_WIN32(GetLastError());
789 goto draw_error;
792 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
793 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
794 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
795 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
796 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
798 if(destCenterWidth > 0) {
799 /* Center top */
800 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
801 destCenterWidth, sm.cyTopHeight,
802 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
803 srcCenterWidth, sm.cyTopHeight,
804 sizingtype, transparent, transparentcolor)) {
805 hr = HRESULT_FROM_WIN32(GetLastError());
806 goto draw_error;
808 /* Center bottom */
809 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
810 destCenterWidth, sm.cyBottomHeight,
811 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
812 srcCenterWidth, sm.cyBottomHeight,
813 sizingtype, transparent, transparentcolor)) {
814 hr = HRESULT_FROM_WIN32(GetLastError());
815 goto draw_error;
818 if(destCenterHeight > 0) {
819 /* Left center */
820 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
821 sm.cxLeftWidth, destCenterHeight,
822 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
823 sm.cxLeftWidth, srcCenterHeight,
824 sizingtype,
825 transparent, transparentcolor)) {
826 hr = HRESULT_FROM_WIN32(GetLastError());
827 goto draw_error;
829 /* Right center */
830 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
831 sm.cxRightWidth, destCenterHeight,
832 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
833 sm.cxRightWidth, srcCenterHeight,
834 sizingtype, transparent, transparentcolor)) {
835 hr = HRESULT_FROM_WIN32(GetLastError());
836 goto draw_error;
839 if(destCenterHeight > 0 && destCenterWidth > 0) {
840 BOOL borderonly = FALSE;
841 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
842 if(!borderonly) {
843 /* Center */
844 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
845 destCenterWidth, destCenterHeight,
846 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
847 srcCenterWidth, srcCenterHeight,
848 sizingtype, transparent, transparentcolor)) {
849 hr = HRESULT_FROM_WIN32(GetLastError());
850 goto draw_error;
856 draw_error:
857 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
859 SelectObject(hdcSrc, oldSrc);
860 DeleteDC(hdcSrc);
861 if (bmpSrcResized) DeleteObject(bmpSrcResized);
862 if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
863 *pRect = rcDst;
864 return hr;
867 /***********************************************************************
868 * UXTHEME_DrawBorderRectangle
870 * Draw the bounding rectangle for a borderfill background
872 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
873 int iStateId, RECT *pRect,
874 const DTBGOPTS *pOptions)
876 HRESULT hr = S_OK;
877 HPEN hPen;
878 HGDIOBJ oldPen;
879 COLORREF bordercolor = RGB(0,0,0);
880 int bordersize = 1;
882 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
883 if(bordersize > 0) {
884 POINT ptCorners[5];
885 ptCorners[0].x = pRect->left;
886 ptCorners[0].y = pRect->top;
887 ptCorners[1].x = pRect->right-1;
888 ptCorners[1].y = pRect->top;
889 ptCorners[2].x = pRect->right-1;
890 ptCorners[2].y = pRect->bottom-1;
891 ptCorners[3].x = pRect->left;
892 ptCorners[3].y = pRect->bottom-1;
893 ptCorners[4].x = pRect->left;
894 ptCorners[4].y = pRect->top;
896 InflateRect(pRect, -bordersize, -bordersize);
897 if(pOptions->dwFlags & DTBG_OMITBORDER)
898 return S_OK;
899 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
900 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
901 if(!hPen)
902 return HRESULT_FROM_WIN32(GetLastError());
903 oldPen = SelectObject(hdc, hPen);
905 if(!Polyline(hdc, ptCorners, 5))
906 hr = HRESULT_FROM_WIN32(GetLastError());
908 SelectObject(hdc, oldPen);
909 DeleteObject(hPen);
911 return hr;
914 /***********************************************************************
915 * UXTHEME_DrawBackgroundFill
917 * Fill a borderfill background rectangle
919 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
920 int iStateId, RECT *pRect,
921 const DTBGOPTS *pOptions)
923 HRESULT hr = S_OK;
924 int filltype = FT_SOLID;
926 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
928 if(pOptions->dwFlags & DTBG_OMITCONTENT)
929 return S_OK;
931 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
933 if(filltype == FT_SOLID) {
934 HBRUSH hBrush;
935 COLORREF fillcolor = RGB(255,255,255);
937 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
938 hBrush = CreateSolidBrush(fillcolor);
939 if(!FillRect(hdc, pRect, hBrush))
940 hr = HRESULT_FROM_WIN32(GetLastError());
941 DeleteObject(hBrush);
943 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
944 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
945 the gradient ratios (no idea how those work)
946 Few themes use this, and the ones I've seen only use 2 colors with
947 a gradient ratio of 0 and 255 respectively
950 COLORREF gradient1 = RGB(0,0,0);
951 COLORREF gradient2 = RGB(255,255,255);
952 TRIVERTEX vert[2];
953 GRADIENT_RECT gRect;
955 FIXME("Gradient implementation not complete\n");
957 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
958 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
960 vert[0].x = pRect->left;
961 vert[0].y = pRect->top;
962 vert[0].Red = GetRValue(gradient1) << 8;
963 vert[0].Green = GetGValue(gradient1) << 8;
964 vert[0].Blue = GetBValue(gradient1) << 8;
965 vert[0].Alpha = 0xff00;
967 vert[1].x = pRect->right;
968 vert[1].y = pRect->bottom;
969 vert[1].Red = GetRValue(gradient2) << 8;
970 vert[1].Green = GetGValue(gradient2) << 8;
971 vert[1].Blue = GetBValue(gradient2) << 8;
972 vert[1].Alpha = 0xff00;
974 gRect.UpperLeft = 0;
975 gRect.LowerRight = 1;
976 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
978 else if(filltype == FT_RADIALGRADIENT) {
979 /* I've never seen this used in a theme */
980 FIXME("Radial gradient\n");
982 else if(filltype == FT_TILEIMAGE) {
983 /* I've never seen this used in a theme */
984 FIXME("Tile image\n");
986 return hr;
989 /***********************************************************************
990 * UXTHEME_DrawBorderBackground
992 * Draw an imagefile background
994 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
995 int iStateId, const RECT *pRect,
996 const DTBGOPTS *pOptions)
998 HRESULT hr;
999 RECT rt;
1001 rt = *pRect;
1003 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1004 if(FAILED(hr))
1005 return hr;
1006 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1009 /***********************************************************************
1010 * DrawThemeBackgroundEx (UXTHEME.@)
1012 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1013 int iStateId, const RECT *pRect,
1014 const DTBGOPTS *pOptions)
1016 HRESULT hr;
1017 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1018 const DTBGOPTS *opts;
1019 HRGN clip = NULL;
1020 int hasClip = -1;
1021 int bgtype = BT_BORDERFILL;
1022 RECT rt;
1024 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1025 if(!hTheme)
1026 return E_HANDLE;
1028 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1029 if (bgtype == BT_NONE) return S_OK;
1031 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1032 opts = pOptions;
1033 if(!opts) opts = &defaultOpts;
1035 if(opts->dwFlags & DTBG_CLIPRECT) {
1036 clip = CreateRectRgn(0,0,1,1);
1037 hasClip = GetClipRgn(hdc, clip);
1038 if(hasClip == -1)
1039 TRACE("Failed to get original clipping region\n");
1040 else
1041 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1043 rt = *pRect;
1045 if(bgtype == BT_IMAGEFILE)
1046 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1047 else if(bgtype == BT_BORDERFILL)
1048 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1049 else {
1050 FIXME("Unknown background type\n");
1051 /* This should never happen, and hence I don't know what to return */
1052 hr = E_FAIL;
1054 if(SUCCEEDED(hr))
1055 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1056 if(opts->dwFlags & DTBG_CLIPRECT) {
1057 if(hasClip == 0)
1058 SelectClipRgn(hdc, NULL);
1059 else if(hasClip == 1)
1060 SelectClipRgn(hdc, clip);
1061 DeleteObject(clip);
1063 return hr;
1067 * DrawThemeEdge() implementation
1069 * Since it basically is DrawEdge() with different colors, I copied its code
1070 * from user32's uitools.c.
1073 enum
1075 EDGE_LIGHT,
1076 EDGE_HIGHLIGHT,
1077 EDGE_SHADOW,
1078 EDGE_DARKSHADOW,
1079 EDGE_FILL,
1081 EDGE_WINDOW,
1082 EDGE_WINDOWFRAME,
1084 EDGE_NUMCOLORS
1087 static const struct
1089 int themeProp;
1090 int sysColor;
1091 } EdgeColorMap[EDGE_NUMCOLORS] = {
1092 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1093 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1094 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1095 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1096 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1097 {-1, COLOR_WINDOW},
1098 {-1, COLOR_WINDOWFRAME}
1101 static const signed char LTInnerNormal[] = {
1102 -1, -1, -1, -1,
1103 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1104 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1105 -1, -1, -1, -1
1108 static const signed char LTOuterNormal[] = {
1109 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1110 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1111 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1112 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1115 static const signed char RBInnerNormal[] = {
1116 -1, -1, -1, -1,
1117 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1118 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1119 -1, -1, -1, -1
1122 static const signed char RBOuterNormal[] = {
1123 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1124 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1125 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1126 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1129 static const signed char LTInnerSoft[] = {
1130 -1, -1, -1, -1,
1131 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1132 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1133 -1, -1, -1, -1
1136 static const signed char LTOuterSoft[] = {
1137 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1138 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1139 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1140 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1143 #define RBInnerSoft RBInnerNormal /* These are the same */
1144 #define RBOuterSoft RBOuterNormal
1146 static const signed char LTRBOuterMono[] = {
1147 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1148 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1149 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1150 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1153 static const signed char LTRBInnerMono[] = {
1154 -1, -1, -1, -1,
1155 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1156 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1157 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1160 static const signed char LTRBOuterFlat[] = {
1161 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1162 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1163 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1164 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1167 static const signed char LTRBInnerFlat[] = {
1168 -1, -1, -1, -1,
1169 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1170 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1171 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1174 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1176 COLORREF col;
1177 if ((EdgeColorMap[edgeType].themeProp == -1)
1178 || FAILED (GetThemeColor (theme, part, state,
1179 EdgeColorMap[edgeType].themeProp, &col)))
1180 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1181 return col;
1184 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1186 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1189 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1191 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1194 /***********************************************************************
1195 * draw_diag_edge
1197 * Same as DrawEdge invoked with BF_DIAGONAL
1199 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1200 const RECT* rc, UINT uType,
1201 UINT uFlags, LPRECT contentsRect)
1203 POINT Points[4];
1204 signed char InnerI, OuterI;
1205 HPEN InnerPen, OuterPen;
1206 POINT SavePoint;
1207 HPEN SavePen;
1208 int spx, spy;
1209 int epx, epy;
1210 int Width = rc->right - rc->left;
1211 int Height= rc->bottom - rc->top;
1212 int SmallDiam = Width > Height ? Height : Width;
1213 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1214 || (uType & BDR_OUTER) == BDR_OUTER)
1215 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1216 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1217 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1219 /* Init some vars */
1220 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1221 SavePen = SelectObject(hdc, InnerPen);
1222 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1224 /* Determine the colors of the edges */
1225 if(uFlags & BF_MONO)
1227 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1228 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1230 else if(uFlags & BF_FLAT)
1232 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1233 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1235 else if(uFlags & BF_SOFT)
1237 if(uFlags & BF_BOTTOM)
1239 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1240 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1242 else
1244 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1245 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1248 else
1250 if(uFlags & BF_BOTTOM)
1252 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1253 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1255 else
1257 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1258 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1262 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1263 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1265 MoveToEx(hdc, 0, 0, &SavePoint);
1267 /* Don't ask me why, but this is what is visible... */
1268 /* This must be possible to do much simpler, but I fail to */
1269 /* see the logic in the MS implementation (sigh...). */
1270 /* So, this might look a bit brute force here (and it is), but */
1271 /* it gets the job done;) */
1273 switch(uFlags & BF_RECT)
1275 case 0:
1276 case BF_LEFT:
1277 case BF_BOTTOM:
1278 case BF_BOTTOMLEFT:
1279 /* Left bottom endpoint */
1280 epx = rc->left-1;
1281 spx = epx + SmallDiam;
1282 epy = rc->bottom;
1283 spy = epy - SmallDiam;
1284 break;
1286 case BF_TOPLEFT:
1287 case BF_BOTTOMRIGHT:
1288 /* Left top endpoint */
1289 epx = rc->left-1;
1290 spx = epx + SmallDiam;
1291 epy = rc->top-1;
1292 spy = epy + SmallDiam;
1293 break;
1295 case BF_TOP:
1296 case BF_RIGHT:
1297 case BF_TOPRIGHT:
1298 case BF_RIGHT|BF_LEFT:
1299 case BF_RIGHT|BF_LEFT|BF_TOP:
1300 case BF_BOTTOM|BF_TOP:
1301 case BF_BOTTOM|BF_TOP|BF_LEFT:
1302 case BF_BOTTOMRIGHT|BF_LEFT:
1303 case BF_BOTTOMRIGHT|BF_TOP:
1304 case BF_RECT:
1305 /* Right top endpoint */
1306 spx = rc->left;
1307 epx = spx + SmallDiam;
1308 spy = rc->bottom-1;
1309 epy = spy - SmallDiam;
1310 break;
1313 MoveToEx(hdc, spx, spy, NULL);
1314 SelectObject(hdc, OuterPen);
1315 LineTo(hdc, epx, epy);
1317 SelectObject(hdc, InnerPen);
1319 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1321 case BF_DIAGONAL_ENDBOTTOMLEFT:
1322 case (BF_DIAGONAL|BF_BOTTOM):
1323 case BF_DIAGONAL:
1324 case (BF_DIAGONAL|BF_LEFT):
1325 MoveToEx(hdc, spx-1, spy, NULL);
1326 LineTo(hdc, epx, epy-1);
1327 Points[0].x = spx-add;
1328 Points[0].y = spy;
1329 Points[1].x = rc->left;
1330 Points[1].y = rc->top;
1331 Points[2].x = epx+1;
1332 Points[2].y = epy-1-add;
1333 Points[3] = Points[2];
1334 break;
1336 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1337 MoveToEx(hdc, spx-1, spy, NULL);
1338 LineTo(hdc, epx, epy+1);
1339 Points[0].x = spx-add;
1340 Points[0].y = spy;
1341 Points[1].x = rc->left;
1342 Points[1].y = rc->bottom-1;
1343 Points[2].x = epx+1;
1344 Points[2].y = epy+1+add;
1345 Points[3] = Points[2];
1346 break;
1348 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1349 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1350 case BF_DIAGONAL_ENDTOPRIGHT:
1351 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1352 MoveToEx(hdc, spx+1, spy, NULL);
1353 LineTo(hdc, epx, epy+1);
1354 Points[0].x = epx-1;
1355 Points[0].y = epy+1+add;
1356 Points[1].x = rc->right-1;
1357 Points[1].y = rc->top+add;
1358 Points[2].x = rc->right-1;
1359 Points[2].y = rc->bottom-1;
1360 Points[3].x = spx+add;
1361 Points[3].y = spy;
1362 break;
1364 case BF_DIAGONAL_ENDTOPLEFT:
1365 MoveToEx(hdc, spx, spy-1, NULL);
1366 LineTo(hdc, epx+1, epy);
1367 Points[0].x = epx+1+add;
1368 Points[0].y = epy+1;
1369 Points[1].x = rc->right-1;
1370 Points[1].y = rc->top;
1371 Points[2].x = rc->right-1;
1372 Points[2].y = rc->bottom-1-add;
1373 Points[3].x = spx;
1374 Points[3].y = spy-add;
1375 break;
1377 case (BF_DIAGONAL|BF_TOP):
1378 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1379 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1380 MoveToEx(hdc, spx+1, spy-1, NULL);
1381 LineTo(hdc, epx, epy);
1382 Points[0].x = epx-1;
1383 Points[0].y = epy+1;
1384 Points[1].x = rc->right-1;
1385 Points[1].y = rc->top;
1386 Points[2].x = rc->right-1;
1387 Points[2].y = rc->bottom-1-add;
1388 Points[3].x = spx+add;
1389 Points[3].y = spy-add;
1390 break;
1392 case (BF_DIAGONAL|BF_RIGHT):
1393 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1394 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1395 MoveToEx(hdc, spx, spy, NULL);
1396 LineTo(hdc, epx-1, epy+1);
1397 Points[0].x = spx;
1398 Points[0].y = spy;
1399 Points[1].x = rc->left;
1400 Points[1].y = rc->top+add;
1401 Points[2].x = epx-1-add;
1402 Points[2].y = epy+1+add;
1403 Points[3] = Points[2];
1404 break;
1407 /* Fill the interior if asked */
1408 if((uFlags & BF_MIDDLE) && retval)
1410 HBRUSH hbsave;
1411 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1412 theme, part, state);
1413 HPEN hpsave;
1414 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1415 theme, part, state);
1416 hbsave = SelectObject(hdc, hb);
1417 hpsave = SelectObject(hdc, hp);
1418 Polygon(hdc, Points, 4);
1419 SelectObject(hdc, hbsave);
1420 SelectObject(hdc, hpsave);
1421 DeleteObject (hp);
1422 DeleteObject (hb);
1425 /* Adjust rectangle if asked */
1426 if(uFlags & BF_ADJUST)
1428 *contentsRect = *rc;
1429 if(uFlags & BF_LEFT) contentsRect->left += add;
1430 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1431 if(uFlags & BF_TOP) contentsRect->top += add;
1432 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1435 /* Cleanup */
1436 SelectObject(hdc, SavePen);
1437 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1438 if(InnerI != -1) DeleteObject (InnerPen);
1439 if(OuterI != -1) DeleteObject (OuterPen);
1441 return retval;
1444 /***********************************************************************
1445 * draw_rect_edge
1447 * Same as DrawEdge invoked without BF_DIAGONAL
1449 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1450 const RECT* rc, UINT uType,
1451 UINT uFlags, LPRECT contentsRect)
1453 signed char LTInnerI, LTOuterI;
1454 signed char RBInnerI, RBOuterI;
1455 HPEN LTInnerPen, LTOuterPen;
1456 HPEN RBInnerPen, RBOuterPen;
1457 RECT InnerRect = *rc;
1458 POINT SavePoint;
1459 HPEN SavePen;
1460 int LBpenplus = 0;
1461 int LTpenplus = 0;
1462 int RTpenplus = 0;
1463 int RBpenplus = 0;
1464 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1465 || (uType & BDR_OUTER) == BDR_OUTER)
1466 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1468 /* Init some vars */
1469 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1470 SavePen = SelectObject(hdc, LTInnerPen);
1472 /* Determine the colors of the edges */
1473 if(uFlags & BF_MONO)
1475 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1476 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1478 else if(uFlags & BF_FLAT)
1480 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1481 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1483 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1485 else if(uFlags & BF_SOFT)
1487 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1488 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1489 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1490 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1492 else
1494 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1495 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1496 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1497 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1500 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1501 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1502 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1503 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1505 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1506 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1507 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1508 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1510 MoveToEx(hdc, 0, 0, &SavePoint);
1512 /* Draw the outer edge */
1513 SelectObject(hdc, LTOuterPen);
1514 if(uFlags & BF_TOP)
1516 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1517 LineTo(hdc, InnerRect.right, InnerRect.top);
1519 if(uFlags & BF_LEFT)
1521 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1522 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1524 SelectObject(hdc, RBOuterPen);
1525 if(uFlags & BF_BOTTOM)
1527 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1528 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1530 if(uFlags & BF_RIGHT)
1532 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1533 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1536 /* Draw the inner edge */
1537 SelectObject(hdc, LTInnerPen);
1538 if(uFlags & BF_TOP)
1540 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1541 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1543 if(uFlags & BF_LEFT)
1545 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1546 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1548 SelectObject(hdc, RBInnerPen);
1549 if(uFlags & BF_BOTTOM)
1551 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1552 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1554 if(uFlags & BF_RIGHT)
1556 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1557 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1560 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1562 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1563 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1565 if(uFlags & BF_LEFT) InnerRect.left += add;
1566 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1567 if(uFlags & BF_TOP) InnerRect.top += add;
1568 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1570 if((uFlags & BF_MIDDLE) && retval)
1572 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1573 theme, part, state);
1574 FillRect(hdc, &InnerRect, br);
1575 DeleteObject (br);
1578 if(uFlags & BF_ADJUST)
1579 *contentsRect = InnerRect;
1582 /* Cleanup */
1583 SelectObject(hdc, SavePen);
1584 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1585 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1586 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1587 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1588 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1589 return retval;
1593 /***********************************************************************
1594 * DrawThemeEdge (UXTHEME.@)
1596 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1597 * difference is that it does not rely on the system colors alone, but
1598 * also allows color specification in the theme.
1600 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1601 int iStateId, const RECT *pDestRect, UINT uEdge,
1602 UINT uFlags, RECT *pContentRect)
1604 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1605 if(!hTheme)
1606 return E_HANDLE;
1608 if(uFlags & BF_DIAGONAL)
1609 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1610 uEdge, uFlags, pContentRect);
1611 else
1612 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1613 uEdge, uFlags, pContentRect);
1617 /***********************************************************************
1618 * DrawThemeIcon (UXTHEME.@)
1620 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1621 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1623 FIXME("%d %d: stub\n", iPartId, iStateId);
1624 if(!hTheme)
1625 return E_HANDLE;
1626 return E_NOTIMPL;
1629 /***********************************************************************
1630 * DrawThemeText (UXTHEME.@)
1632 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1633 LPCWSTR pszText, int iCharCount, DWORD flags,
1634 DWORD flags2, const RECT *pRect)
1636 DTTOPTS opts = { 0 };
1637 RECT rt;
1639 TRACE("%d %d\n", iPartId, iStateId);
1641 rt = *pRect;
1643 opts.dwSize = sizeof(opts);
1644 if (flags2 & DTT_GRAYED) {
1645 opts.dwFlags = DTT_TEXTCOLOR;
1646 opts.crText = GetSysColor(COLOR_GRAYTEXT);
1648 return DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, flags, &rt, &opts);
1651 /***********************************************************************
1652 * DrawThemeTextEx (UXTHEME.@)
1654 HRESULT WINAPI DrawThemeTextEx(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1655 LPCWSTR pszText, int iCharCount, DWORD flags, RECT *rect, const DTTOPTS *options)
1657 HRESULT hr;
1658 HFONT hFont = NULL;
1659 HGDIOBJ oldFont = NULL;
1660 LOGFONTW logfont;
1661 COLORREF textColor;
1662 COLORREF oldTextColor;
1663 int oldBkMode;
1665 TRACE("%p %p %d %d %s:%d 0x%08x %p %p\n", hTheme, hdc, iPartId, iStateId,
1666 debugstr_wn(pszText, iCharCount), iCharCount, flags, rect, options);
1668 if(!hTheme)
1669 return E_HANDLE;
1671 if (options->dwFlags & ~DTT_TEXTCOLOR)
1672 FIXME("unsupported flags 0x%08x\n", options->dwFlags);
1674 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1675 if(SUCCEEDED(hr)) {
1676 hFont = CreateFontIndirectW(&logfont);
1677 if(!hFont)
1678 TRACE("Failed to create font\n");
1681 if(hFont)
1682 oldFont = SelectObject(hdc, hFont);
1684 if (options->dwFlags & DTT_TEXTCOLOR)
1685 textColor = options->crText;
1686 else {
1687 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1688 textColor = GetTextColor(hdc);
1690 oldTextColor = SetTextColor(hdc, textColor);
1691 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1692 DrawTextW(hdc, pszText, iCharCount, rect, flags);
1693 SetBkMode(hdc, oldBkMode);
1694 SetTextColor(hdc, oldTextColor);
1696 if(hFont) {
1697 SelectObject(hdc, oldFont);
1698 DeleteObject(hFont);
1700 return S_OK;
1703 /***********************************************************************
1704 * GetThemeBackgroundContentRect (UXTHEME.@)
1706 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1707 int iStateId,
1708 const RECT *pBoundingRect,
1709 RECT *pContentRect)
1711 MARGINS margin;
1712 HRESULT hr;
1714 TRACE("(%d,%d)\n", iPartId, iStateId);
1715 if(!hTheme)
1716 return E_HANDLE;
1718 /* try content margins property... */
1719 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1720 if(SUCCEEDED(hr)) {
1721 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1722 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1723 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1724 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1725 } else {
1726 /* otherwise, try to determine content rect from the background type and props */
1727 int bgtype = BT_BORDERFILL;
1728 *pContentRect = *pBoundingRect;
1730 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1731 if(bgtype == BT_BORDERFILL) {
1732 int bordersize = 1;
1734 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1735 InflateRect(pContentRect, -bordersize, -bordersize);
1736 } else if ((bgtype == BT_IMAGEFILE)
1737 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1738 TMT_SIZINGMARGINS, NULL, &margin)))) {
1739 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1740 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1741 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1742 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1744 /* If nothing was found, leave unchanged */
1747 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1749 return S_OK;
1752 /***********************************************************************
1753 * GetThemeBackgroundExtent (UXTHEME.@)
1755 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1756 int iStateId, const RECT *pContentRect,
1757 RECT *pExtentRect)
1759 MARGINS margin;
1760 HRESULT hr;
1762 TRACE("(%d,%d)\n", iPartId, iStateId);
1763 if(!hTheme)
1764 return E_HANDLE;
1766 /* try content margins property... */
1767 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1768 if(SUCCEEDED(hr)) {
1769 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1770 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1771 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1772 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1773 } else {
1774 /* otherwise, try to determine content rect from the background type and props */
1775 int bgtype = BT_BORDERFILL;
1776 *pExtentRect = *pContentRect;
1778 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1779 if(bgtype == BT_BORDERFILL) {
1780 int bordersize = 1;
1782 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1783 InflateRect(pExtentRect, bordersize, bordersize);
1784 } else if ((bgtype == BT_IMAGEFILE)
1785 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1786 TMT_SIZINGMARGINS, NULL, &margin)))) {
1787 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1788 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1789 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1790 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1792 /* If nothing was found, leave unchanged */
1795 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1797 return S_OK;
1800 static inline void flush_rgn_data( HRGN rgn, RGNDATA *data )
1802 HRGN tmp = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1804 CombineRgn( rgn, rgn, tmp, RGN_OR );
1805 DeleteObject( tmp );
1806 data->rdh.nCount = 0;
1809 static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len )
1811 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
1813 if (len <= 0) return;
1814 rect->left = x;
1815 rect->top = y;
1816 rect->right = x + len;
1817 rect->bottom = y + 1;
1818 data->rdh.nCount++;
1819 if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT))
1820 flush_rgn_data( rgn, data );
1823 static HRESULT create_image_bg_region(HTHEME theme, int part, int state, const RECT *rect, HRGN *rgn)
1825 RECT r;
1826 HDC dc;
1827 HBITMAP bmp;
1828 HRGN hrgn;
1829 BOOL istrans;
1830 COLORREF transcolour;
1831 HBRUSH transbrush;
1832 unsigned int x, y, start;
1833 BITMAPINFO bitmapinfo;
1834 DWORD *bits;
1835 char buffer[4096];
1836 RGNDATA *data = (RGNDATA *)buffer;
1838 if (FAILED(GetThemeBool(theme, part, state, TMT_TRANSPARENT, &istrans)) || !istrans) {
1839 *rgn = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
1840 return S_OK;
1843 r = *rect;
1844 OffsetRect(&r, -r.left, -r.top);
1846 if (FAILED(GetThemeColor(theme, part, state, TMT_TRANSPARENTCOLOR, &transcolour)))
1847 transcolour = RGB(255, 0, 255); /* defaults to magenta */
1849 dc = CreateCompatibleDC(NULL);
1850 if (!dc) {
1851 WARN("CreateCompatibleDC failed\n");
1852 return E_FAIL;
1855 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1856 bitmapinfo.bmiHeader.biWidth = rect->right - rect->left;
1857 bitmapinfo.bmiHeader.biHeight = -(rect->bottom - rect->top);
1858 bitmapinfo.bmiHeader.biPlanes = 1;
1859 bitmapinfo.bmiHeader.biBitCount = 32;
1860 bitmapinfo.bmiHeader.biCompression = BI_RGB;
1861 bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * 4;
1862 bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
1863 bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
1864 bitmapinfo.bmiHeader.biClrUsed = 0;
1865 bitmapinfo.bmiHeader.biClrImportant = 0;
1867 bmp = CreateDIBSection(dc, &bitmapinfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1868 if (!bmp) {
1869 WARN("CreateDIBSection failed\n");
1870 DeleteDC(dc);
1871 return E_FAIL;
1874 SelectObject(dc, bmp);
1876 transbrush = CreateSolidBrush(transcolour);
1877 FillRect(dc, &r, transbrush);
1878 DeleteObject(transbrush);
1880 if (FAILED(DrawThemeBackground(theme, dc, part, state, &r, NULL))) {
1881 WARN("DrawThemeBackground failed\n");
1882 DeleteObject(bmp);
1883 DeleteDC(dc);
1884 return E_FAIL;
1887 data->rdh.dwSize = sizeof(data->rdh);
1888 data->rdh.iType = RDH_RECTANGLES;
1889 data->rdh.nCount = 0;
1890 data->rdh.nRgnSize = sizeof(buffer) - sizeof(data->rdh);
1892 hrgn = CreateRectRgn(0, 0, 0, 0);
1894 for (y = 0; y < r.bottom; y++, bits += r.right) {
1895 x = 0;
1896 while (x < r.right) {
1897 while (x < r.right && (bits[x] & 0xffffff) == transcolour) x++;
1898 start = x;
1899 while (x < r.right && !((bits[x] & 0xffffff) == transcolour)) x++;
1900 add_row( hrgn, data, rect->left + start, rect->top + y, x - start );
1904 if (data->rdh.nCount > 0) flush_rgn_data(hrgn, data);
1906 *rgn = hrgn;
1908 DeleteObject(bmp);
1909 DeleteDC(dc);
1911 return S_OK;
1914 /***********************************************************************
1915 * GetThemeBackgroundRegion (UXTHEME.@)
1917 * Calculate the background region, taking into consideration transparent areas
1918 * of the background image.
1920 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1921 int iStateId, const RECT *pRect,
1922 HRGN *pRegion)
1924 HRESULT hr = S_OK;
1925 int bgtype = BT_BORDERFILL;
1927 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1928 if(!hTheme)
1929 return E_HANDLE;
1930 if(!pRect || !pRegion)
1931 return E_POINTER;
1933 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1934 if(bgtype == BT_IMAGEFILE) {
1935 hr = create_image_bg_region(hTheme, iPartId, iStateId, pRect, pRegion);
1937 else if(bgtype == BT_BORDERFILL) {
1938 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1939 if(!*pRegion)
1940 hr = HRESULT_FROM_WIN32(GetLastError());
1942 else {
1943 FIXME("Unknown background type\n");
1944 /* This should never happen, and hence I don't know what to return */
1945 hr = E_FAIL;
1947 return hr;
1950 /* compute part size for "borderfill" backgrounds */
1951 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1952 int iStateId, THEMESIZE eSize, POINT* psz)
1954 HRESULT hr = S_OK;
1955 int bordersize = 1;
1957 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1958 &bordersize)))
1960 psz->x = psz->y = 2*bordersize;
1961 if (eSize != TS_MIN)
1963 psz->x++;
1964 psz->y++;
1967 return hr;
1970 /***********************************************************************
1971 * GetThemePartSize (UXTHEME.@)
1973 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1974 int iStateId, RECT *prc, THEMESIZE eSize,
1975 SIZE *psz)
1977 int bgtype = BT_BORDERFILL;
1978 HRESULT hr = S_OK;
1979 POINT size = {1, 1};
1981 if(!hTheme)
1982 return E_HANDLE;
1984 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1985 if (bgtype == BT_NONE)
1986 /* do nothing */;
1987 else if(bgtype == BT_IMAGEFILE)
1988 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1989 else if(bgtype == BT_BORDERFILL)
1990 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1991 else {
1992 FIXME("Unknown background type\n");
1993 /* This should never happen, and hence I don't know what to return */
1994 hr = E_FAIL;
1996 psz->cx = size.x;
1997 psz->cy = size.y;
1998 return hr;
2002 /***********************************************************************
2003 * GetThemeTextExtent (UXTHEME.@)
2005 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2006 int iStateId, LPCWSTR pszText, int iCharCount,
2007 DWORD dwTextFlags, const RECT *pBoundingRect,
2008 RECT *pExtentRect)
2010 HRESULT hr;
2011 HFONT hFont = NULL;
2012 HGDIOBJ oldFont = NULL;
2013 LOGFONTW logfont;
2014 RECT rt = {0,0,0xFFFF,0xFFFF};
2016 TRACE("%d %d\n", iPartId, iStateId);
2017 if(!hTheme)
2018 return E_HANDLE;
2020 if(pBoundingRect)
2021 rt = *pBoundingRect;
2023 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2024 if(SUCCEEDED(hr)) {
2025 hFont = CreateFontIndirectW(&logfont);
2026 if(!hFont)
2027 TRACE("Failed to create font\n");
2029 if(hFont)
2030 oldFont = SelectObject(hdc, hFont);
2032 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2033 *pExtentRect = rt;
2035 if(hFont) {
2036 SelectObject(hdc, oldFont);
2037 DeleteObject(hFont);
2039 return S_OK;
2042 /***********************************************************************
2043 * GetThemeTextMetrics (UXTHEME.@)
2045 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2046 int iStateId, TEXTMETRICW *ptm)
2048 HRESULT hr;
2049 HFONT hFont = NULL;
2050 HGDIOBJ oldFont = NULL;
2051 LOGFONTW logfont;
2053 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2054 if(!hTheme)
2055 return E_HANDLE;
2057 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2058 if(SUCCEEDED(hr)) {
2059 hFont = CreateFontIndirectW(&logfont);
2060 if(!hFont)
2061 TRACE("Failed to create font\n");
2063 if(hFont)
2064 oldFont = SelectObject(hdc, hFont);
2066 if(!GetTextMetricsW(hdc, ptm))
2067 hr = HRESULT_FROM_WIN32(GetLastError());
2069 if(hFont) {
2070 SelectObject(hdc, oldFont);
2071 DeleteObject(hFont);
2073 return hr;
2076 /***********************************************************************
2077 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2079 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2080 int iStateId)
2082 int bgtype = BT_BORDERFILL;
2083 RECT rect = {0, 0, 0, 0};
2084 HBITMAP bmpSrc;
2085 RECT rcSrc;
2086 BOOL hasAlpha;
2087 INT transparent;
2088 COLORREF transparentcolor;
2090 TRACE("(%d,%d)\n", iPartId, iStateId);
2092 if(!hTheme)
2093 return FALSE;
2095 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2097 if (bgtype != BT_IMAGEFILE) return FALSE;
2099 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2100 &bmpSrc, &rcSrc, &hasAlpha)))
2101 return FALSE;
2103 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2104 &transparentcolor, FALSE);
2105 return (transparent != ALPHABLEND_NONE);