winex11: Allow wintab devices with less than 5 axes.
[wine/testsucceed.git] / dlls / uxtheme / draw.c
blob9c7d62b20b16b0fd4662be68eb85e2ec1861a0d2
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 int imagenum;
200 BITMAP bmp;
201 HBITMAP hBmp;
202 BOOL hasAlpha;
204 lstrcpynW(szPath, fileProp->lpValue,
205 min(fileProp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
206 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
207 if(!hBmp) continue;
209 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
210 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
212 imagenum = max (min (imagecount, iStateId), 1) - 1;
213 GetObjectW(hBmp, sizeof(bmp), &bmp);
214 if(imagelayout == IL_VERTICAL) {
215 reqsize.x = bmp.bmWidth;
216 reqsize.y = bmp.bmHeight/imagecount;
218 else {
219 reqsize.x = bmp.bmWidth/imagecount;
220 reqsize.y = bmp.bmHeight;
223 if(reqsize.x <= size.x && reqsize.y <= size.y) {
224 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
225 return fileProp;
228 /* If an image couldn't be selected, choose the smallest one */
229 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
231 return NULL;
234 /***********************************************************************
235 * UXTHEME_LoadImage
237 * Load image for part/state
239 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
240 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
242 int imagelayout = IL_HORIZONTAL;
243 int imagecount = 1;
244 int imagenum;
245 BITMAP bmp;
246 WCHAR szPath[MAX_PATH];
247 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
248 if(!tp) {
249 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
250 return E_PROP_ID_UNSUPPORTED;
252 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
253 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
254 if(!*hBmp) {
255 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
256 return HRESULT_FROM_WIN32(GetLastError());
259 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
260 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
262 imagenum = max (min (imagecount, iStateId), 1) - 1;
263 GetObjectW(*hBmp, sizeof(bmp), &bmp);
264 if(imagelayout == IL_VERTICAL) {
265 int height = bmp.bmHeight/imagecount;
266 bmpRect->left = 0;
267 bmpRect->right = bmp.bmWidth;
268 bmpRect->top = imagenum * height;
269 bmpRect->bottom = bmpRect->top + height;
271 else {
272 int width = bmp.bmWidth/imagecount;
273 bmpRect->left = imagenum * width;
274 bmpRect->right = bmpRect->left + width;
275 bmpRect->top = 0;
276 bmpRect->bottom = bmp.bmHeight;
278 return S_OK;
281 /***********************************************************************
282 * UXTHEME_StretchBlt
284 * Pseudo TransparentBlt/StretchBlt
286 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
287 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
288 INT transparent, COLORREF transcolor)
290 static const BLENDFUNCTION blendFunc =
292 AC_SRC_OVER, /* BlendOp */
293 0, /* BlendFlag */
294 255, /* SourceConstantAlpha */
295 AC_SRC_ALPHA /* AlphaFormat */
297 if (transparent == ALPHABLEND_BINARY) {
298 /* Ensure we don't pass any negative values to TransparentBlt */
299 return TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
300 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
301 transcolor);
303 if ((transparent == ALPHABLEND_NONE) ||
304 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
305 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
306 blendFunc))
308 return StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
309 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
310 SRCCOPY);
312 return TRUE;
315 /***********************************************************************
316 * UXTHEME_Blt
318 * Simplify sending same width/height for both source and dest
320 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
321 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
322 INT transparent, COLORREF transcolor)
324 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
325 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
326 transparent, transcolor);
329 /***********************************************************************
330 * UXTHEME_SizedBlt
332 * Stretches or tiles, depending on sizingtype.
334 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
335 int nWidthDst, int nHeightDst,
336 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
337 int nWidthSrc, int nHeightSrc,
338 int sizingtype,
339 INT transparent, COLORREF transcolor)
341 if (sizingtype == ST_TILE)
343 HDC hdcTemp;
344 BOOL result = FALSE;
346 if (!nWidthSrc || !nHeightSrc) return TRUE;
348 /* For destination width/height less than or equal to source
349 width/height, do not bother with memory bitmap optimization */
350 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
352 int bltWidth = min (nWidthDst, nWidthSrc);
353 int bltHeight = min (nHeightDst, nHeightSrc);
355 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
356 hdcSrc, nXOriginSrc, nYOriginSrc,
357 transparent, transcolor);
360 /* Create a DC with a bitmap consisting of a tiling of the source
361 bitmap, with standard GDI functions. This is faster than an
362 iteration with UXTHEME_Blt(). */
363 hdcTemp = CreateCompatibleDC(hdcSrc);
364 if (hdcTemp != 0)
366 HBITMAP bitmapTemp;
367 HBITMAP bitmapOrig;
368 int nWidthTemp, nHeightTemp;
369 int xOfs, xRemaining;
370 int yOfs, yRemaining;
371 int growSize;
373 /* Calculate temp dimensions of integer multiples of source dimensions */
374 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
375 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
376 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
377 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
379 /* Initial copy of bitmap */
380 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
382 /* Extend bitmap in the X direction. Growth of width is exponential */
383 xOfs = nWidthSrc;
384 xRemaining = nWidthTemp - nWidthSrc;
385 growSize = nWidthSrc;
386 while (xRemaining > 0)
388 growSize = min(growSize, xRemaining);
389 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
390 xOfs += growSize;
391 xRemaining -= growSize;
392 growSize *= 2;
395 /* Extend bitmap in the Y direction. Growth of height is exponential */
396 yOfs = nHeightSrc;
397 yRemaining = nHeightTemp - nHeightSrc;
398 growSize = nHeightSrc;
399 while (yRemaining > 0)
401 growSize = min(growSize, yRemaining);
402 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
403 yOfs += growSize;
404 yRemaining -= growSize;
405 growSize *= 2;
408 /* Use temporary hdc for source */
409 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
410 hdcTemp, 0, 0,
411 transparent, transcolor);
413 SelectObject(hdcTemp, bitmapOrig);
414 DeleteObject(bitmapTemp);
416 DeleteDC(hdcTemp);
417 return result;
419 else
421 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
422 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
423 transparent, transcolor);
427 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
428 * depend on whether the image has full alpha or whether it is
429 * color-transparent or just opaque. */
430 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
431 BOOL hasImageAlpha, INT* transparent,
432 COLORREF* transparentcolor, BOOL glyph)
434 if (hasImageAlpha)
436 *transparent = ALPHABLEND_FULL;
437 *transparentcolor = RGB (255, 0, 255);
439 else
441 BOOL trans = FALSE;
442 GetThemeBool(hTheme, iPartId, iStateId,
443 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
444 if(trans) {
445 *transparent = ALPHABLEND_BINARY;
446 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
447 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
448 transparentcolor))) {
449 /* If image is transparent, but no color was specified, use magenta */
450 *transparentcolor = RGB(255, 0, 255);
453 else
454 *transparent = ALPHABLEND_NONE;
458 /***********************************************************************
459 * UXTHEME_DrawImageGlyph
461 * Draw an imagefile glyph
463 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
464 int iStateId, RECT *pRect,
465 const DTBGOPTS *pOptions)
467 HRESULT hr;
468 HBITMAP bmpSrc = NULL;
469 HDC hdcSrc = NULL;
470 HGDIOBJ oldSrc = NULL;
471 RECT rcSrc;
472 INT transparent = FALSE;
473 COLORREF transparentcolor;
474 int valign = VA_CENTER;
475 int halign = HA_CENTER;
476 POINT dstSize;
477 POINT srcSize;
478 POINT topleft;
479 BOOL hasAlpha;
481 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
482 &bmpSrc, &rcSrc, &hasAlpha);
483 if(FAILED(hr)) return hr;
484 hdcSrc = CreateCompatibleDC(hdc);
485 if(!hdcSrc) {
486 hr = HRESULT_FROM_WIN32(GetLastError());
487 return hr;
489 oldSrc = SelectObject(hdcSrc, bmpSrc);
491 dstSize.x = pRect->right-pRect->left;
492 dstSize.y = pRect->bottom-pRect->top;
493 srcSize.x = rcSrc.right-rcSrc.left;
494 srcSize.y = rcSrc.bottom-rcSrc.top;
496 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
497 &transparentcolor, TRUE);
498 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
499 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
501 topleft.x = pRect->left;
502 topleft.y = pRect->top;
503 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
504 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
505 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
506 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
508 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
509 hdcSrc, rcSrc.left, rcSrc.top,
510 transparent, transparentcolor)) {
511 hr = HRESULT_FROM_WIN32(GetLastError());
514 SelectObject(hdcSrc, oldSrc);
515 DeleteDC(hdcSrc);
516 return hr;
519 /***********************************************************************
520 * UXTHEME_DrawImageGlyph
522 * Draw glyph on top of background, if appropriate
524 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
525 int iStateId, RECT *pRect,
526 const DTBGOPTS *pOptions)
528 int glyphtype = GT_NONE;
530 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
532 if(glyphtype == GT_IMAGEGLYPH) {
533 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
535 else if(glyphtype == GT_FONTGLYPH) {
536 /* I don't know what a font glyph is, I've never seen it used in any themes */
537 FIXME("Font glyph\n");
539 return S_OK;
542 /***********************************************************************
543 * get_image_part_size
545 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
547 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
548 int iStateId, RECT *prc, THEMESIZE eSize,
549 POINT *psz)
551 HRESULT hr = S_OK;
552 HBITMAP bmpSrc;
553 RECT rcSrc;
554 BOOL hasAlpha;
556 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
557 &bmpSrc, &rcSrc, &hasAlpha);
558 if (FAILED(hr)) return hr;
560 switch (eSize)
562 case TS_DRAW:
563 if (prc != NULL)
565 RECT rcDst;
566 POINT dstSize;
567 POINT srcSize;
568 int sizingtype = ST_STRETCH;
569 BOOL uniformsizing = FALSE;
571 CopyRect(&rcDst, prc);
573 dstSize.x = rcDst.right-rcDst.left;
574 dstSize.y = rcDst.bottom-rcDst.top;
575 srcSize.x = rcSrc.right-rcSrc.left;
576 srcSize.y = rcSrc.bottom-rcSrc.top;
578 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
579 if(uniformsizing) {
580 /* Scale height and width equally */
581 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
583 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
584 rcDst.bottom = rcDst.top + dstSize.y;
586 else
588 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
589 rcDst.right = rcDst.left + dstSize.x;
593 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
594 if(sizingtype == ST_TRUESIZE) {
595 int truesizestretchmark = 100;
597 if(dstSize.x < 0 || dstSize.y < 0) {
598 BOOL mirrorimage = TRUE;
599 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
600 if(mirrorimage) {
601 if(dstSize.x < 0) {
602 rcDst.left += dstSize.x;
603 rcDst.right += dstSize.x;
605 if(dstSize.y < 0) {
606 rcDst.top += dstSize.y;
607 rcDst.bottom += dstSize.y;
611 /* Whatever TrueSizeStretchMark does - it does not seem to
612 * be what's outlined below. It appears as if native
613 * uxtheme always stretches if dest is smaller than source
614 * (ie as if TrueSizeStretchMark==100 with the code below) */
615 #if 0
616 /* Only stretch when target exceeds source by truesizestretchmark percent */
617 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
618 #endif
619 if(dstSize.x < 0 || dstSize.y < 0 ||
620 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
621 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
622 memcpy (psz, &dstSize, sizeof (SIZE));
624 else {
625 memcpy (psz, &srcSize, sizeof (SIZE));
628 else
630 psz->x = abs(dstSize.x);
631 psz->y = abs(dstSize.y);
633 break;
635 /* else fall through */
636 case TS_MIN:
637 /* FIXME: couldn't figure how native uxtheme computes min size */
638 case TS_TRUE:
639 psz->x = rcSrc.right - rcSrc.left;
640 psz->y = rcSrc.bottom - rcSrc.top;
641 break;
643 return hr;
646 /***********************************************************************
647 * UXTHEME_DrawImageBackground
649 * Draw an imagefile background
651 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
652 int iStateId, RECT *pRect,
653 const DTBGOPTS *pOptions)
655 HRESULT hr = S_OK;
656 HBITMAP bmpSrc;
657 HGDIOBJ oldSrc;
658 HDC hdcSrc;
659 RECT rcSrc;
660 RECT rcDst;
661 POINT dstSize;
662 POINT srcSize;
663 POINT drawSize;
664 int sizingtype = ST_STRETCH;
665 INT transparent;
666 COLORREF transparentcolor = 0;
667 BOOL hasAlpha;
669 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
670 &bmpSrc, &rcSrc, &hasAlpha);
671 if(FAILED(hr)) return hr;
672 hdcSrc = CreateCompatibleDC(hdc);
673 if(!hdcSrc) {
674 hr = HRESULT_FROM_WIN32(GetLastError());
675 return hr;
677 oldSrc = SelectObject(hdcSrc, bmpSrc);
679 CopyRect(&rcDst, pRect);
681 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
682 &transparentcolor, FALSE);
684 dstSize.x = rcDst.right-rcDst.left;
685 dstSize.y = rcDst.bottom-rcDst.top;
686 srcSize.x = rcSrc.right-rcSrc.left;
687 srcSize.y = rcSrc.bottom-rcSrc.top;
689 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
690 if(sizingtype == ST_TRUESIZE) {
691 int valign = VA_CENTER, halign = HA_CENTER;
693 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
694 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
695 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
697 if (halign == HA_CENTER)
698 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
699 else if (halign == HA_RIGHT)
700 rcDst.left = rcDst.right - drawSize.x;
701 if (valign == VA_CENTER)
702 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
703 else if (valign == VA_BOTTOM)
704 rcDst.top = rcDst.bottom - drawSize.y;
705 rcDst.right = rcDst.left + drawSize.x;
706 rcDst.bottom = rcDst.top + drawSize.y;
707 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
708 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
709 transparent, transparentcolor))
710 hr = HRESULT_FROM_WIN32(GetLastError());
712 else {
713 HDC hdcDst = NULL;
714 MARGINS sm;
715 POINT org;
717 dstSize.x = abs(dstSize.x);
718 dstSize.y = abs(dstSize.y);
720 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
722 hdcDst = hdc;
723 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
725 /* Upper left corner */
726 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
727 hdcSrc, rcSrc.left, rcSrc.top,
728 transparent, transparentcolor)) {
729 hr = HRESULT_FROM_WIN32(GetLastError());
730 goto draw_error;
732 /* Upper right corner */
733 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
734 sm.cxRightWidth, sm.cyTopHeight,
735 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
736 transparent, transparentcolor)) {
737 hr = HRESULT_FROM_WIN32(GetLastError());
738 goto draw_error;
740 /* Lower left corner */
741 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
742 sm.cxLeftWidth, sm.cyBottomHeight,
743 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
744 transparent, transparentcolor)) {
745 hr = HRESULT_FROM_WIN32(GetLastError());
746 goto draw_error;
748 /* Lower right corner */
749 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
750 sm.cxRightWidth, sm.cyBottomHeight,
751 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
752 transparent, transparentcolor)) {
753 hr = HRESULT_FROM_WIN32(GetLastError());
754 goto draw_error;
757 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
758 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
759 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
760 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
761 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
763 if(destCenterWidth > 0) {
764 /* Center top */
765 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
766 destCenterWidth, sm.cyTopHeight,
767 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
768 srcCenterWidth, sm.cyTopHeight,
769 sizingtype, transparent, transparentcolor)) {
770 hr = HRESULT_FROM_WIN32(GetLastError());
771 goto draw_error;
773 /* Center bottom */
774 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
775 destCenterWidth, sm.cyBottomHeight,
776 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
777 srcCenterWidth, sm.cyBottomHeight,
778 sizingtype, transparent, transparentcolor)) {
779 hr = HRESULT_FROM_WIN32(GetLastError());
780 goto draw_error;
783 if(destCenterHeight > 0) {
784 /* Left center */
785 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
786 sm.cxLeftWidth, destCenterHeight,
787 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
788 sm.cxLeftWidth, srcCenterHeight,
789 sizingtype,
790 transparent, transparentcolor)) {
791 hr = HRESULT_FROM_WIN32(GetLastError());
792 goto draw_error;
794 /* Right center */
795 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
796 sm.cxRightWidth, destCenterHeight,
797 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
798 sm.cxRightWidth, srcCenterHeight,
799 sizingtype, transparent, transparentcolor)) {
800 hr = HRESULT_FROM_WIN32(GetLastError());
801 goto draw_error;
804 if(destCenterHeight > 0 && destCenterWidth > 0) {
805 BOOL borderonly = FALSE;
806 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
807 if(!borderonly) {
808 /* Center */
809 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
810 destCenterWidth, destCenterHeight,
811 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
812 srcCenterWidth, srcCenterHeight,
813 sizingtype, transparent, transparentcolor)) {
814 hr = HRESULT_FROM_WIN32(GetLastError());
815 goto draw_error;
821 draw_error:
822 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
824 SelectObject(hdcSrc, oldSrc);
825 DeleteDC(hdcSrc);
826 CopyRect(pRect, &rcDst);
827 return hr;
830 /***********************************************************************
831 * UXTHEME_DrawBorderRectangle
833 * Draw the bounding rectangle for a borderfill background
835 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
836 int iStateId, RECT *pRect,
837 const DTBGOPTS *pOptions)
839 HRESULT hr = S_OK;
840 HPEN hPen;
841 HGDIOBJ oldPen;
842 COLORREF bordercolor = RGB(0,0,0);
843 int bordersize = 1;
845 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
846 if(bordersize > 0) {
847 POINT ptCorners[5];
848 ptCorners[0].x = pRect->left;
849 ptCorners[0].y = pRect->top;
850 ptCorners[1].x = pRect->right-1;
851 ptCorners[1].y = pRect->top;
852 ptCorners[2].x = pRect->right-1;
853 ptCorners[2].y = pRect->bottom-1;
854 ptCorners[3].x = pRect->left;
855 ptCorners[3].y = pRect->bottom-1;
856 ptCorners[4].x = pRect->left;
857 ptCorners[4].y = pRect->top;
859 InflateRect(pRect, -bordersize, -bordersize);
860 if(pOptions->dwFlags & DTBG_OMITBORDER)
861 return S_OK;
862 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
863 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
864 if(!hPen)
865 return HRESULT_FROM_WIN32(GetLastError());
866 oldPen = SelectObject(hdc, hPen);
868 if(!Polyline(hdc, ptCorners, 5))
869 hr = HRESULT_FROM_WIN32(GetLastError());
871 SelectObject(hdc, oldPen);
872 DeleteObject(hPen);
874 return hr;
877 /***********************************************************************
878 * UXTHEME_DrawBackgroundFill
880 * Fill a borderfill background rectangle
882 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
883 int iStateId, RECT *pRect,
884 const DTBGOPTS *pOptions)
886 HRESULT hr = S_OK;
887 int filltype = FT_SOLID;
889 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
891 if(pOptions->dwFlags & DTBG_OMITCONTENT)
892 return S_OK;
894 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
896 if(filltype == FT_SOLID) {
897 HBRUSH hBrush;
898 COLORREF fillcolor = RGB(255,255,255);
900 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
901 hBrush = CreateSolidBrush(fillcolor);
902 if(!FillRect(hdc, pRect, hBrush))
903 hr = HRESULT_FROM_WIN32(GetLastError());
904 DeleteObject(hBrush);
906 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
907 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
908 the gradient ratios (no idea how those work)
909 Few themes use this, and the ones I've seen only use 2 colors with
910 a gradient ratio of 0 and 255 respectively
913 COLORREF gradient1 = RGB(0,0,0);
914 COLORREF gradient2 = RGB(255,255,255);
915 TRIVERTEX vert[2];
916 GRADIENT_RECT gRect;
918 FIXME("Gradient implementation not complete\n");
920 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
921 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
923 vert[0].x = pRect->left;
924 vert[0].y = pRect->top;
925 vert[0].Red = GetRValue(gradient1) << 8;
926 vert[0].Green = GetGValue(gradient1) << 8;
927 vert[0].Blue = GetBValue(gradient1) << 8;
928 vert[0].Alpha = 0x0000;
930 vert[1].x = pRect->right;
931 vert[1].y = pRect->bottom;
932 vert[1].Red = GetRValue(gradient2) << 8;
933 vert[1].Green = GetGValue(gradient2) << 8;
934 vert[1].Blue = GetBValue(gradient2) << 8;
935 vert[1].Alpha = 0x0000;
937 gRect.UpperLeft = 0;
938 gRect.LowerRight = 1;
939 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
941 else if(filltype == FT_RADIALGRADIENT) {
942 /* I've never seen this used in a theme */
943 FIXME("Radial gradient\n");
945 else if(filltype == FT_TILEIMAGE) {
946 /* I've never seen this used in a theme */
947 FIXME("Tile image\n");
949 return hr;
952 /***********************************************************************
953 * UXTHEME_DrawBorderBackground
955 * Draw an imagefile background
957 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
958 int iStateId, const RECT *pRect,
959 const DTBGOPTS *pOptions)
961 HRESULT hr;
962 RECT rt;
964 CopyRect(&rt, pRect);
966 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
967 if(FAILED(hr))
968 return hr;
969 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
972 /***********************************************************************
973 * DrawThemeBackgroundEx (UXTHEME.@)
975 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
976 int iStateId, const RECT *pRect,
977 const DTBGOPTS *pOptions)
979 HRESULT hr;
980 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
981 const DTBGOPTS *opts;
982 HRGN clip = NULL;
983 int hasClip = -1;
984 int bgtype = BT_BORDERFILL;
985 RECT rt;
987 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
988 if(!hTheme)
989 return E_HANDLE;
991 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
992 if (bgtype == BT_NONE) return S_OK;
994 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
995 opts = pOptions;
996 if(!opts) opts = &defaultOpts;
998 if(opts->dwFlags & DTBG_CLIPRECT) {
999 clip = CreateRectRgn(0,0,1,1);
1000 hasClip = GetClipRgn(hdc, clip);
1001 if(hasClip == -1)
1002 TRACE("Failed to get original clipping region\n");
1003 else
1004 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1006 CopyRect(&rt, pRect);
1008 if(bgtype == BT_IMAGEFILE)
1009 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1010 else if(bgtype == BT_BORDERFILL)
1011 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1012 else {
1013 FIXME("Unknown background type\n");
1014 /* This should never happen, and hence I don't know what to return */
1015 hr = E_FAIL;
1017 if(SUCCEEDED(hr))
1018 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1019 if(opts->dwFlags & DTBG_CLIPRECT) {
1020 if(hasClip == 0)
1021 SelectClipRgn(hdc, NULL);
1022 else if(hasClip == 1)
1023 SelectClipRgn(hdc, clip);
1024 DeleteObject(clip);
1026 return hr;
1030 * DrawThemeEdge() implementation
1032 * Since it basically is DrawEdge() with different colors, I copied its code
1033 * from user32's uitools.c.
1036 enum
1038 EDGE_LIGHT,
1039 EDGE_HIGHLIGHT,
1040 EDGE_SHADOW,
1041 EDGE_DARKSHADOW,
1042 EDGE_FILL,
1044 EDGE_WINDOW,
1045 EDGE_WINDOWFRAME,
1047 EDGE_NUMCOLORS
1050 static const struct
1052 int themeProp;
1053 int sysColor;
1054 } EdgeColorMap[EDGE_NUMCOLORS] = {
1055 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1056 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1057 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1058 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1059 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1060 {-1, COLOR_WINDOW},
1061 {-1, COLOR_WINDOWFRAME}
1064 static const signed char LTInnerNormal[] = {
1065 -1, -1, -1, -1,
1066 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1067 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1068 -1, -1, -1, -1
1071 static const signed char LTOuterNormal[] = {
1072 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1073 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1074 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1075 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1078 static const signed char RBInnerNormal[] = {
1079 -1, -1, -1, -1,
1080 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1081 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1082 -1, -1, -1, -1
1085 static const signed char RBOuterNormal[] = {
1086 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1087 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1088 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1089 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1092 static const signed char LTInnerSoft[] = {
1093 -1, -1, -1, -1,
1094 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1095 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1096 -1, -1, -1, -1
1099 static const signed char LTOuterSoft[] = {
1100 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1101 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1102 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1103 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1106 #define RBInnerSoft RBInnerNormal /* These are the same */
1107 #define RBOuterSoft RBOuterNormal
1109 static const signed char LTRBOuterMono[] = {
1110 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1111 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1112 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1113 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1116 static const signed char LTRBInnerMono[] = {
1117 -1, -1, -1, -1,
1118 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1119 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1120 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1123 static const signed char LTRBOuterFlat[] = {
1124 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1125 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1126 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1127 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1130 static const signed char LTRBInnerFlat[] = {
1131 -1, -1, -1, -1,
1132 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1133 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1134 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1137 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1139 COLORREF col;
1140 if ((EdgeColorMap[edgeType].themeProp == -1)
1141 || FAILED (GetThemeColor (theme, part, state,
1142 EdgeColorMap[edgeType].themeProp, &col)))
1143 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1144 return col;
1147 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1149 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1152 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1154 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1157 /***********************************************************************
1158 * draw_diag_edge
1160 * Same as DrawEdge invoked with BF_DIAGONAL
1162 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1163 const RECT* rc, UINT uType,
1164 UINT uFlags, LPRECT contentsRect)
1166 POINT Points[4];
1167 signed char InnerI, OuterI;
1168 HPEN InnerPen, OuterPen;
1169 POINT SavePoint;
1170 HPEN SavePen;
1171 int spx, spy;
1172 int epx, epy;
1173 int Width = rc->right - rc->left;
1174 int Height= rc->bottom - rc->top;
1175 int SmallDiam = Width > Height ? Height : Width;
1176 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1177 || (uType & BDR_OUTER) == BDR_OUTER)
1178 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1179 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1180 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1182 /* Init some vars */
1183 OuterPen = InnerPen = (HPEN)GetStockObject(NULL_PEN);
1184 SavePen = (HPEN)SelectObject(hdc, InnerPen);
1185 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1187 /* Determine the colors of the edges */
1188 if(uFlags & BF_MONO)
1190 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1191 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1193 else if(uFlags & BF_FLAT)
1195 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1196 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1198 else if(uFlags & BF_SOFT)
1200 if(uFlags & BF_BOTTOM)
1202 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1203 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1205 else
1207 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1208 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1211 else
1213 if(uFlags & BF_BOTTOM)
1215 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1216 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1218 else
1220 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1221 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1225 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1226 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1228 MoveToEx(hdc, 0, 0, &SavePoint);
1230 /* Don't ask me why, but this is what is visible... */
1231 /* This must be possible to do much simpler, but I fail to */
1232 /* see the logic in the MS implementation (sigh...). */
1233 /* So, this might look a bit brute force here (and it is), but */
1234 /* it gets the job done;) */
1236 switch(uFlags & BF_RECT)
1238 case 0:
1239 case BF_LEFT:
1240 case BF_BOTTOM:
1241 case BF_BOTTOMLEFT:
1242 /* Left bottom endpoint */
1243 epx = rc->left-1;
1244 spx = epx + SmallDiam;
1245 epy = rc->bottom;
1246 spy = epy - SmallDiam;
1247 break;
1249 case BF_TOPLEFT:
1250 case BF_BOTTOMRIGHT:
1251 /* Left top endpoint */
1252 epx = rc->left-1;
1253 spx = epx + SmallDiam;
1254 epy = rc->top-1;
1255 spy = epy + SmallDiam;
1256 break;
1258 case BF_TOP:
1259 case BF_RIGHT:
1260 case BF_TOPRIGHT:
1261 case BF_RIGHT|BF_LEFT:
1262 case BF_RIGHT|BF_LEFT|BF_TOP:
1263 case BF_BOTTOM|BF_TOP:
1264 case BF_BOTTOM|BF_TOP|BF_LEFT:
1265 case BF_BOTTOMRIGHT|BF_LEFT:
1266 case BF_BOTTOMRIGHT|BF_TOP:
1267 case BF_RECT:
1268 /* Right top endpoint */
1269 spx = rc->left;
1270 epx = spx + SmallDiam;
1271 spy = rc->bottom-1;
1272 epy = spy - SmallDiam;
1273 break;
1276 MoveToEx(hdc, spx, spy, NULL);
1277 SelectObject(hdc, OuterPen);
1278 LineTo(hdc, epx, epy);
1280 SelectObject(hdc, InnerPen);
1282 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1284 case BF_DIAGONAL_ENDBOTTOMLEFT:
1285 case (BF_DIAGONAL|BF_BOTTOM):
1286 case BF_DIAGONAL:
1287 case (BF_DIAGONAL|BF_LEFT):
1288 MoveToEx(hdc, spx-1, spy, NULL);
1289 LineTo(hdc, epx, epy-1);
1290 Points[0].x = spx-add;
1291 Points[0].y = spy;
1292 Points[1].x = rc->left;
1293 Points[1].y = rc->top;
1294 Points[2].x = epx+1;
1295 Points[2].y = epy-1-add;
1296 Points[3] = Points[2];
1297 break;
1299 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1300 MoveToEx(hdc, spx-1, spy, NULL);
1301 LineTo(hdc, epx, epy+1);
1302 Points[0].x = spx-add;
1303 Points[0].y = spy;
1304 Points[1].x = rc->left;
1305 Points[1].y = rc->bottom-1;
1306 Points[2].x = epx+1;
1307 Points[2].y = epy+1+add;
1308 Points[3] = Points[2];
1309 break;
1311 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1312 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1313 case BF_DIAGONAL_ENDTOPRIGHT:
1314 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1315 MoveToEx(hdc, spx+1, spy, NULL);
1316 LineTo(hdc, epx, epy+1);
1317 Points[0].x = epx-1;
1318 Points[0].y = epy+1+add;
1319 Points[1].x = rc->right-1;
1320 Points[1].y = rc->top+add;
1321 Points[2].x = rc->right-1;
1322 Points[2].y = rc->bottom-1;
1323 Points[3].x = spx+add;
1324 Points[3].y = spy;
1325 break;
1327 case BF_DIAGONAL_ENDTOPLEFT:
1328 MoveToEx(hdc, spx, spy-1, NULL);
1329 LineTo(hdc, epx+1, epy);
1330 Points[0].x = epx+1+add;
1331 Points[0].y = epy+1;
1332 Points[1].x = rc->right-1;
1333 Points[1].y = rc->top;
1334 Points[2].x = rc->right-1;
1335 Points[2].y = rc->bottom-1-add;
1336 Points[3].x = spx;
1337 Points[3].y = spy-add;
1338 break;
1340 case (BF_DIAGONAL|BF_TOP):
1341 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1342 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1343 MoveToEx(hdc, spx+1, spy-1, NULL);
1344 LineTo(hdc, epx, epy);
1345 Points[0].x = epx-1;
1346 Points[0].y = epy+1;
1347 Points[1].x = rc->right-1;
1348 Points[1].y = rc->top;
1349 Points[2].x = rc->right-1;
1350 Points[2].y = rc->bottom-1-add;
1351 Points[3].x = spx+add;
1352 Points[3].y = spy-add;
1353 break;
1355 case (BF_DIAGONAL|BF_RIGHT):
1356 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1357 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1358 MoveToEx(hdc, spx, spy, NULL);
1359 LineTo(hdc, epx-1, epy+1);
1360 Points[0].x = spx;
1361 Points[0].y = spy;
1362 Points[1].x = rc->left;
1363 Points[1].y = rc->top+add;
1364 Points[2].x = epx-1-add;
1365 Points[2].y = epy+1+add;
1366 Points[3] = Points[2];
1367 break;
1370 /* Fill the interior if asked */
1371 if((uFlags & BF_MIDDLE) && retval)
1373 HBRUSH hbsave;
1374 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1375 theme, part, state);
1376 HPEN hpsave;
1377 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1378 theme, part, state);
1379 hbsave = (HBRUSH)SelectObject(hdc, hb);
1380 hpsave = (HPEN)SelectObject(hdc, hp);
1381 Polygon(hdc, Points, 4);
1382 SelectObject(hdc, hbsave);
1383 SelectObject(hdc, hpsave);
1384 DeleteObject (hp);
1385 DeleteObject (hb);
1388 /* Adjust rectangle if asked */
1389 if(uFlags & BF_ADJUST)
1391 *contentsRect = *rc;
1392 if(uFlags & BF_LEFT) contentsRect->left += add;
1393 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1394 if(uFlags & BF_TOP) contentsRect->top += add;
1395 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1398 /* Cleanup */
1399 SelectObject(hdc, SavePen);
1400 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1401 if(InnerI != -1) DeleteObject (InnerPen);
1402 if(OuterI != -1) DeleteObject (OuterPen);
1404 return retval;
1407 /***********************************************************************
1408 * draw_rect_edge
1410 * Same as DrawEdge invoked without BF_DIAGONAL
1412 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1413 const RECT* rc, UINT uType,
1414 UINT uFlags, LPRECT contentsRect)
1416 signed char LTInnerI, LTOuterI;
1417 signed char RBInnerI, RBOuterI;
1418 HPEN LTInnerPen, LTOuterPen;
1419 HPEN RBInnerPen, RBOuterPen;
1420 RECT InnerRect = *rc;
1421 POINT SavePoint;
1422 HPEN SavePen;
1423 int LBpenplus = 0;
1424 int LTpenplus = 0;
1425 int RTpenplus = 0;
1426 int RBpenplus = 0;
1427 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1428 || (uType & BDR_OUTER) == BDR_OUTER)
1429 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1431 /* Init some vars */
1432 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = (HPEN)GetStockObject(NULL_PEN);
1433 SavePen = (HPEN)SelectObject(hdc, LTInnerPen);
1435 /* Determine the colors of the edges */
1436 if(uFlags & BF_MONO)
1438 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1439 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1441 else if(uFlags & BF_FLAT)
1443 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1444 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1446 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1448 else if(uFlags & BF_SOFT)
1450 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1451 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1452 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1453 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1455 else
1457 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1458 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1459 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1460 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1463 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1464 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1465 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1466 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1468 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1469 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1470 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1471 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1473 MoveToEx(hdc, 0, 0, &SavePoint);
1475 /* Draw the outer edge */
1476 SelectObject(hdc, LTOuterPen);
1477 if(uFlags & BF_TOP)
1479 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1480 LineTo(hdc, InnerRect.right, InnerRect.top);
1482 if(uFlags & BF_LEFT)
1484 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1485 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1487 SelectObject(hdc, RBOuterPen);
1488 if(uFlags & BF_BOTTOM)
1490 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1491 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1493 if(uFlags & BF_RIGHT)
1495 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1496 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1499 /* Draw the inner edge */
1500 SelectObject(hdc, LTInnerPen);
1501 if(uFlags & BF_TOP)
1503 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1504 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1506 if(uFlags & BF_LEFT)
1508 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1509 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1511 SelectObject(hdc, RBInnerPen);
1512 if(uFlags & BF_BOTTOM)
1514 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1515 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1517 if(uFlags & BF_RIGHT)
1519 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1520 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1523 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1525 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1526 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1528 if(uFlags & BF_LEFT) InnerRect.left += add;
1529 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1530 if(uFlags & BF_TOP) InnerRect.top += add;
1531 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1533 if((uFlags & BF_MIDDLE) && retval)
1535 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1536 theme, part, state);
1537 FillRect(hdc, &InnerRect, br);
1538 DeleteObject (br);
1541 if(uFlags & BF_ADJUST)
1542 *contentsRect = InnerRect;
1545 /* Cleanup */
1546 SelectObject(hdc, SavePen);
1547 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1548 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1549 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1550 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1551 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1552 return retval;
1556 /***********************************************************************
1557 * DrawThemeEdge (UXTHEME.@)
1559 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1560 * difference is that it does not rely on the system colors alone, but
1561 * also allows color specification in the theme.
1563 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1564 int iStateId, const RECT *pDestRect, UINT uEdge,
1565 UINT uFlags, RECT *pContentRect)
1567 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1568 if(!hTheme)
1569 return E_HANDLE;
1571 if(uFlags & BF_DIAGONAL)
1572 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1573 uEdge, uFlags, pContentRect);
1574 else
1575 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1576 uEdge, uFlags, pContentRect);
1580 /***********************************************************************
1581 * DrawThemeIcon (UXTHEME.@)
1583 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1584 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1586 FIXME("%d %d: stub\n", iPartId, iStateId);
1587 if(!hTheme)
1588 return E_HANDLE;
1589 return ERROR_CALL_NOT_IMPLEMENTED;
1592 /***********************************************************************
1593 * DrawThemeText (UXTHEME.@)
1595 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1596 LPCWSTR pszText, int iCharCount, DWORD dwTextFlags,
1597 DWORD dwTextFlags2, const RECT *pRect)
1599 HRESULT hr;
1600 HFONT hFont = NULL;
1601 HGDIOBJ oldFont = NULL;
1602 LOGFONTW logfont;
1603 COLORREF textColor;
1604 COLORREF oldTextColor;
1605 int oldBkMode;
1606 RECT rt;
1608 TRACE("%d %d: stub\n", iPartId, iStateId);
1609 if(!hTheme)
1610 return E_HANDLE;
1612 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1613 if(SUCCEEDED(hr)) {
1614 hFont = CreateFontIndirectW(&logfont);
1615 if(!hFont)
1616 TRACE("Failed to create font\n");
1618 CopyRect(&rt, pRect);
1619 if(hFont)
1620 oldFont = SelectObject(hdc, hFont);
1622 if(dwTextFlags2 & DTT_GRAYED)
1623 textColor = GetSysColor(COLOR_GRAYTEXT);
1624 else {
1625 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1626 textColor = GetTextColor(hdc);
1628 oldTextColor = SetTextColor(hdc, textColor);
1629 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1630 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1631 SetBkMode(hdc, oldBkMode);
1632 SetTextColor(hdc, oldTextColor);
1634 if(hFont) {
1635 SelectObject(hdc, oldFont);
1636 DeleteObject(hFont);
1638 return S_OK;
1641 /***********************************************************************
1642 * GetThemeBackgroundContentRect (UXTHEME.@)
1644 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1645 int iStateId,
1646 const RECT *pBoundingRect,
1647 RECT *pContentRect)
1649 MARGINS margin;
1650 HRESULT hr;
1652 TRACE("(%d,%d)\n", iPartId, iStateId);
1653 if(!hTheme)
1654 return E_HANDLE;
1656 /* try content margins property... */
1657 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1658 if(SUCCEEDED(hr)) {
1659 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1660 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1661 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1662 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1663 } else {
1664 /* otherwise, try to determine content rect from the background type and props */
1665 int bgtype = BT_BORDERFILL;
1666 *pContentRect = *pBoundingRect;
1668 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1669 if(bgtype == BT_BORDERFILL) {
1670 int bordersize = 1;
1672 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1673 InflateRect(pContentRect, -bordersize, -bordersize);
1674 } else if ((bgtype == BT_IMAGEFILE)
1675 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1676 TMT_SIZINGMARGINS, NULL, &margin)))) {
1677 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1678 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1679 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1680 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1682 /* If nothing was found, leave unchanged */
1685 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pContentRect->left, pContentRect->top, pContentRect->right, pContentRect->bottom);
1687 return S_OK;
1690 /***********************************************************************
1691 * GetThemeBackgroundExtent (UXTHEME.@)
1693 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1694 int iStateId, const RECT *pContentRect,
1695 RECT *pExtentRect)
1697 MARGINS margin;
1698 HRESULT hr;
1700 TRACE("(%d,%d)\n", iPartId, iStateId);
1701 if(!hTheme)
1702 return E_HANDLE;
1704 /* try content margins property... */
1705 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1706 if(SUCCEEDED(hr)) {
1707 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1708 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1709 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1710 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1711 } else {
1712 /* otherwise, try to determine content rect from the background type and props */
1713 int bgtype = BT_BORDERFILL;
1714 *pExtentRect = *pContentRect;
1716 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1717 if(bgtype == BT_BORDERFILL) {
1718 int bordersize = 1;
1720 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1721 InflateRect(pExtentRect, bordersize, bordersize);
1722 } else if ((bgtype == BT_IMAGEFILE)
1723 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1724 TMT_SIZINGMARGINS, NULL, &margin)))) {
1725 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1726 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1727 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1728 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1730 /* If nothing was found, leave unchanged */
1733 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pExtentRect->left, pExtentRect->top, pExtentRect->right, pExtentRect->bottom);
1735 return S_OK;
1738 /***********************************************************************
1739 * GetThemeBackgroundRegion (UXTHEME.@)
1741 * Calculate the background region, taking into consideration transparent areas
1742 * of the background image.
1744 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1745 int iStateId, const RECT *pRect,
1746 HRGN *pRegion)
1748 HRESULT hr = S_OK;
1749 int bgtype = BT_BORDERFILL;
1751 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1752 if(!hTheme)
1753 return E_HANDLE;
1754 if(!pRect || !pRegion)
1755 return E_POINTER;
1757 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1758 if(bgtype == BT_IMAGEFILE) {
1759 FIXME("Images not handled yet\n");
1760 hr = ERROR_CALL_NOT_IMPLEMENTED;
1762 else if(bgtype == BT_BORDERFILL) {
1763 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1764 if(!*pRegion)
1765 hr = HRESULT_FROM_WIN32(GetLastError());
1767 else {
1768 FIXME("Unknown background type\n");
1769 /* This should never happen, and hence I don't know what to return */
1770 hr = E_FAIL;
1772 return hr;
1775 /* compute part size for "borderfill" backgrounds */
1776 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1777 int iStateId, THEMESIZE eSize, POINT* psz)
1779 HRESULT hr = S_OK;
1780 int bordersize = 1;
1782 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1783 &bordersize)))
1785 psz->x = psz->y = 2*bordersize;
1786 if (eSize != TS_MIN)
1788 psz->x++;
1789 psz->y++;
1792 return hr;
1795 /***********************************************************************
1796 * GetThemePartSize (UXTHEME.@)
1798 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1799 int iStateId, RECT *prc, THEMESIZE eSize,
1800 SIZE *psz)
1802 int bgtype = BT_BORDERFILL;
1803 HRESULT hr = S_OK;
1804 POINT size = {1, 1};
1806 if(!hTheme)
1807 return E_HANDLE;
1809 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1810 if (bgtype == BT_NONE)
1811 /* do nothing */;
1812 else if(bgtype == BT_IMAGEFILE)
1813 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1814 else if(bgtype == BT_BORDERFILL)
1815 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1816 else {
1817 FIXME("Unknown background type\n");
1818 /* This should never happen, and hence I don't know what to return */
1819 hr = E_FAIL;
1821 psz->cx = size.x;
1822 psz->cy = size.y;
1823 return hr;
1827 /***********************************************************************
1828 * GetThemeTextExtent (UXTHEME.@)
1830 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
1831 int iStateId, LPCWSTR pszText, int iCharCount,
1832 DWORD dwTextFlags, const RECT *pBoundingRect,
1833 RECT *pExtentRect)
1835 HRESULT hr;
1836 HFONT hFont = NULL;
1837 HGDIOBJ oldFont = NULL;
1838 LOGFONTW logfont;
1839 RECT rt = {0,0,0xFFFF,0xFFFF};
1841 TRACE("%d %d: stub\n", iPartId, iStateId);
1842 if(!hTheme)
1843 return E_HANDLE;
1845 if(pBoundingRect)
1846 CopyRect(&rt, pBoundingRect);
1848 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1849 if(SUCCEEDED(hr)) {
1850 hFont = CreateFontIndirectW(&logfont);
1851 if(!hFont)
1852 TRACE("Failed to create font\n");
1854 if(hFont)
1855 oldFont = SelectObject(hdc, hFont);
1857 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
1858 CopyRect(pExtentRect, &rt);
1860 if(hFont) {
1861 SelectObject(hdc, oldFont);
1862 DeleteObject(hFont);
1864 return S_OK;
1867 /***********************************************************************
1868 * GetThemeTextMetrics (UXTHEME.@)
1870 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
1871 int iStateId, TEXTMETRICW *ptm)
1873 HRESULT hr;
1874 HFONT hFont = NULL;
1875 HGDIOBJ oldFont = NULL;
1876 LOGFONTW logfont;
1878 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
1879 if(!hTheme)
1880 return E_HANDLE;
1882 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1883 if(SUCCEEDED(hr)) {
1884 hFont = CreateFontIndirectW(&logfont);
1885 if(!hFont)
1886 TRACE("Failed to create font\n");
1888 if(hFont)
1889 oldFont = SelectObject(hdc, hFont);
1891 if(!GetTextMetricsW(hdc, ptm))
1892 hr = HRESULT_FROM_WIN32(GetLastError());
1894 if(hFont) {
1895 SelectObject(hdc, oldFont);
1896 DeleteObject(hFont);
1898 return hr;
1901 /***********************************************************************
1902 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
1904 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
1905 int iStateId)
1907 int bgtype = BT_BORDERFILL;
1908 RECT rect = {0, 0, 0, 0};
1909 HBITMAP bmpSrc;
1910 RECT rcSrc;
1911 BOOL hasAlpha;
1912 INT transparent;
1913 COLORREF transparentcolor;
1915 TRACE("(%d,%d)\n", iPartId, iStateId);
1917 if(!hTheme)
1918 return FALSE;
1920 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1922 if (bgtype != BT_IMAGEFILE) return FALSE;
1924 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
1925 &bmpSrc, &rcSrc, &hasAlpha)))
1926 return FALSE;
1928 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
1929 &transparentcolor, FALSE);
1930 return (transparent != ALPHABLEND_NONE);