push a9ea98eed33411fd355fd4e8b78f7880f1dd09a3
[wine/hacks.git] / dlls / uxtheme / draw.c
blob8b3b95956c5f276be76b7b90b857552604a0e1c7
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 "config.h"
23 #include <stdlib.h>
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "vfwmsgs.h"
31 #include "uxtheme.h"
32 #include "tmschema.h"
34 #include "msstyles.h"
35 #include "uxthemedll.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
41 /***********************************************************************
42 * Defines and global variables
45 extern ATOM atDialogThemeEnabled;
47 /***********************************************************************/
49 /***********************************************************************
50 * EnableThemeDialogTexture (UXTHEME.@)
52 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
54 static const WCHAR szTab[] = { 'T','a','b',0 };
55 HRESULT hr;
57 TRACE("(%p,0x%08x\n", hwnd, dwFlags);
58 hr = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
59 (HANDLE)(dwFlags|0x80000000));
60 /* 0x80000000 serves as a "flags set" flag */
61 if (FAILED(hr))
62 return hr;
63 if (dwFlags & ETDT_USETABTEXTURE)
64 return SetWindowTheme (hwnd, NULL, szTab);
65 else
66 return SetWindowTheme (hwnd, NULL, NULL);
67 return S_OK;
70 /***********************************************************************
71 * IsThemeDialogTextureEnabled (UXTHEME.@)
73 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
75 DWORD dwDialogTextureFlags;
76 TRACE("(%p)\n", hwnd);
78 dwDialogTextureFlags = (DWORD)GetPropW (hwnd,
79 (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled));
80 if (dwDialogTextureFlags == 0)
81 /* Means EnableThemeDialogTexture wasn't called for this dialog */
82 return TRUE;
84 return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
87 /***********************************************************************
88 * DrawThemeParentBackground (UXTHEME.@)
90 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
92 RECT rt;
93 POINT org;
94 HWND hParent;
95 HRGN clip = NULL;
96 int hasClip = -1;
98 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
99 hParent = GetParent(hwnd);
100 if(!hParent)
101 hParent = hwnd;
102 if(prc) {
103 CopyRect(&rt, prc);
104 MapWindowPoints(hwnd, NULL, (LPPOINT)&rt, 2);
106 clip = CreateRectRgn(0,0,1,1);
107 hasClip = GetClipRgn(hdc, clip);
108 if(hasClip == -1)
109 TRACE("Failed to get original clipping region\n");
110 else
111 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
113 else {
114 GetClientRect(hParent, &rt);
115 MapWindowPoints(hParent, NULL, (LPPOINT)&rt, 2);
118 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
120 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
121 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
123 SetViewportOrgEx(hdc, org.x, org.y, NULL);
124 if(prc) {
125 if(hasClip == 0)
126 SelectClipRgn(hdc, NULL);
127 else if(hasClip == 1)
128 SelectClipRgn(hdc, clip);
129 DeleteObject(clip);
131 return S_OK;
135 /***********************************************************************
136 * DrawThemeBackground (UXTHEME.@)
138 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
139 int iStateId, const RECT *pRect,
140 const RECT *pClipRect)
142 DTBGOPTS opts;
143 opts.dwSize = sizeof(DTBGOPTS);
144 opts.dwFlags = 0;
145 if(pClipRect) {
146 opts.dwFlags |= DTBG_CLIPRECT;
147 CopyRect(&opts.rcClip, pClipRect);
149 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
152 /***********************************************************************
153 * UXTHEME_SelectImage
155 * Select the image to use
157 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
159 PTHEME_PROPERTY tp;
160 int imageselecttype = IST_NONE;
161 int i;
162 int image;
163 if(glyph)
164 image = TMT_GLYPHIMAGEFILE;
165 else
166 image = TMT_IMAGEFILE;
168 if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
169 return tp;
170 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
172 if(imageselecttype == IST_DPI) {
173 int reqdpi = 0;
174 int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
175 for(i=4; i>=0; i--) {
176 reqdpi = 0;
177 if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
178 if(reqdpi != 0 && screendpi >= reqdpi) {
179 TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
180 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
184 /* If an image couldn't be selected, choose the first one */
185 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
187 else if(imageselecttype == IST_SIZE) {
188 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
189 POINT reqsize;
190 for(i=4; i>=0; i--) {
191 PTHEME_PROPERTY fileProp =
192 MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
193 if (!fileProp) continue;
194 if(FAILED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
195 /* fall back to size of Nth image */
196 WCHAR szPath[MAX_PATH];
197 int imagelayout = IL_HORIZONTAL;
198 int imagecount = 1;
199 BITMAP bmp;
200 HBITMAP hBmp;
201 BOOL hasAlpha;
203 lstrcpynW(szPath, fileProp->lpValue,
204 min(fileProp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
205 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
206 if(!hBmp) continue;
208 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
209 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
211 GetObjectW(hBmp, sizeof(bmp), &bmp);
212 if(imagelayout == IL_VERTICAL) {
213 reqsize.x = bmp.bmWidth;
214 reqsize.y = bmp.bmHeight/imagecount;
216 else {
217 reqsize.x = bmp.bmWidth/imagecount;
218 reqsize.y = bmp.bmHeight;
221 if(reqsize.x <= size.x && reqsize.y <= size.y) {
222 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
223 return fileProp;
226 /* If an image couldn't be selected, choose the smallest one */
227 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
229 return NULL;
232 /***********************************************************************
233 * UXTHEME_LoadImage
235 * Load image for part/state
237 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
238 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
240 int imagelayout = IL_HORIZONTAL;
241 int imagecount = 1;
242 int imagenum;
243 BITMAP bmp;
244 WCHAR szPath[MAX_PATH];
245 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
246 if(!tp) {
247 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
248 return E_PROP_ID_UNSUPPORTED;
250 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
251 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
252 if(!*hBmp) {
253 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
254 return HRESULT_FROM_WIN32(GetLastError());
257 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
258 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
260 imagenum = max (min (imagecount, iStateId), 1) - 1;
261 GetObjectW(*hBmp, sizeof(bmp), &bmp);
262 if(imagelayout == IL_VERTICAL) {
263 int height = bmp.bmHeight/imagecount;
264 bmpRect->left = 0;
265 bmpRect->right = bmp.bmWidth;
266 bmpRect->top = imagenum * height;
267 bmpRect->bottom = bmpRect->top + height;
269 else {
270 int width = bmp.bmWidth/imagecount;
271 bmpRect->left = imagenum * width;
272 bmpRect->right = bmpRect->left + width;
273 bmpRect->top = 0;
274 bmpRect->bottom = bmp.bmHeight;
276 return S_OK;
279 /***********************************************************************
280 * UXTHEME_StretchBlt
282 * Pseudo TransparentBlt/StretchBlt
284 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
285 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
286 INT transparent, COLORREF transcolor)
288 static const BLENDFUNCTION blendFunc =
290 AC_SRC_OVER, /* BlendOp */
291 0, /* BlendFlag */
292 255, /* SourceConstantAlpha */
293 AC_SRC_ALPHA /* AlphaFormat */
295 if (transparent == ALPHABLEND_BINARY) {
296 /* Ensure we don't pass any negative values to TransparentBlt */
297 return TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
298 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
299 transcolor);
301 if ((transparent == ALPHABLEND_NONE) ||
302 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
303 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
304 blendFunc))
306 return StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
307 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
308 SRCCOPY);
310 return TRUE;
313 /***********************************************************************
314 * UXTHEME_Blt
316 * Simplify sending same width/height for both source and dest
318 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
319 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
320 INT transparent, COLORREF transcolor)
322 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
323 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
324 transparent, transcolor);
327 /***********************************************************************
328 * UXTHEME_SizedBlt
330 * Stretches or tiles, depending on sizingtype.
332 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
333 int nWidthDst, int nHeightDst,
334 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
335 int nWidthSrc, int nHeightSrc,
336 int sizingtype,
337 INT transparent, COLORREF transcolor)
339 if (sizingtype == ST_TILE)
341 HDC hdcTemp;
342 BOOL result = FALSE;
344 if (!nWidthSrc || !nHeightSrc) return TRUE;
346 /* For destination width/height less than or equal to source
347 width/height, do not bother with memory bitmap optimization */
348 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
350 int bltWidth = min (nWidthDst, nWidthSrc);
351 int bltHeight = min (nHeightDst, nHeightSrc);
353 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
354 hdcSrc, nXOriginSrc, nYOriginSrc,
355 transparent, transcolor);
358 /* Create a DC with a bitmap consisting of a tiling of the source
359 bitmap, with standard GDI functions. This is faster than an
360 iteration with UXTHEME_Blt(). */
361 hdcTemp = CreateCompatibleDC(hdcSrc);
362 if (hdcTemp != 0)
364 HBITMAP bitmapTemp;
365 HBITMAP bitmapOrig;
366 int nWidthTemp, nHeightTemp;
367 int xOfs, xRemaining;
368 int yOfs, yRemaining;
369 int growSize;
371 /* Calculate temp dimensions of integer multiples of source dimensions */
372 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
373 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
374 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
375 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
377 /* Initial copy of bitmap */
378 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
380 /* Extend bitmap in the X direction. Growth of width is exponential */
381 xOfs = nWidthSrc;
382 xRemaining = nWidthTemp - nWidthSrc;
383 growSize = nWidthSrc;
384 while (xRemaining > 0)
386 growSize = min(growSize, xRemaining);
387 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
388 xOfs += growSize;
389 xRemaining -= growSize;
390 growSize *= 2;
393 /* Extend bitmap in the Y direction. Growth of height is exponential */
394 yOfs = nHeightSrc;
395 yRemaining = nHeightTemp - nHeightSrc;
396 growSize = nHeightSrc;
397 while (yRemaining > 0)
399 growSize = min(growSize, yRemaining);
400 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
401 yOfs += growSize;
402 yRemaining -= growSize;
403 growSize *= 2;
406 /* Use temporary hdc for source */
407 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
408 hdcTemp, 0, 0,
409 transparent, transcolor);
411 SelectObject(hdcTemp, bitmapOrig);
412 DeleteObject(bitmapTemp);
414 DeleteDC(hdcTemp);
415 return result;
417 else
419 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
420 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
421 transparent, transcolor);
425 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
426 * depend on whether the image has full alpha or whether it is
427 * color-transparent or just opaque. */
428 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
429 BOOL hasImageAlpha, INT* transparent,
430 COLORREF* transparentcolor, BOOL glyph)
432 if (hasImageAlpha)
434 *transparent = ALPHABLEND_FULL;
435 *transparentcolor = RGB (255, 0, 255);
437 else
439 BOOL trans = FALSE;
440 GetThemeBool(hTheme, iPartId, iStateId,
441 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
442 if(trans) {
443 *transparent = ALPHABLEND_BINARY;
444 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
445 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
446 transparentcolor))) {
447 /* If image is transparent, but no color was specified, use magenta */
448 *transparentcolor = RGB(255, 0, 255);
451 else
452 *transparent = ALPHABLEND_NONE;
456 /***********************************************************************
457 * UXTHEME_DrawImageGlyph
459 * Draw an imagefile glyph
461 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
462 int iStateId, RECT *pRect,
463 const DTBGOPTS *pOptions)
465 HRESULT hr;
466 HBITMAP bmpSrc = NULL;
467 HDC hdcSrc = NULL;
468 HGDIOBJ oldSrc = NULL;
469 RECT rcSrc;
470 INT transparent = FALSE;
471 COLORREF transparentcolor;
472 int valign = VA_CENTER;
473 int halign = HA_CENTER;
474 POINT dstSize;
475 POINT srcSize;
476 POINT topleft;
477 BOOL hasAlpha;
479 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
480 &bmpSrc, &rcSrc, &hasAlpha);
481 if(FAILED(hr)) return hr;
482 hdcSrc = CreateCompatibleDC(hdc);
483 if(!hdcSrc) {
484 hr = HRESULT_FROM_WIN32(GetLastError());
485 return hr;
487 oldSrc = SelectObject(hdcSrc, bmpSrc);
489 dstSize.x = pRect->right-pRect->left;
490 dstSize.y = pRect->bottom-pRect->top;
491 srcSize.x = rcSrc.right-rcSrc.left;
492 srcSize.y = rcSrc.bottom-rcSrc.top;
494 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
495 &transparentcolor, TRUE);
496 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
497 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
499 topleft.x = pRect->left;
500 topleft.y = pRect->top;
501 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
502 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
503 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
504 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
506 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
507 hdcSrc, rcSrc.left, rcSrc.top,
508 transparent, transparentcolor)) {
509 hr = HRESULT_FROM_WIN32(GetLastError());
512 SelectObject(hdcSrc, oldSrc);
513 DeleteDC(hdcSrc);
514 return hr;
517 /***********************************************************************
518 * UXTHEME_DrawImageGlyph
520 * Draw glyph on top of background, if appropriate
522 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
523 int iStateId, RECT *pRect,
524 const DTBGOPTS *pOptions)
526 int glyphtype = GT_NONE;
528 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
530 if(glyphtype == GT_IMAGEGLYPH) {
531 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
533 else if(glyphtype == GT_FONTGLYPH) {
534 /* I don't know what a font glyph is, I've never seen it used in any themes */
535 FIXME("Font glyph\n");
537 return S_OK;
540 /***********************************************************************
541 * get_image_part_size
543 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
545 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
546 int iStateId, RECT *prc, THEMESIZE eSize,
547 POINT *psz)
549 HRESULT hr = S_OK;
550 HBITMAP bmpSrc;
551 RECT rcSrc;
552 BOOL hasAlpha;
554 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
555 &bmpSrc, &rcSrc, &hasAlpha);
556 if (FAILED(hr)) return hr;
558 switch (eSize)
560 case TS_DRAW:
561 if (prc != NULL)
563 RECT rcDst;
564 POINT dstSize;
565 POINT srcSize;
566 int sizingtype = ST_STRETCH;
567 BOOL uniformsizing = FALSE;
569 CopyRect(&rcDst, prc);
571 dstSize.x = rcDst.right-rcDst.left;
572 dstSize.y = rcDst.bottom-rcDst.top;
573 srcSize.x = rcSrc.right-rcSrc.left;
574 srcSize.y = rcSrc.bottom-rcSrc.top;
576 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
577 if(uniformsizing) {
578 /* Scale height and width equally */
579 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
581 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
582 rcDst.bottom = rcDst.top + dstSize.y;
584 else
586 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
587 rcDst.right = rcDst.left + dstSize.x;
591 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
592 if(sizingtype == ST_TRUESIZE) {
593 int truesizestretchmark = 100;
595 if(dstSize.x < 0 || dstSize.y < 0) {
596 BOOL mirrorimage = TRUE;
597 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
598 if(mirrorimage) {
599 if(dstSize.x < 0) {
600 rcDst.left += dstSize.x;
601 rcDst.right += dstSize.x;
603 if(dstSize.y < 0) {
604 rcDst.top += dstSize.y;
605 rcDst.bottom += dstSize.y;
609 /* Whatever TrueSizeStretchMark does - it does not seem to
610 * be what's outlined below. It appears as if native
611 * uxtheme always stretches if dest is smaller than source
612 * (ie as if TrueSizeStretchMark==100 with the code below) */
613 #if 0
614 /* Only stretch when target exceeds source by truesizestretchmark percent */
615 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
616 #endif
617 if(dstSize.x < 0 || dstSize.y < 0 ||
618 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
619 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
620 memcpy (psz, &dstSize, sizeof (SIZE));
622 else {
623 memcpy (psz, &srcSize, sizeof (SIZE));
626 else
628 psz->x = abs(dstSize.x);
629 psz->y = abs(dstSize.y);
631 break;
633 /* else fall through */
634 case TS_MIN:
635 /* FIXME: couldn't figure how native uxtheme computes min size */
636 case TS_TRUE:
637 psz->x = rcSrc.right - rcSrc.left;
638 psz->y = rcSrc.bottom - rcSrc.top;
639 break;
641 return hr;
644 /***********************************************************************
645 * UXTHEME_DrawImageBackground
647 * Draw an imagefile background
649 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
650 int iStateId, RECT *pRect,
651 const DTBGOPTS *pOptions)
653 HRESULT hr = S_OK;
654 HBITMAP bmpSrc;
655 HGDIOBJ oldSrc;
656 HDC hdcSrc;
657 RECT rcSrc;
658 RECT rcDst;
659 POINT dstSize;
660 POINT srcSize;
661 POINT drawSize;
662 int sizingtype = ST_STRETCH;
663 INT transparent;
664 COLORREF transparentcolor = 0;
665 BOOL hasAlpha;
667 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
668 &bmpSrc, &rcSrc, &hasAlpha);
669 if(FAILED(hr)) return hr;
670 hdcSrc = CreateCompatibleDC(hdc);
671 if(!hdcSrc) {
672 hr = HRESULT_FROM_WIN32(GetLastError());
673 return hr;
675 oldSrc = SelectObject(hdcSrc, bmpSrc);
677 CopyRect(&rcDst, pRect);
679 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
680 &transparentcolor, FALSE);
682 dstSize.x = rcDst.right-rcDst.left;
683 dstSize.y = rcDst.bottom-rcDst.top;
684 srcSize.x = rcSrc.right-rcSrc.left;
685 srcSize.y = rcSrc.bottom-rcSrc.top;
687 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
688 if(sizingtype == ST_TRUESIZE) {
689 int valign = VA_CENTER, halign = HA_CENTER;
691 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
692 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
693 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
695 if (halign == HA_CENTER)
696 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
697 else if (halign == HA_RIGHT)
698 rcDst.left = rcDst.right - drawSize.x;
699 if (valign == VA_CENTER)
700 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
701 else if (valign == VA_BOTTOM)
702 rcDst.top = rcDst.bottom - drawSize.y;
703 rcDst.right = rcDst.left + drawSize.x;
704 rcDst.bottom = rcDst.top + drawSize.y;
705 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
706 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
707 transparent, transparentcolor))
708 hr = HRESULT_FROM_WIN32(GetLastError());
710 else {
711 HDC hdcDst = NULL;
712 MARGINS sm;
713 POINT org;
715 dstSize.x = abs(dstSize.x);
716 dstSize.y = abs(dstSize.y);
718 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
720 hdcDst = hdc;
721 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
723 /* Upper left corner */
724 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
725 hdcSrc, rcSrc.left, rcSrc.top,
726 transparent, transparentcolor)) {
727 hr = HRESULT_FROM_WIN32(GetLastError());
728 goto draw_error;
730 /* Upper right corner */
731 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
732 sm.cxRightWidth, sm.cyTopHeight,
733 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
734 transparent, transparentcolor)) {
735 hr = HRESULT_FROM_WIN32(GetLastError());
736 goto draw_error;
738 /* Lower left corner */
739 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
740 sm.cxLeftWidth, sm.cyBottomHeight,
741 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
742 transparent, transparentcolor)) {
743 hr = HRESULT_FROM_WIN32(GetLastError());
744 goto draw_error;
746 /* Lower right corner */
747 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
748 sm.cxRightWidth, sm.cyBottomHeight,
749 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
750 transparent, transparentcolor)) {
751 hr = HRESULT_FROM_WIN32(GetLastError());
752 goto draw_error;
755 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
756 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
757 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
758 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
759 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
761 if(destCenterWidth > 0) {
762 /* Center top */
763 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
764 destCenterWidth, sm.cyTopHeight,
765 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
766 srcCenterWidth, sm.cyTopHeight,
767 sizingtype, transparent, transparentcolor)) {
768 hr = HRESULT_FROM_WIN32(GetLastError());
769 goto draw_error;
771 /* Center bottom */
772 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
773 destCenterWidth, sm.cyBottomHeight,
774 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
775 srcCenterWidth, sm.cyBottomHeight,
776 sizingtype, transparent, transparentcolor)) {
777 hr = HRESULT_FROM_WIN32(GetLastError());
778 goto draw_error;
781 if(destCenterHeight > 0) {
782 /* Left center */
783 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
784 sm.cxLeftWidth, destCenterHeight,
785 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
786 sm.cxLeftWidth, srcCenterHeight,
787 sizingtype,
788 transparent, transparentcolor)) {
789 hr = HRESULT_FROM_WIN32(GetLastError());
790 goto draw_error;
792 /* Right center */
793 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
794 sm.cxRightWidth, destCenterHeight,
795 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
796 sm.cxRightWidth, srcCenterHeight,
797 sizingtype, transparent, transparentcolor)) {
798 hr = HRESULT_FROM_WIN32(GetLastError());
799 goto draw_error;
802 if(destCenterHeight > 0 && destCenterWidth > 0) {
803 BOOL borderonly = FALSE;
804 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
805 if(!borderonly) {
806 /* Center */
807 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
808 destCenterWidth, destCenterHeight,
809 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
810 srcCenterWidth, srcCenterHeight,
811 sizingtype, transparent, transparentcolor)) {
812 hr = HRESULT_FROM_WIN32(GetLastError());
813 goto draw_error;
819 draw_error:
820 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
822 SelectObject(hdcSrc, oldSrc);
823 DeleteDC(hdcSrc);
824 CopyRect(pRect, &rcDst);
825 return hr;
828 /***********************************************************************
829 * UXTHEME_DrawBorderRectangle
831 * Draw the bounding rectangle for a borderfill background
833 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
834 int iStateId, RECT *pRect,
835 const DTBGOPTS *pOptions)
837 HRESULT hr = S_OK;
838 HPEN hPen;
839 HGDIOBJ oldPen;
840 COLORREF bordercolor = RGB(0,0,0);
841 int bordersize = 1;
843 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
844 if(bordersize > 0) {
845 POINT ptCorners[5];
846 ptCorners[0].x = pRect->left;
847 ptCorners[0].y = pRect->top;
848 ptCorners[1].x = pRect->right-1;
849 ptCorners[1].y = pRect->top;
850 ptCorners[2].x = pRect->right-1;
851 ptCorners[2].y = pRect->bottom-1;
852 ptCorners[3].x = pRect->left;
853 ptCorners[3].y = pRect->bottom-1;
854 ptCorners[4].x = pRect->left;
855 ptCorners[4].y = pRect->top;
857 InflateRect(pRect, -bordersize, -bordersize);
858 if(pOptions->dwFlags & DTBG_OMITBORDER)
859 return S_OK;
860 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
861 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
862 if(!hPen)
863 return HRESULT_FROM_WIN32(GetLastError());
864 oldPen = SelectObject(hdc, hPen);
866 if(!Polyline(hdc, ptCorners, 5))
867 hr = HRESULT_FROM_WIN32(GetLastError());
869 SelectObject(hdc, oldPen);
870 DeleteObject(hPen);
872 return hr;
875 /***********************************************************************
876 * UXTHEME_DrawBackgroundFill
878 * Fill a borderfill background rectangle
880 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
881 int iStateId, RECT *pRect,
882 const DTBGOPTS *pOptions)
884 HRESULT hr = S_OK;
885 int filltype = FT_SOLID;
887 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
889 if(pOptions->dwFlags & DTBG_OMITCONTENT)
890 return S_OK;
892 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
894 if(filltype == FT_SOLID) {
895 HBRUSH hBrush;
896 COLORREF fillcolor = RGB(255,255,255);
898 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
899 hBrush = CreateSolidBrush(fillcolor);
900 if(!FillRect(hdc, pRect, hBrush))
901 hr = HRESULT_FROM_WIN32(GetLastError());
902 DeleteObject(hBrush);
904 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
905 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
906 the gradient ratios (no idea how those work)
907 Few themes use this, and the ones I've seen only use 2 colors with
908 a gradient ratio of 0 and 255 respectively
911 COLORREF gradient1 = RGB(0,0,0);
912 COLORREF gradient2 = RGB(255,255,255);
913 TRIVERTEX vert[2];
914 GRADIENT_RECT gRect;
916 FIXME("Gradient implementation not complete\n");
918 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
919 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
921 vert[0].x = pRect->left;
922 vert[0].y = pRect->top;
923 vert[0].Red = GetRValue(gradient1) << 8;
924 vert[0].Green = GetGValue(gradient1) << 8;
925 vert[0].Blue = GetBValue(gradient1) << 8;
926 vert[0].Alpha = 0x0000;
928 vert[1].x = pRect->right;
929 vert[1].y = pRect->bottom;
930 vert[1].Red = GetRValue(gradient2) << 8;
931 vert[1].Green = GetGValue(gradient2) << 8;
932 vert[1].Blue = GetBValue(gradient2) << 8;
933 vert[1].Alpha = 0x0000;
935 gRect.UpperLeft = 0;
936 gRect.LowerRight = 1;
937 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
939 else if(filltype == FT_RADIALGRADIENT) {
940 /* I've never seen this used in a theme */
941 FIXME("Radial gradient\n");
943 else if(filltype == FT_TILEIMAGE) {
944 /* I've never seen this used in a theme */
945 FIXME("Tile image\n");
947 return hr;
950 /***********************************************************************
951 * UXTHEME_DrawBorderBackground
953 * Draw an imagefile background
955 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
956 int iStateId, const RECT *pRect,
957 const DTBGOPTS *pOptions)
959 HRESULT hr;
960 RECT rt;
962 CopyRect(&rt, pRect);
964 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
965 if(FAILED(hr))
966 return hr;
967 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
970 /***********************************************************************
971 * DrawThemeBackgroundEx (UXTHEME.@)
973 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
974 int iStateId, const RECT *pRect,
975 const DTBGOPTS *pOptions)
977 HRESULT hr;
978 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
979 const DTBGOPTS *opts;
980 HRGN clip = NULL;
981 int hasClip = -1;
982 int bgtype = BT_BORDERFILL;
983 RECT rt;
985 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
986 if(!hTheme)
987 return E_HANDLE;
989 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
990 if (bgtype == BT_NONE) return S_OK;
992 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
993 opts = pOptions;
994 if(!opts) opts = &defaultOpts;
996 if(opts->dwFlags & DTBG_CLIPRECT) {
997 clip = CreateRectRgn(0,0,1,1);
998 hasClip = GetClipRgn(hdc, clip);
999 if(hasClip == -1)
1000 TRACE("Failed to get original clipping region\n");
1001 else
1002 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1004 CopyRect(&rt, pRect);
1006 if(bgtype == BT_IMAGEFILE)
1007 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1008 else if(bgtype == BT_BORDERFILL)
1009 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1010 else {
1011 FIXME("Unknown background type\n");
1012 /* This should never happen, and hence I don't know what to return */
1013 hr = E_FAIL;
1015 if(SUCCEEDED(hr))
1016 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1017 if(opts->dwFlags & DTBG_CLIPRECT) {
1018 if(hasClip == 0)
1019 SelectClipRgn(hdc, NULL);
1020 else if(hasClip == 1)
1021 SelectClipRgn(hdc, clip);
1022 DeleteObject(clip);
1024 return hr;
1028 * DrawThemeEdge() implementation
1030 * Since it basically is DrawEdge() with different colors, I copied its code
1031 * from user32's uitools.c.
1034 enum
1036 EDGE_LIGHT,
1037 EDGE_HIGHLIGHT,
1038 EDGE_SHADOW,
1039 EDGE_DARKSHADOW,
1040 EDGE_FILL,
1042 EDGE_WINDOW,
1043 EDGE_WINDOWFRAME,
1045 EDGE_NUMCOLORS
1048 static const struct
1050 int themeProp;
1051 int sysColor;
1052 } EdgeColorMap[EDGE_NUMCOLORS] = {
1053 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1054 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1055 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1056 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1057 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1058 {-1, COLOR_WINDOW},
1059 {-1, COLOR_WINDOWFRAME}
1062 static const signed char LTInnerNormal[] = {
1063 -1, -1, -1, -1,
1064 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1065 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1066 -1, -1, -1, -1
1069 static const signed char LTOuterNormal[] = {
1070 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1071 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1072 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1073 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1076 static const signed char RBInnerNormal[] = {
1077 -1, -1, -1, -1,
1078 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1079 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1080 -1, -1, -1, -1
1083 static const signed char RBOuterNormal[] = {
1084 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1085 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1086 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1087 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1090 static const signed char LTInnerSoft[] = {
1091 -1, -1, -1, -1,
1092 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1093 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1094 -1, -1, -1, -1
1097 static const signed char LTOuterSoft[] = {
1098 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1099 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1100 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1101 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1104 #define RBInnerSoft RBInnerNormal /* These are the same */
1105 #define RBOuterSoft RBOuterNormal
1107 static const signed char LTRBOuterMono[] = {
1108 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1109 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1110 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1111 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1114 static const signed char LTRBInnerMono[] = {
1115 -1, -1, -1, -1,
1116 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1117 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1118 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1121 static const signed char LTRBOuterFlat[] = {
1122 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1123 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1124 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1125 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1128 static const signed char LTRBInnerFlat[] = {
1129 -1, -1, -1, -1,
1130 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1131 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1132 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1135 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1137 COLORREF col;
1138 if ((EdgeColorMap[edgeType].themeProp == -1)
1139 || FAILED (GetThemeColor (theme, part, state,
1140 EdgeColorMap[edgeType].themeProp, &col)))
1141 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1142 return col;
1145 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1147 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1150 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1152 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1155 /***********************************************************************
1156 * draw_diag_edge
1158 * Same as DrawEdge invoked with BF_DIAGONAL
1160 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1161 const RECT* rc, UINT uType,
1162 UINT uFlags, LPRECT contentsRect)
1164 POINT Points[4];
1165 signed char InnerI, OuterI;
1166 HPEN InnerPen, OuterPen;
1167 POINT SavePoint;
1168 HPEN SavePen;
1169 int spx, spy;
1170 int epx, epy;
1171 int Width = rc->right - rc->left;
1172 int Height= rc->bottom - rc->top;
1173 int SmallDiam = Width > Height ? Height : Width;
1174 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1175 || (uType & BDR_OUTER) == BDR_OUTER)
1176 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1177 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1178 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1180 /* Init some vars */
1181 OuterPen = InnerPen = (HPEN)GetStockObject(NULL_PEN);
1182 SavePen = (HPEN)SelectObject(hdc, InnerPen);
1183 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1185 /* Determine the colors of the edges */
1186 if(uFlags & BF_MONO)
1188 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1189 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1191 else if(uFlags & BF_FLAT)
1193 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1194 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1196 else if(uFlags & BF_SOFT)
1198 if(uFlags & BF_BOTTOM)
1200 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1201 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1203 else
1205 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1206 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1209 else
1211 if(uFlags & BF_BOTTOM)
1213 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1214 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1216 else
1218 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1219 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1223 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1224 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1226 MoveToEx(hdc, 0, 0, &SavePoint);
1228 /* Don't ask me why, but this is what is visible... */
1229 /* This must be possible to do much simpler, but I fail to */
1230 /* see the logic in the MS implementation (sigh...). */
1231 /* So, this might look a bit brute force here (and it is), but */
1232 /* it gets the job done;) */
1234 switch(uFlags & BF_RECT)
1236 case 0:
1237 case BF_LEFT:
1238 case BF_BOTTOM:
1239 case BF_BOTTOMLEFT:
1240 /* Left bottom endpoint */
1241 epx = rc->left-1;
1242 spx = epx + SmallDiam;
1243 epy = rc->bottom;
1244 spy = epy - SmallDiam;
1245 break;
1247 case BF_TOPLEFT:
1248 case BF_BOTTOMRIGHT:
1249 /* Left top endpoint */
1250 epx = rc->left-1;
1251 spx = epx + SmallDiam;
1252 epy = rc->top-1;
1253 spy = epy + SmallDiam;
1254 break;
1256 case BF_TOP:
1257 case BF_RIGHT:
1258 case BF_TOPRIGHT:
1259 case BF_RIGHT|BF_LEFT:
1260 case BF_RIGHT|BF_LEFT|BF_TOP:
1261 case BF_BOTTOM|BF_TOP:
1262 case BF_BOTTOM|BF_TOP|BF_LEFT:
1263 case BF_BOTTOMRIGHT|BF_LEFT:
1264 case BF_BOTTOMRIGHT|BF_TOP:
1265 case BF_RECT:
1266 /* Right top endpoint */
1267 spx = rc->left;
1268 epx = spx + SmallDiam;
1269 spy = rc->bottom-1;
1270 epy = spy - SmallDiam;
1271 break;
1274 MoveToEx(hdc, spx, spy, NULL);
1275 SelectObject(hdc, OuterPen);
1276 LineTo(hdc, epx, epy);
1278 SelectObject(hdc, InnerPen);
1280 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1282 case BF_DIAGONAL_ENDBOTTOMLEFT:
1283 case (BF_DIAGONAL|BF_BOTTOM):
1284 case BF_DIAGONAL:
1285 case (BF_DIAGONAL|BF_LEFT):
1286 MoveToEx(hdc, spx-1, spy, NULL);
1287 LineTo(hdc, epx, epy-1);
1288 Points[0].x = spx-add;
1289 Points[0].y = spy;
1290 Points[1].x = rc->left;
1291 Points[1].y = rc->top;
1292 Points[2].x = epx+1;
1293 Points[2].y = epy-1-add;
1294 Points[3] = Points[2];
1295 break;
1297 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1298 MoveToEx(hdc, spx-1, spy, NULL);
1299 LineTo(hdc, epx, epy+1);
1300 Points[0].x = spx-add;
1301 Points[0].y = spy;
1302 Points[1].x = rc->left;
1303 Points[1].y = rc->bottom-1;
1304 Points[2].x = epx+1;
1305 Points[2].y = epy+1+add;
1306 Points[3] = Points[2];
1307 break;
1309 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1310 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1311 case BF_DIAGONAL_ENDTOPRIGHT:
1312 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1313 MoveToEx(hdc, spx+1, spy, NULL);
1314 LineTo(hdc, epx, epy+1);
1315 Points[0].x = epx-1;
1316 Points[0].y = epy+1+add;
1317 Points[1].x = rc->right-1;
1318 Points[1].y = rc->top+add;
1319 Points[2].x = rc->right-1;
1320 Points[2].y = rc->bottom-1;
1321 Points[3].x = spx+add;
1322 Points[3].y = spy;
1323 break;
1325 case BF_DIAGONAL_ENDTOPLEFT:
1326 MoveToEx(hdc, spx, spy-1, NULL);
1327 LineTo(hdc, epx+1, epy);
1328 Points[0].x = epx+1+add;
1329 Points[0].y = epy+1;
1330 Points[1].x = rc->right-1;
1331 Points[1].y = rc->top;
1332 Points[2].x = rc->right-1;
1333 Points[2].y = rc->bottom-1-add;
1334 Points[3].x = spx;
1335 Points[3].y = spy-add;
1336 break;
1338 case (BF_DIAGONAL|BF_TOP):
1339 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1340 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1341 MoveToEx(hdc, spx+1, spy-1, NULL);
1342 LineTo(hdc, epx, epy);
1343 Points[0].x = epx-1;
1344 Points[0].y = epy+1;
1345 Points[1].x = rc->right-1;
1346 Points[1].y = rc->top;
1347 Points[2].x = rc->right-1;
1348 Points[2].y = rc->bottom-1-add;
1349 Points[3].x = spx+add;
1350 Points[3].y = spy-add;
1351 break;
1353 case (BF_DIAGONAL|BF_RIGHT):
1354 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1355 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1356 MoveToEx(hdc, spx, spy, NULL);
1357 LineTo(hdc, epx-1, epy+1);
1358 Points[0].x = spx;
1359 Points[0].y = spy;
1360 Points[1].x = rc->left;
1361 Points[1].y = rc->top+add;
1362 Points[2].x = epx-1-add;
1363 Points[2].y = epy+1+add;
1364 Points[3] = Points[2];
1365 break;
1368 /* Fill the interior if asked */
1369 if((uFlags & BF_MIDDLE) && retval)
1371 HBRUSH hbsave;
1372 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1373 theme, part, state);
1374 HPEN hpsave;
1375 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1376 theme, part, state);
1377 hbsave = (HBRUSH)SelectObject(hdc, hb);
1378 hpsave = (HPEN)SelectObject(hdc, hp);
1379 Polygon(hdc, Points, 4);
1380 SelectObject(hdc, hbsave);
1381 SelectObject(hdc, hpsave);
1382 DeleteObject (hp);
1383 DeleteObject (hb);
1386 /* Adjust rectangle if asked */
1387 if(uFlags & BF_ADJUST)
1389 *contentsRect = *rc;
1390 if(uFlags & BF_LEFT) contentsRect->left += add;
1391 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1392 if(uFlags & BF_TOP) contentsRect->top += add;
1393 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1396 /* Cleanup */
1397 SelectObject(hdc, SavePen);
1398 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1399 if(InnerI != -1) DeleteObject (InnerPen);
1400 if(OuterI != -1) DeleteObject (OuterPen);
1402 return retval;
1405 /***********************************************************************
1406 * draw_rect_edge
1408 * Same as DrawEdge invoked without BF_DIAGONAL
1410 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1411 const RECT* rc, UINT uType,
1412 UINT uFlags, LPRECT contentsRect)
1414 signed char LTInnerI, LTOuterI;
1415 signed char RBInnerI, RBOuterI;
1416 HPEN LTInnerPen, LTOuterPen;
1417 HPEN RBInnerPen, RBOuterPen;
1418 RECT InnerRect = *rc;
1419 POINT SavePoint;
1420 HPEN SavePen;
1421 int LBpenplus = 0;
1422 int LTpenplus = 0;
1423 int RTpenplus = 0;
1424 int RBpenplus = 0;
1425 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1426 || (uType & BDR_OUTER) == BDR_OUTER)
1427 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1429 /* Init some vars */
1430 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = (HPEN)GetStockObject(NULL_PEN);
1431 SavePen = (HPEN)SelectObject(hdc, LTInnerPen);
1433 /* Determine the colors of the edges */
1434 if(uFlags & BF_MONO)
1436 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1437 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1439 else if(uFlags & BF_FLAT)
1441 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1442 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1444 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1446 else if(uFlags & BF_SOFT)
1448 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1449 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1450 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1451 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1453 else
1455 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1456 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1457 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1458 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1461 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1462 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1463 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1464 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1466 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1467 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1468 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1469 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1471 MoveToEx(hdc, 0, 0, &SavePoint);
1473 /* Draw the outer edge */
1474 SelectObject(hdc, LTOuterPen);
1475 if(uFlags & BF_TOP)
1477 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1478 LineTo(hdc, InnerRect.right, InnerRect.top);
1480 if(uFlags & BF_LEFT)
1482 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1483 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1485 SelectObject(hdc, RBOuterPen);
1486 if(uFlags & BF_BOTTOM)
1488 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1489 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1491 if(uFlags & BF_RIGHT)
1493 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1494 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1497 /* Draw the inner edge */
1498 SelectObject(hdc, LTInnerPen);
1499 if(uFlags & BF_TOP)
1501 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1502 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1504 if(uFlags & BF_LEFT)
1506 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1507 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1509 SelectObject(hdc, RBInnerPen);
1510 if(uFlags & BF_BOTTOM)
1512 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1513 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1515 if(uFlags & BF_RIGHT)
1517 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1518 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1521 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1523 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1524 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1526 if(uFlags & BF_LEFT) InnerRect.left += add;
1527 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1528 if(uFlags & BF_TOP) InnerRect.top += add;
1529 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1531 if((uFlags & BF_MIDDLE) && retval)
1533 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1534 theme, part, state);
1535 FillRect(hdc, &InnerRect, br);
1536 DeleteObject (br);
1539 if(uFlags & BF_ADJUST)
1540 *contentsRect = InnerRect;
1543 /* Cleanup */
1544 SelectObject(hdc, SavePen);
1545 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1546 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1547 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1548 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1549 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1550 return retval;
1554 /***********************************************************************
1555 * DrawThemeEdge (UXTHEME.@)
1557 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1558 * difference is that it does not rely on the system colors alone, but
1559 * also allows color specification in the theme.
1561 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1562 int iStateId, const RECT *pDestRect, UINT uEdge,
1563 UINT uFlags, RECT *pContentRect)
1565 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1566 if(!hTheme)
1567 return E_HANDLE;
1569 if(uFlags & BF_DIAGONAL)
1570 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1571 uEdge, uFlags, pContentRect);
1572 else
1573 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1574 uEdge, uFlags, pContentRect);
1578 /***********************************************************************
1579 * DrawThemeIcon (UXTHEME.@)
1581 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1582 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1584 FIXME("%d %d: stub\n", iPartId, iStateId);
1585 if(!hTheme)
1586 return E_HANDLE;
1587 return ERROR_CALL_NOT_IMPLEMENTED;
1590 /***********************************************************************
1591 * DrawThemeText (UXTHEME.@)
1593 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1594 LPCWSTR pszText, int iCharCount, DWORD dwTextFlags,
1595 DWORD dwTextFlags2, const RECT *pRect)
1597 HRESULT hr;
1598 HFONT hFont = NULL;
1599 HGDIOBJ oldFont = NULL;
1600 LOGFONTW logfont;
1601 COLORREF textColor;
1602 COLORREF oldTextColor;
1603 int oldBkMode;
1604 RECT rt;
1606 TRACE("%d %d: stub\n", iPartId, iStateId);
1607 if(!hTheme)
1608 return E_HANDLE;
1610 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1611 if(SUCCEEDED(hr)) {
1612 hFont = CreateFontIndirectW(&logfont);
1613 if(!hFont)
1614 TRACE("Failed to create font\n");
1616 CopyRect(&rt, pRect);
1617 if(hFont)
1618 oldFont = SelectObject(hdc, hFont);
1620 if(dwTextFlags2 & DTT_GRAYED)
1621 textColor = GetSysColor(COLOR_GRAYTEXT);
1622 else {
1623 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1624 textColor = GetTextColor(hdc);
1626 oldTextColor = SetTextColor(hdc, textColor);
1627 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1628 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1629 SetBkMode(hdc, oldBkMode);
1630 SetTextColor(hdc, oldTextColor);
1632 if(hFont) {
1633 SelectObject(hdc, oldFont);
1634 DeleteObject(hFont);
1636 return S_OK;
1639 /***********************************************************************
1640 * GetThemeBackgroundContentRect (UXTHEME.@)
1642 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1643 int iStateId,
1644 const RECT *pBoundingRect,
1645 RECT *pContentRect)
1647 MARGINS margin;
1648 HRESULT hr;
1650 TRACE("(%d,%d)\n", iPartId, iStateId);
1651 if(!hTheme)
1652 return E_HANDLE;
1654 /* try content margins property... */
1655 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1656 if(SUCCEEDED(hr)) {
1657 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1658 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1659 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1660 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1661 } else {
1662 /* otherwise, try to determine content rect from the background type and props */
1663 int bgtype = BT_BORDERFILL;
1664 *pContentRect = *pBoundingRect;
1666 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1667 if(bgtype == BT_BORDERFILL) {
1668 int bordersize = 1;
1670 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1671 InflateRect(pContentRect, -bordersize, -bordersize);
1672 } else if ((bgtype == BT_IMAGEFILE)
1673 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1674 TMT_SIZINGMARGINS, NULL, &margin)))) {
1675 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1676 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1677 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1678 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1680 /* If nothing was found, leave unchanged */
1683 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pContentRect->left, pContentRect->top, pContentRect->right, pContentRect->bottom);
1685 return S_OK;
1688 /***********************************************************************
1689 * GetThemeBackgroundExtent (UXTHEME.@)
1691 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1692 int iStateId, const RECT *pContentRect,
1693 RECT *pExtentRect)
1695 MARGINS margin;
1696 HRESULT hr;
1698 TRACE("(%d,%d)\n", iPartId, iStateId);
1699 if(!hTheme)
1700 return E_HANDLE;
1702 /* try content margins property... */
1703 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1704 if(SUCCEEDED(hr)) {
1705 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1706 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1707 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1708 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1709 } else {
1710 /* otherwise, try to determine content rect from the background type and props */
1711 int bgtype = BT_BORDERFILL;
1712 *pExtentRect = *pContentRect;
1714 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1715 if(bgtype == BT_BORDERFILL) {
1716 int bordersize = 1;
1718 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1719 InflateRect(pExtentRect, bordersize, bordersize);
1720 } else if ((bgtype == BT_IMAGEFILE)
1721 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1722 TMT_SIZINGMARGINS, NULL, &margin)))) {
1723 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1724 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1725 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1726 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1728 /* If nothing was found, leave unchanged */
1731 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pExtentRect->left, pExtentRect->top, pExtentRect->right, pExtentRect->bottom);
1733 return S_OK;
1736 /***********************************************************************
1737 * GetThemeBackgroundRegion (UXTHEME.@)
1739 * Calculate the background region, taking into consideration transparent areas
1740 * of the background image.
1742 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1743 int iStateId, const RECT *pRect,
1744 HRGN *pRegion)
1746 HRESULT hr = S_OK;
1747 int bgtype = BT_BORDERFILL;
1749 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1750 if(!hTheme)
1751 return E_HANDLE;
1752 if(!pRect || !pRegion)
1753 return E_POINTER;
1755 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1756 if(bgtype == BT_IMAGEFILE) {
1757 FIXME("Images not handled yet\n");
1758 hr = ERROR_CALL_NOT_IMPLEMENTED;
1760 else if(bgtype == BT_BORDERFILL) {
1761 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1762 if(!*pRegion)
1763 hr = HRESULT_FROM_WIN32(GetLastError());
1765 else {
1766 FIXME("Unknown background type\n");
1767 /* This should never happen, and hence I don't know what to return */
1768 hr = E_FAIL;
1770 return hr;
1773 /* compute part size for "borderfill" backgrounds */
1774 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1775 int iStateId, THEMESIZE eSize, POINT* psz)
1777 HRESULT hr = S_OK;
1778 int bordersize = 1;
1780 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1781 &bordersize)))
1783 psz->x = psz->y = 2*bordersize;
1784 if (eSize != TS_MIN)
1786 psz->x++;
1787 psz->y++;
1790 return hr;
1793 /***********************************************************************
1794 * GetThemePartSize (UXTHEME.@)
1796 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1797 int iStateId, RECT *prc, THEMESIZE eSize,
1798 SIZE *psz)
1800 int bgtype = BT_BORDERFILL;
1801 HRESULT hr = S_OK;
1802 POINT size = {1, 1};
1804 if(!hTheme)
1805 return E_HANDLE;
1807 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1808 if (bgtype == BT_NONE)
1809 /* do nothing */;
1810 else if(bgtype == BT_IMAGEFILE)
1811 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1812 else if(bgtype == BT_BORDERFILL)
1813 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1814 else {
1815 FIXME("Unknown background type\n");
1816 /* This should never happen, and hence I don't know what to return */
1817 hr = E_FAIL;
1819 psz->cx = size.x;
1820 psz->cy = size.y;
1821 return hr;
1825 /***********************************************************************
1826 * GetThemeTextExtent (UXTHEME.@)
1828 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
1829 int iStateId, LPCWSTR pszText, int iCharCount,
1830 DWORD dwTextFlags, const RECT *pBoundingRect,
1831 RECT *pExtentRect)
1833 HRESULT hr;
1834 HFONT hFont = NULL;
1835 HGDIOBJ oldFont = NULL;
1836 LOGFONTW logfont;
1837 RECT rt = {0,0,0xFFFF,0xFFFF};
1839 TRACE("%d %d: stub\n", iPartId, iStateId);
1840 if(!hTheme)
1841 return E_HANDLE;
1843 if(pBoundingRect)
1844 CopyRect(&rt, pBoundingRect);
1846 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1847 if(SUCCEEDED(hr)) {
1848 hFont = CreateFontIndirectW(&logfont);
1849 if(!hFont)
1850 TRACE("Failed to create font\n");
1852 if(hFont)
1853 oldFont = SelectObject(hdc, hFont);
1855 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
1856 CopyRect(pExtentRect, &rt);
1858 if(hFont) {
1859 SelectObject(hdc, oldFont);
1860 DeleteObject(hFont);
1862 return S_OK;
1865 /***********************************************************************
1866 * GetThemeTextMetrics (UXTHEME.@)
1868 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
1869 int iStateId, TEXTMETRICW *ptm)
1871 HRESULT hr;
1872 HFONT hFont = NULL;
1873 HGDIOBJ oldFont = NULL;
1874 LOGFONTW logfont;
1876 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
1877 if(!hTheme)
1878 return E_HANDLE;
1880 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1881 if(SUCCEEDED(hr)) {
1882 hFont = CreateFontIndirectW(&logfont);
1883 if(!hFont)
1884 TRACE("Failed to create font\n");
1886 if(hFont)
1887 oldFont = SelectObject(hdc, hFont);
1889 if(!GetTextMetricsW(hdc, ptm))
1890 hr = HRESULT_FROM_WIN32(GetLastError());
1892 if(hFont) {
1893 SelectObject(hdc, oldFont);
1894 DeleteObject(hFont);
1896 return hr;
1899 /***********************************************************************
1900 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
1902 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
1903 int iStateId)
1905 int bgtype = BT_BORDERFILL;
1906 RECT rect = {0, 0, 0, 0};
1907 HBITMAP bmpSrc;
1908 RECT rcSrc;
1909 BOOL hasAlpha;
1910 INT transparent;
1911 COLORREF transparentcolor;
1913 TRACE("(%d,%d)\n", iPartId, iStateId);
1915 if(!hTheme)
1916 return FALSE;
1918 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1920 if (bgtype != BT_IMAGEFILE) return FALSE;
1922 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
1923 &bmpSrc, &rcSrc, &hasAlpha)))
1924 return FALSE;
1926 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
1927 &transparentcolor, FALSE);
1928 return (transparent != ALPHABLEND_NONE);