wineqtdecoder: Use the ARRAY_SIZE() macro.
[wine.git] / dlls / uxtheme / draw.c
blob69c53b9ebc34ab3ae9fdeea0844b2ff95245b4b0
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 BOOL res;
57 TRACE("(%p,0x%08x\n", hwnd, dwFlags);
58 res = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
59 UlongToHandle(dwFlags|0x80000000));
60 /* 0x80000000 serves as a "flags set" flag */
61 if (!res)
62 return HRESULT_FROM_WIN32(GetLastError());
63 if (dwFlags & ETDT_USETABTEXTURE)
64 return SetWindowTheme (hwnd, NULL, szTab);
65 else
66 return SetWindowTheme (hwnd, NULL, NULL);
69 /***********************************************************************
70 * IsThemeDialogTextureEnabled (UXTHEME.@)
72 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
74 DWORD dwDialogTextureFlags;
75 TRACE("(%p)\n", hwnd);
77 dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
78 if (dwDialogTextureFlags == 0)
79 /* Means EnableThemeDialogTexture wasn't called for this dialog */
80 return TRUE;
82 return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
85 /***********************************************************************
86 * DrawThemeParentBackground (UXTHEME.@)
88 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
90 RECT rt;
91 POINT org;
92 HWND hParent;
93 HRGN clip = NULL;
94 int hasClip = -1;
96 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
97 hParent = GetParent(hwnd);
98 if(!hParent)
99 hParent = hwnd;
100 if(prc) {
101 rt = *prc;
102 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
104 clip = CreateRectRgn(0,0,1,1);
105 hasClip = GetClipRgn(hdc, clip);
106 if(hasClip == -1)
107 TRACE("Failed to get original clipping region\n");
108 else
109 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
111 else {
112 GetClientRect(hwnd, &rt);
113 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
116 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
118 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
119 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
121 SetViewportOrgEx(hdc, org.x, org.y, NULL);
122 if(prc) {
123 if(hasClip == 0)
124 SelectClipRgn(hdc, NULL);
125 else if(hasClip == 1)
126 SelectClipRgn(hdc, clip);
127 DeleteObject(clip);
129 return S_OK;
133 /***********************************************************************
134 * DrawThemeBackground (UXTHEME.@)
136 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
137 int iStateId, const RECT *pRect,
138 const RECT *pClipRect)
140 DTBGOPTS opts;
141 opts.dwSize = sizeof(DTBGOPTS);
142 opts.dwFlags = 0;
143 if(pClipRect) {
144 opts.dwFlags |= DTBG_CLIPRECT;
145 opts.rcClip = *pClipRect;
147 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
150 /***********************************************************************
151 * UXTHEME_SelectImage
153 * Select the image to use
155 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
157 PTHEME_PROPERTY tp;
158 int imageselecttype = IST_NONE;
159 int i;
160 int image;
161 if(glyph)
162 image = TMT_GLYPHIMAGEFILE;
163 else
164 image = TMT_IMAGEFILE;
166 if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
167 return tp;
168 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
170 if(imageselecttype == IST_DPI) {
171 int reqdpi = 0;
172 int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
173 for(i=4; i>=0; i--) {
174 reqdpi = 0;
175 if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
176 if(reqdpi != 0 && screendpi >= reqdpi) {
177 TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
178 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
182 /* If an image couldn't be selected, choose the first one */
183 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
185 else if(imageselecttype == IST_SIZE) {
186 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
187 POINT reqsize;
188 for(i=4; i>=0; i--) {
189 PTHEME_PROPERTY fileProp =
190 MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
191 if (!fileProp) continue;
192 if(FAILED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
193 /* fall back to size of Nth image */
194 WCHAR szPath[MAX_PATH];
195 int imagelayout = IL_HORIZONTAL;
196 int imagecount = 1;
197 BITMAP bmp;
198 HBITMAP hBmp;
199 BOOL hasAlpha;
201 lstrcpynW(szPath, fileProp->lpValue, min(fileProp->dwValueLen+1, ARRAY_SIZE(szPath)));
202 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
203 if(!hBmp) continue;
205 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
206 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
208 GetObjectW(hBmp, sizeof(bmp), &bmp);
209 if(imagelayout == IL_VERTICAL) {
210 reqsize.x = bmp.bmWidth;
211 reqsize.y = bmp.bmHeight/imagecount;
213 else {
214 reqsize.x = bmp.bmWidth/imagecount;
215 reqsize.y = bmp.bmHeight;
218 if(reqsize.x <= size.x && reqsize.y <= size.y) {
219 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
220 return fileProp;
223 /* If an image couldn't be selected, choose the smallest one */
224 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
226 return NULL;
229 /***********************************************************************
230 * UXTHEME_LoadImage
232 * Load image for part/state
234 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
235 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
237 int imagelayout = IL_HORIZONTAL;
238 int imagecount = 1;
239 int imagenum;
240 BITMAP bmp;
241 WCHAR szPath[MAX_PATH];
242 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
243 if(!tp) {
244 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
245 return E_PROP_ID_UNSUPPORTED;
247 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, ARRAY_SIZE(szPath)));
248 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
249 if(!*hBmp) {
250 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
251 return HRESULT_FROM_WIN32(GetLastError());
254 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
255 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
257 imagenum = max (min (imagecount, iStateId), 1) - 1;
258 GetObjectW(*hBmp, sizeof(bmp), &bmp);
260 if(imagecount < 1) imagecount = 1;
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 */
296 BOOL ret = TRUE;
297 int old_stretch_mode;
298 POINT old_brush_org;
300 old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
301 SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
303 if (transparent == ALPHABLEND_BINARY) {
304 /* Ensure we don't pass any negative values to TransparentBlt */
305 ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
306 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
307 transcolor);
308 } else if ((transparent == ALPHABLEND_NONE) ||
309 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
310 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
311 blendFunc))
313 ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
314 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
315 SRCCOPY);
318 SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
319 SetStretchBltMode(hdcDst, old_stretch_mode);
321 return ret;
324 /***********************************************************************
325 * UXTHEME_Blt
327 * Simplify sending same width/height for both source and dest
329 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
330 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
331 INT transparent, COLORREF transcolor)
333 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
334 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
335 transparent, transcolor);
338 /***********************************************************************
339 * UXTHEME_SizedBlt
341 * Stretches or tiles, depending on sizingtype.
343 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
344 int nWidthDst, int nHeightDst,
345 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
346 int nWidthSrc, int nHeightSrc,
347 int sizingtype,
348 INT transparent, COLORREF transcolor)
350 if (sizingtype == ST_TILE)
352 HDC hdcTemp;
353 BOOL result = FALSE;
355 if (!nWidthSrc || !nHeightSrc) return TRUE;
357 /* For destination width/height less than or equal to source
358 width/height, do not bother with memory bitmap optimization */
359 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
361 int bltWidth = min (nWidthDst, nWidthSrc);
362 int bltHeight = min (nHeightDst, nHeightSrc);
364 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
365 hdcSrc, nXOriginSrc, nYOriginSrc,
366 transparent, transcolor);
369 /* Create a DC with a bitmap consisting of a tiling of the source
370 bitmap, with standard GDI functions. This is faster than an
371 iteration with UXTHEME_Blt(). */
372 hdcTemp = CreateCompatibleDC(hdcSrc);
373 if (hdcTemp != 0)
375 HBITMAP bitmapTemp;
376 HBITMAP bitmapOrig;
377 int nWidthTemp, nHeightTemp;
378 int xOfs, xRemaining;
379 int yOfs, yRemaining;
380 int growSize;
382 /* Calculate temp dimensions of integer multiples of source dimensions */
383 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
384 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
385 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
386 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
388 /* Initial copy of bitmap */
389 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
391 /* Extend bitmap in the X direction. Growth of width is exponential */
392 xOfs = nWidthSrc;
393 xRemaining = nWidthTemp - nWidthSrc;
394 growSize = nWidthSrc;
395 while (xRemaining > 0)
397 growSize = min(growSize, xRemaining);
398 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
399 xOfs += growSize;
400 xRemaining -= growSize;
401 growSize *= 2;
404 /* Extend bitmap in the Y direction. Growth of height is exponential */
405 yOfs = nHeightSrc;
406 yRemaining = nHeightTemp - nHeightSrc;
407 growSize = nHeightSrc;
408 while (yRemaining > 0)
410 growSize = min(growSize, yRemaining);
411 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
412 yOfs += growSize;
413 yRemaining -= growSize;
414 growSize *= 2;
417 /* Use temporary hdc for source */
418 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
419 hdcTemp, 0, 0,
420 transparent, transcolor);
422 SelectObject(hdcTemp, bitmapOrig);
423 DeleteObject(bitmapTemp);
425 DeleteDC(hdcTemp);
426 return result;
428 else
430 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
431 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
432 transparent, transcolor);
436 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
437 * depend on whether the image has full alpha or whether it is
438 * color-transparent or just opaque. */
439 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
440 BOOL hasImageAlpha, INT* transparent,
441 COLORREF* transparentcolor, BOOL glyph)
443 if (hasImageAlpha)
445 *transparent = ALPHABLEND_FULL;
446 *transparentcolor = RGB (255, 0, 255);
448 else
450 BOOL trans = FALSE;
451 GetThemeBool(hTheme, iPartId, iStateId,
452 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
453 if(trans) {
454 *transparent = ALPHABLEND_BINARY;
455 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
456 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
457 transparentcolor))) {
458 /* If image is transparent, but no color was specified, use magenta */
459 *transparentcolor = RGB(255, 0, 255);
462 else
463 *transparent = ALPHABLEND_NONE;
467 /***********************************************************************
468 * UXTHEME_DrawImageGlyph
470 * Draw an imagefile glyph
472 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
473 int iStateId, RECT *pRect,
474 const DTBGOPTS *pOptions)
476 HRESULT hr;
477 HBITMAP bmpSrc = NULL;
478 HDC hdcSrc = NULL;
479 HGDIOBJ oldSrc = NULL;
480 RECT rcSrc;
481 INT transparent = 0;
482 COLORREF transparentcolor;
483 int valign = VA_CENTER;
484 int halign = HA_CENTER;
485 POINT dstSize;
486 POINT srcSize;
487 POINT topleft;
488 BOOL hasAlpha;
490 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
491 &bmpSrc, &rcSrc, &hasAlpha);
492 if(FAILED(hr)) return hr;
493 hdcSrc = CreateCompatibleDC(hdc);
494 if(!hdcSrc) {
495 hr = HRESULT_FROM_WIN32(GetLastError());
496 return hr;
498 oldSrc = SelectObject(hdcSrc, bmpSrc);
500 dstSize.x = pRect->right-pRect->left;
501 dstSize.y = pRect->bottom-pRect->top;
502 srcSize.x = rcSrc.right-rcSrc.left;
503 srcSize.y = rcSrc.bottom-rcSrc.top;
505 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
506 &transparentcolor, TRUE);
507 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
508 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
510 topleft.x = pRect->left;
511 topleft.y = pRect->top;
512 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
513 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
514 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
515 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
517 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
518 hdcSrc, rcSrc.left, rcSrc.top,
519 transparent, transparentcolor)) {
520 hr = HRESULT_FROM_WIN32(GetLastError());
523 SelectObject(hdcSrc, oldSrc);
524 DeleteDC(hdcSrc);
525 return hr;
528 /***********************************************************************
529 * UXTHEME_DrawImageGlyph
531 * Draw glyph on top of background, if appropriate
533 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
534 int iStateId, RECT *pRect,
535 const DTBGOPTS *pOptions)
537 int glyphtype = GT_NONE;
539 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
541 if(glyphtype == GT_IMAGEGLYPH) {
542 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
544 else if(glyphtype == GT_FONTGLYPH) {
545 /* I don't know what a font glyph is, I've never seen it used in any themes */
546 FIXME("Font glyph\n");
548 return S_OK;
551 /***********************************************************************
552 * get_image_part_size
554 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
556 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
557 int iStateId, RECT *prc, THEMESIZE eSize,
558 POINT *psz)
560 HRESULT hr = S_OK;
561 HBITMAP bmpSrc;
562 RECT rcSrc;
563 BOOL hasAlpha;
565 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
566 &bmpSrc, &rcSrc, &hasAlpha);
567 if (FAILED(hr)) return hr;
569 switch (eSize)
571 case TS_DRAW:
572 if (prc != NULL)
574 RECT rcDst;
575 POINT dstSize;
576 POINT srcSize;
577 int sizingtype = ST_STRETCH;
578 BOOL uniformsizing = FALSE;
580 rcDst = *prc;
582 dstSize.x = rcDst.right-rcDst.left;
583 dstSize.y = rcDst.bottom-rcDst.top;
584 srcSize.x = rcSrc.right-rcSrc.left;
585 srcSize.y = rcSrc.bottom-rcSrc.top;
587 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
588 if(uniformsizing) {
589 /* Scale height and width equally */
590 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
592 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
593 rcDst.bottom = rcDst.top + dstSize.y;
595 else
597 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
598 rcDst.right = rcDst.left + dstSize.x;
602 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
603 if(sizingtype == ST_TRUESIZE) {
604 int truesizestretchmark = 100;
606 if(dstSize.x < 0 || dstSize.y < 0) {
607 BOOL mirrorimage = TRUE;
608 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
609 if(mirrorimage) {
610 if(dstSize.x < 0) {
611 rcDst.left += dstSize.x;
612 rcDst.right += dstSize.x;
614 if(dstSize.y < 0) {
615 rcDst.top += dstSize.y;
616 rcDst.bottom += dstSize.y;
620 /* Whatever TrueSizeStretchMark does - it does not seem to
621 * be what's outlined below. It appears as if native
622 * uxtheme always stretches if dest is smaller than source
623 * (ie as if TrueSizeStretchMark==100 with the code below) */
624 #if 0
625 /* Only stretch when target exceeds source by truesizestretchmark percent */
626 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
627 #endif
628 if(dstSize.x < 0 || dstSize.y < 0 ||
629 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
630 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
631 memcpy (psz, &dstSize, sizeof (SIZE));
633 else {
634 memcpy (psz, &srcSize, sizeof (SIZE));
637 else
639 psz->x = abs(dstSize.x);
640 psz->y = abs(dstSize.y);
642 break;
644 /* else fall through */
645 case TS_MIN:
646 /* FIXME: couldn't figure how native uxtheme computes min size */
647 case TS_TRUE:
648 psz->x = rcSrc.right - rcSrc.left;
649 psz->y = rcSrc.bottom - rcSrc.top;
650 break;
652 return hr;
655 /***********************************************************************
656 * UXTHEME_DrawImageBackground
658 * Draw an imagefile background
660 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
661 int iStateId, RECT *pRect,
662 const DTBGOPTS *pOptions)
664 HRESULT hr = S_OK;
665 HBITMAP bmpSrc, bmpSrcResized = NULL;
666 HGDIOBJ oldSrc;
667 HDC hdcSrc, hdcOrigSrc = NULL;
668 RECT rcSrc;
669 RECT rcDst;
670 POINT dstSize;
671 POINT srcSize;
672 POINT drawSize;
673 int sizingtype = ST_STRETCH;
674 INT transparent;
675 COLORREF transparentcolor = 0;
676 BOOL hasAlpha;
678 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
679 &bmpSrc, &rcSrc, &hasAlpha);
680 if(FAILED(hr)) return hr;
681 hdcSrc = CreateCompatibleDC(hdc);
682 if(!hdcSrc) {
683 hr = HRESULT_FROM_WIN32(GetLastError());
684 return hr;
686 oldSrc = SelectObject(hdcSrc, bmpSrc);
688 rcDst = *pRect;
690 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
691 &transparentcolor, FALSE);
693 dstSize.x = rcDst.right-rcDst.left;
694 dstSize.y = rcDst.bottom-rcDst.top;
695 srcSize.x = rcSrc.right-rcSrc.left;
696 srcSize.y = rcSrc.bottom-rcSrc.top;
698 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
699 if(sizingtype == ST_TRUESIZE) {
700 int valign = VA_CENTER, halign = HA_CENTER;
702 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
703 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
704 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
706 if (halign == HA_CENTER)
707 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
708 else if (halign == HA_RIGHT)
709 rcDst.left = rcDst.right - drawSize.x;
710 if (valign == VA_CENTER)
711 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
712 else if (valign == VA_BOTTOM)
713 rcDst.top = rcDst.bottom - drawSize.y;
714 rcDst.right = rcDst.left + drawSize.x;
715 rcDst.bottom = rcDst.top + drawSize.y;
716 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
717 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
718 transparent, transparentcolor))
719 hr = HRESULT_FROM_WIN32(GetLastError());
721 else {
722 HDC hdcDst = NULL;
723 MARGINS sm;
724 POINT org;
726 dstSize.x = abs(dstSize.x);
727 dstSize.y = abs(dstSize.y);
729 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
731 /* Resize source image if destination smaller than margins */
732 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
733 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
734 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
735 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
736 srcSize.y = dstSize.y;
739 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
740 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
741 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
742 srcSize.x = dstSize.x;
745 hdcOrigSrc = hdcSrc;
746 hdcSrc = CreateCompatibleDC(NULL);
747 bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
748 SelectObject(hdcSrc, bmpSrcResized);
750 UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
751 rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
753 rcSrc.left = 0;
754 rcSrc.top = 0;
755 rcSrc.right = srcSize.x;
756 rcSrc.bottom = srcSize.y;
759 hdcDst = hdc;
760 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
762 /* Upper left corner */
763 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
764 hdcSrc, rcSrc.left, rcSrc.top,
765 transparent, transparentcolor)) {
766 hr = HRESULT_FROM_WIN32(GetLastError());
767 goto draw_error;
769 /* Upper right corner */
770 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
771 sm.cxRightWidth, sm.cyTopHeight,
772 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
773 transparent, transparentcolor)) {
774 hr = HRESULT_FROM_WIN32(GetLastError());
775 goto draw_error;
777 /* Lower left corner */
778 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
779 sm.cxLeftWidth, sm.cyBottomHeight,
780 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
781 transparent, transparentcolor)) {
782 hr = HRESULT_FROM_WIN32(GetLastError());
783 goto draw_error;
785 /* Lower right corner */
786 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
787 sm.cxRightWidth, sm.cyBottomHeight,
788 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
789 transparent, transparentcolor)) {
790 hr = HRESULT_FROM_WIN32(GetLastError());
791 goto draw_error;
794 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
795 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
796 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
797 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
798 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
800 if(destCenterWidth > 0) {
801 /* Center top */
802 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
803 destCenterWidth, sm.cyTopHeight,
804 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
805 srcCenterWidth, sm.cyTopHeight,
806 sizingtype, transparent, transparentcolor)) {
807 hr = HRESULT_FROM_WIN32(GetLastError());
808 goto draw_error;
810 /* Center bottom */
811 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
812 destCenterWidth, sm.cyBottomHeight,
813 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
814 srcCenterWidth, sm.cyBottomHeight,
815 sizingtype, transparent, transparentcolor)) {
816 hr = HRESULT_FROM_WIN32(GetLastError());
817 goto draw_error;
820 if(destCenterHeight > 0) {
821 /* Left center */
822 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
823 sm.cxLeftWidth, destCenterHeight,
824 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
825 sm.cxLeftWidth, srcCenterHeight,
826 sizingtype,
827 transparent, transparentcolor)) {
828 hr = HRESULT_FROM_WIN32(GetLastError());
829 goto draw_error;
831 /* Right center */
832 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
833 sm.cxRightWidth, destCenterHeight,
834 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
835 sm.cxRightWidth, srcCenterHeight,
836 sizingtype, transparent, transparentcolor)) {
837 hr = HRESULT_FROM_WIN32(GetLastError());
838 goto draw_error;
841 if(destCenterHeight > 0 && destCenterWidth > 0) {
842 BOOL borderonly = FALSE;
843 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
844 if(!borderonly) {
845 /* Center */
846 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
847 destCenterWidth, destCenterHeight,
848 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
849 srcCenterWidth, srcCenterHeight,
850 sizingtype, transparent, transparentcolor)) {
851 hr = HRESULT_FROM_WIN32(GetLastError());
852 goto draw_error;
858 draw_error:
859 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
861 SelectObject(hdcSrc, oldSrc);
862 DeleteDC(hdcSrc);
863 if (bmpSrcResized) DeleteObject(bmpSrcResized);
864 if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
865 *pRect = rcDst;
866 return hr;
869 /***********************************************************************
870 * UXTHEME_DrawBorderRectangle
872 * Draw the bounding rectangle for a borderfill background
874 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
875 int iStateId, RECT *pRect,
876 const DTBGOPTS *pOptions)
878 HRESULT hr = S_OK;
879 HPEN hPen;
880 HGDIOBJ oldPen;
881 COLORREF bordercolor = RGB(0,0,0);
882 int bordersize = 1;
884 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
885 if(bordersize > 0) {
886 POINT ptCorners[5];
887 ptCorners[0].x = pRect->left;
888 ptCorners[0].y = pRect->top;
889 ptCorners[1].x = pRect->right-1;
890 ptCorners[1].y = pRect->top;
891 ptCorners[2].x = pRect->right-1;
892 ptCorners[2].y = pRect->bottom-1;
893 ptCorners[3].x = pRect->left;
894 ptCorners[3].y = pRect->bottom-1;
895 ptCorners[4].x = pRect->left;
896 ptCorners[4].y = pRect->top;
898 InflateRect(pRect, -bordersize, -bordersize);
899 if(pOptions->dwFlags & DTBG_OMITBORDER)
900 return S_OK;
901 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
902 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
903 if(!hPen)
904 return HRESULT_FROM_WIN32(GetLastError());
905 oldPen = SelectObject(hdc, hPen);
907 if(!Polyline(hdc, ptCorners, 5))
908 hr = HRESULT_FROM_WIN32(GetLastError());
910 SelectObject(hdc, oldPen);
911 DeleteObject(hPen);
913 return hr;
916 /***********************************************************************
917 * UXTHEME_DrawBackgroundFill
919 * Fill a borderfill background rectangle
921 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
922 int iStateId, RECT *pRect,
923 const DTBGOPTS *pOptions)
925 HRESULT hr = S_OK;
926 int filltype = FT_SOLID;
928 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
930 if(pOptions->dwFlags & DTBG_OMITCONTENT)
931 return S_OK;
933 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
935 if(filltype == FT_SOLID) {
936 HBRUSH hBrush;
937 COLORREF fillcolor = RGB(255,255,255);
939 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
940 hBrush = CreateSolidBrush(fillcolor);
941 if(!FillRect(hdc, pRect, hBrush))
942 hr = HRESULT_FROM_WIN32(GetLastError());
943 DeleteObject(hBrush);
945 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
946 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
947 the gradient ratios (no idea how those work)
948 Few themes use this, and the ones I've seen only use 2 colors with
949 a gradient ratio of 0 and 255 respectively
952 COLORREF gradient1 = RGB(0,0,0);
953 COLORREF gradient2 = RGB(255,255,255);
954 TRIVERTEX vert[2];
955 GRADIENT_RECT gRect;
957 FIXME("Gradient implementation not complete\n");
959 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
960 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
962 vert[0].x = pRect->left;
963 vert[0].y = pRect->top;
964 vert[0].Red = GetRValue(gradient1) << 8;
965 vert[0].Green = GetGValue(gradient1) << 8;
966 vert[0].Blue = GetBValue(gradient1) << 8;
967 vert[0].Alpha = 0xff00;
969 vert[1].x = pRect->right;
970 vert[1].y = pRect->bottom;
971 vert[1].Red = GetRValue(gradient2) << 8;
972 vert[1].Green = GetGValue(gradient2) << 8;
973 vert[1].Blue = GetBValue(gradient2) << 8;
974 vert[1].Alpha = 0xff00;
976 gRect.UpperLeft = 0;
977 gRect.LowerRight = 1;
978 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
980 else if(filltype == FT_RADIALGRADIENT) {
981 /* I've never seen this used in a theme */
982 FIXME("Radial gradient\n");
984 else if(filltype == FT_TILEIMAGE) {
985 /* I've never seen this used in a theme */
986 FIXME("Tile image\n");
988 return hr;
991 /***********************************************************************
992 * UXTHEME_DrawBorderBackground
994 * Draw an imagefile background
996 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
997 int iStateId, const RECT *pRect,
998 const DTBGOPTS *pOptions)
1000 HRESULT hr;
1001 RECT rt;
1003 rt = *pRect;
1005 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1006 if(FAILED(hr))
1007 return hr;
1008 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1011 /***********************************************************************
1012 * DrawThemeBackgroundEx (UXTHEME.@)
1014 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1015 int iStateId, const RECT *pRect,
1016 const DTBGOPTS *pOptions)
1018 HRESULT hr;
1019 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1020 const DTBGOPTS *opts;
1021 HRGN clip = NULL;
1022 int hasClip = -1;
1023 int bgtype = BT_BORDERFILL;
1024 RECT rt;
1026 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1027 if(!hTheme)
1028 return E_HANDLE;
1030 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1031 if (bgtype == BT_NONE) return S_OK;
1033 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1034 opts = pOptions;
1035 if(!opts) opts = &defaultOpts;
1037 if(opts->dwFlags & DTBG_CLIPRECT) {
1038 clip = CreateRectRgn(0,0,1,1);
1039 hasClip = GetClipRgn(hdc, clip);
1040 if(hasClip == -1)
1041 TRACE("Failed to get original clipping region\n");
1042 else
1043 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1045 rt = *pRect;
1047 if(bgtype == BT_IMAGEFILE)
1048 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1049 else if(bgtype == BT_BORDERFILL)
1050 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1051 else {
1052 FIXME("Unknown background type\n");
1053 /* This should never happen, and hence I don't know what to return */
1054 hr = E_FAIL;
1056 if(SUCCEEDED(hr))
1057 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1058 if(opts->dwFlags & DTBG_CLIPRECT) {
1059 if(hasClip == 0)
1060 SelectClipRgn(hdc, NULL);
1061 else if(hasClip == 1)
1062 SelectClipRgn(hdc, clip);
1063 DeleteObject(clip);
1065 return hr;
1069 * DrawThemeEdge() implementation
1071 * Since it basically is DrawEdge() with different colors, I copied its code
1072 * from user32's uitools.c.
1075 enum
1077 EDGE_LIGHT,
1078 EDGE_HIGHLIGHT,
1079 EDGE_SHADOW,
1080 EDGE_DARKSHADOW,
1081 EDGE_FILL,
1083 EDGE_WINDOW,
1084 EDGE_WINDOWFRAME,
1086 EDGE_NUMCOLORS
1089 static const struct
1091 int themeProp;
1092 int sysColor;
1093 } EdgeColorMap[EDGE_NUMCOLORS] = {
1094 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1095 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1096 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1097 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1098 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1099 {-1, COLOR_WINDOW},
1100 {-1, COLOR_WINDOWFRAME}
1103 static const signed char LTInnerNormal[] = {
1104 -1, -1, -1, -1,
1105 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1106 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1107 -1, -1, -1, -1
1110 static const signed char LTOuterNormal[] = {
1111 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1112 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1113 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1114 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1117 static const signed char RBInnerNormal[] = {
1118 -1, -1, -1, -1,
1119 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1120 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1121 -1, -1, -1, -1
1124 static const signed char RBOuterNormal[] = {
1125 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1126 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1127 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1128 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1131 static const signed char LTInnerSoft[] = {
1132 -1, -1, -1, -1,
1133 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1134 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1135 -1, -1, -1, -1
1138 static const signed char LTOuterSoft[] = {
1139 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1140 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1141 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1142 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1145 #define RBInnerSoft RBInnerNormal /* These are the same */
1146 #define RBOuterSoft RBOuterNormal
1148 static const signed char LTRBOuterMono[] = {
1149 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1150 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1151 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1152 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1155 static const signed char LTRBInnerMono[] = {
1156 -1, -1, -1, -1,
1157 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1158 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1159 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1162 static const signed char LTRBOuterFlat[] = {
1163 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1164 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1165 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1166 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1169 static const signed char LTRBInnerFlat[] = {
1170 -1, -1, -1, -1,
1171 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1172 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1173 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1176 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1178 COLORREF col;
1179 if ((EdgeColorMap[edgeType].themeProp == -1)
1180 || FAILED (GetThemeColor (theme, part, state,
1181 EdgeColorMap[edgeType].themeProp, &col)))
1182 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1183 return col;
1186 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1188 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1191 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1193 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1196 /***********************************************************************
1197 * draw_diag_edge
1199 * Same as DrawEdge invoked with BF_DIAGONAL
1201 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1202 const RECT* rc, UINT uType,
1203 UINT uFlags, LPRECT contentsRect)
1205 POINT Points[4];
1206 signed char InnerI, OuterI;
1207 HPEN InnerPen, OuterPen;
1208 POINT SavePoint;
1209 HPEN SavePen;
1210 int spx, spy;
1211 int epx, epy;
1212 int Width = rc->right - rc->left;
1213 int Height= rc->bottom - rc->top;
1214 int SmallDiam = Width > Height ? Height : Width;
1215 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1216 || (uType & BDR_OUTER) == BDR_OUTER)
1217 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1218 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1219 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1221 /* Init some vars */
1222 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1223 SavePen = SelectObject(hdc, InnerPen);
1224 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1226 /* Determine the colors of the edges */
1227 if(uFlags & BF_MONO)
1229 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1230 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1232 else if(uFlags & BF_FLAT)
1234 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1235 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1237 else if(uFlags & BF_SOFT)
1239 if(uFlags & BF_BOTTOM)
1241 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1242 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1244 else
1246 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1247 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1250 else
1252 if(uFlags & BF_BOTTOM)
1254 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1255 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1257 else
1259 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1260 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1264 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1265 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1267 MoveToEx(hdc, 0, 0, &SavePoint);
1269 /* Don't ask me why, but this is what is visible... */
1270 /* This must be possible to do much simpler, but I fail to */
1271 /* see the logic in the MS implementation (sigh...). */
1272 /* So, this might look a bit brute force here (and it is), but */
1273 /* it gets the job done;) */
1275 switch(uFlags & BF_RECT)
1277 case 0:
1278 case BF_LEFT:
1279 case BF_BOTTOM:
1280 case BF_BOTTOMLEFT:
1281 /* Left bottom endpoint */
1282 epx = rc->left-1;
1283 spx = epx + SmallDiam;
1284 epy = rc->bottom;
1285 spy = epy - SmallDiam;
1286 break;
1288 case BF_TOPLEFT:
1289 case BF_BOTTOMRIGHT:
1290 /* Left top endpoint */
1291 epx = rc->left-1;
1292 spx = epx + SmallDiam;
1293 epy = rc->top-1;
1294 spy = epy + SmallDiam;
1295 break;
1297 case BF_TOP:
1298 case BF_RIGHT:
1299 case BF_TOPRIGHT:
1300 case BF_RIGHT|BF_LEFT:
1301 case BF_RIGHT|BF_LEFT|BF_TOP:
1302 case BF_BOTTOM|BF_TOP:
1303 case BF_BOTTOM|BF_TOP|BF_LEFT:
1304 case BF_BOTTOMRIGHT|BF_LEFT:
1305 case BF_BOTTOMRIGHT|BF_TOP:
1306 case BF_RECT:
1307 /* Right top endpoint */
1308 spx = rc->left;
1309 epx = spx + SmallDiam;
1310 spy = rc->bottom-1;
1311 epy = spy - SmallDiam;
1312 break;
1315 MoveToEx(hdc, spx, spy, NULL);
1316 SelectObject(hdc, OuterPen);
1317 LineTo(hdc, epx, epy);
1319 SelectObject(hdc, InnerPen);
1321 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1323 case BF_DIAGONAL_ENDBOTTOMLEFT:
1324 case (BF_DIAGONAL|BF_BOTTOM):
1325 case BF_DIAGONAL:
1326 case (BF_DIAGONAL|BF_LEFT):
1327 MoveToEx(hdc, spx-1, spy, NULL);
1328 LineTo(hdc, epx, epy-1);
1329 Points[0].x = spx-add;
1330 Points[0].y = spy;
1331 Points[1].x = rc->left;
1332 Points[1].y = rc->top;
1333 Points[2].x = epx+1;
1334 Points[2].y = epy-1-add;
1335 Points[3] = Points[2];
1336 break;
1338 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1339 MoveToEx(hdc, spx-1, spy, NULL);
1340 LineTo(hdc, epx, epy+1);
1341 Points[0].x = spx-add;
1342 Points[0].y = spy;
1343 Points[1].x = rc->left;
1344 Points[1].y = rc->bottom-1;
1345 Points[2].x = epx+1;
1346 Points[2].y = epy+1+add;
1347 Points[3] = Points[2];
1348 break;
1350 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1351 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1352 case BF_DIAGONAL_ENDTOPRIGHT:
1353 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1354 MoveToEx(hdc, spx+1, spy, NULL);
1355 LineTo(hdc, epx, epy+1);
1356 Points[0].x = epx-1;
1357 Points[0].y = epy+1+add;
1358 Points[1].x = rc->right-1;
1359 Points[1].y = rc->top+add;
1360 Points[2].x = rc->right-1;
1361 Points[2].y = rc->bottom-1;
1362 Points[3].x = spx+add;
1363 Points[3].y = spy;
1364 break;
1366 case BF_DIAGONAL_ENDTOPLEFT:
1367 MoveToEx(hdc, spx, spy-1, NULL);
1368 LineTo(hdc, epx+1, epy);
1369 Points[0].x = epx+1+add;
1370 Points[0].y = epy+1;
1371 Points[1].x = rc->right-1;
1372 Points[1].y = rc->top;
1373 Points[2].x = rc->right-1;
1374 Points[2].y = rc->bottom-1-add;
1375 Points[3].x = spx;
1376 Points[3].y = spy-add;
1377 break;
1379 case (BF_DIAGONAL|BF_TOP):
1380 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1381 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1382 MoveToEx(hdc, spx+1, spy-1, NULL);
1383 LineTo(hdc, epx, epy);
1384 Points[0].x = epx-1;
1385 Points[0].y = epy+1;
1386 Points[1].x = rc->right-1;
1387 Points[1].y = rc->top;
1388 Points[2].x = rc->right-1;
1389 Points[2].y = rc->bottom-1-add;
1390 Points[3].x = spx+add;
1391 Points[3].y = spy-add;
1392 break;
1394 case (BF_DIAGONAL|BF_RIGHT):
1395 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1396 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1397 MoveToEx(hdc, spx, spy, NULL);
1398 LineTo(hdc, epx-1, epy+1);
1399 Points[0].x = spx;
1400 Points[0].y = spy;
1401 Points[1].x = rc->left;
1402 Points[1].y = rc->top+add;
1403 Points[2].x = epx-1-add;
1404 Points[2].y = epy+1+add;
1405 Points[3] = Points[2];
1406 break;
1409 /* Fill the interior if asked */
1410 if((uFlags & BF_MIDDLE) && retval)
1412 HBRUSH hbsave;
1413 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1414 theme, part, state);
1415 HPEN hpsave;
1416 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1417 theme, part, state);
1418 hbsave = SelectObject(hdc, hb);
1419 hpsave = SelectObject(hdc, hp);
1420 Polygon(hdc, Points, 4);
1421 SelectObject(hdc, hbsave);
1422 SelectObject(hdc, hpsave);
1423 DeleteObject (hp);
1424 DeleteObject (hb);
1427 /* Adjust rectangle if asked */
1428 if(uFlags & BF_ADJUST)
1430 *contentsRect = *rc;
1431 if(uFlags & BF_LEFT) contentsRect->left += add;
1432 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1433 if(uFlags & BF_TOP) contentsRect->top += add;
1434 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1437 /* Cleanup */
1438 SelectObject(hdc, SavePen);
1439 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1440 if(InnerI != -1) DeleteObject (InnerPen);
1441 if(OuterI != -1) DeleteObject (OuterPen);
1443 return retval;
1446 /***********************************************************************
1447 * draw_rect_edge
1449 * Same as DrawEdge invoked without BF_DIAGONAL
1451 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1452 const RECT* rc, UINT uType,
1453 UINT uFlags, LPRECT contentsRect)
1455 signed char LTInnerI, LTOuterI;
1456 signed char RBInnerI, RBOuterI;
1457 HPEN LTInnerPen, LTOuterPen;
1458 HPEN RBInnerPen, RBOuterPen;
1459 RECT InnerRect = *rc;
1460 POINT SavePoint;
1461 HPEN SavePen;
1462 int LBpenplus = 0;
1463 int LTpenplus = 0;
1464 int RTpenplus = 0;
1465 int RBpenplus = 0;
1466 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1467 || (uType & BDR_OUTER) == BDR_OUTER)
1468 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1470 /* Init some vars */
1471 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1472 SavePen = SelectObject(hdc, LTInnerPen);
1474 /* Determine the colors of the edges */
1475 if(uFlags & BF_MONO)
1477 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1478 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1480 else if(uFlags & BF_FLAT)
1482 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1483 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1485 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1487 else if(uFlags & BF_SOFT)
1489 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1490 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1491 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1492 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1494 else
1496 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1497 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1498 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1499 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1502 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1503 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1504 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1505 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1507 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1508 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1509 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1510 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1512 MoveToEx(hdc, 0, 0, &SavePoint);
1514 /* Draw the outer edge */
1515 SelectObject(hdc, LTOuterPen);
1516 if(uFlags & BF_TOP)
1518 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1519 LineTo(hdc, InnerRect.right, InnerRect.top);
1521 if(uFlags & BF_LEFT)
1523 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1524 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1526 SelectObject(hdc, RBOuterPen);
1527 if(uFlags & BF_BOTTOM)
1529 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1530 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1532 if(uFlags & BF_RIGHT)
1534 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1535 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1538 /* Draw the inner edge */
1539 SelectObject(hdc, LTInnerPen);
1540 if(uFlags & BF_TOP)
1542 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1543 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1545 if(uFlags & BF_LEFT)
1547 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1548 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1550 SelectObject(hdc, RBInnerPen);
1551 if(uFlags & BF_BOTTOM)
1553 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1554 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1556 if(uFlags & BF_RIGHT)
1558 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1559 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1562 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1564 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1565 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1567 if(uFlags & BF_LEFT) InnerRect.left += add;
1568 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1569 if(uFlags & BF_TOP) InnerRect.top += add;
1570 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1572 if((uFlags & BF_MIDDLE) && retval)
1574 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1575 theme, part, state);
1576 FillRect(hdc, &InnerRect, br);
1577 DeleteObject (br);
1580 if(uFlags & BF_ADJUST)
1581 *contentsRect = InnerRect;
1584 /* Cleanup */
1585 SelectObject(hdc, SavePen);
1586 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1587 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1588 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1589 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1590 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1591 return retval;
1595 /***********************************************************************
1596 * DrawThemeEdge (UXTHEME.@)
1598 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1599 * difference is that it does not rely on the system colors alone, but
1600 * also allows color specification in the theme.
1602 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1603 int iStateId, const RECT *pDestRect, UINT uEdge,
1604 UINT uFlags, RECT *pContentRect)
1606 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1607 if(!hTheme)
1608 return E_HANDLE;
1610 if(uFlags & BF_DIAGONAL)
1611 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1612 uEdge, uFlags, pContentRect);
1613 else
1614 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1615 uEdge, uFlags, pContentRect);
1619 /***********************************************************************
1620 * DrawThemeIcon (UXTHEME.@)
1622 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1623 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1625 FIXME("%d %d: stub\n", iPartId, iStateId);
1626 if(!hTheme)
1627 return E_HANDLE;
1628 return E_NOTIMPL;
1631 /***********************************************************************
1632 * DrawThemeText (UXTHEME.@)
1634 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1635 LPCWSTR pszText, int iCharCount, DWORD flags,
1636 DWORD flags2, const RECT *pRect)
1638 DTTOPTS opts = { 0 };
1639 RECT rt;
1641 TRACE("%d %d\n", iPartId, iStateId);
1643 rt = *pRect;
1645 opts.dwSize = sizeof(opts);
1646 if (flags2 & DTT_GRAYED) {
1647 opts.dwFlags = DTT_TEXTCOLOR;
1648 opts.crText = GetSysColor(COLOR_GRAYTEXT);
1650 return DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, flags, &rt, &opts);
1653 /***********************************************************************
1654 * DrawThemeTextEx (UXTHEME.@)
1656 HRESULT WINAPI DrawThemeTextEx(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1657 LPCWSTR pszText, int iCharCount, DWORD flags, RECT *rect, const DTTOPTS *options)
1659 HRESULT hr;
1660 HFONT hFont = NULL;
1661 HGDIOBJ oldFont = NULL;
1662 LOGFONTW logfont;
1663 COLORREF textColor;
1664 COLORREF oldTextColor;
1665 int oldBkMode;
1667 TRACE("%p %p %d %d %s:%d 0x%08x %p %p\n", hTheme, hdc, iPartId, iStateId,
1668 debugstr_wn(pszText, iCharCount), iCharCount, flags, rect, options);
1670 if(!hTheme)
1671 return E_HANDLE;
1673 if (options->dwFlags & ~DTT_TEXTCOLOR)
1674 FIXME("unsupported flags 0x%08x\n", options->dwFlags);
1676 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1677 if(SUCCEEDED(hr)) {
1678 hFont = CreateFontIndirectW(&logfont);
1679 if(!hFont)
1680 TRACE("Failed to create font\n");
1683 if(hFont)
1684 oldFont = SelectObject(hdc, hFont);
1686 if (options->dwFlags & DTT_TEXTCOLOR)
1687 textColor = options->crText;
1688 else {
1689 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1690 textColor = GetTextColor(hdc);
1692 oldTextColor = SetTextColor(hdc, textColor);
1693 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1694 DrawTextW(hdc, pszText, iCharCount, rect, flags);
1695 SetBkMode(hdc, oldBkMode);
1696 SetTextColor(hdc, oldTextColor);
1698 if(hFont) {
1699 SelectObject(hdc, oldFont);
1700 DeleteObject(hFont);
1702 return S_OK;
1705 /***********************************************************************
1706 * GetThemeBackgroundContentRect (UXTHEME.@)
1708 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1709 int iStateId,
1710 const RECT *pBoundingRect,
1711 RECT *pContentRect)
1713 MARGINS margin;
1714 HRESULT hr;
1716 TRACE("(%d,%d)\n", iPartId, iStateId);
1717 if(!hTheme)
1718 return E_HANDLE;
1720 /* try content margins property... */
1721 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1722 if(SUCCEEDED(hr)) {
1723 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1724 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1725 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1726 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1727 } else {
1728 /* otherwise, try to determine content rect from the background type and props */
1729 int bgtype = BT_BORDERFILL;
1730 *pContentRect = *pBoundingRect;
1732 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1733 if(bgtype == BT_BORDERFILL) {
1734 int bordersize = 1;
1736 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1737 InflateRect(pContentRect, -bordersize, -bordersize);
1738 } else if ((bgtype == BT_IMAGEFILE)
1739 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1740 TMT_SIZINGMARGINS, NULL, &margin)))) {
1741 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1742 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1743 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1744 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1746 /* If nothing was found, leave unchanged */
1749 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1751 return S_OK;
1754 /***********************************************************************
1755 * GetThemeBackgroundExtent (UXTHEME.@)
1757 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1758 int iStateId, const RECT *pContentRect,
1759 RECT *pExtentRect)
1761 MARGINS margin;
1762 HRESULT hr;
1764 TRACE("(%d,%d)\n", iPartId, iStateId);
1765 if(!hTheme)
1766 return E_HANDLE;
1768 /* try content margins property... */
1769 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1770 if(SUCCEEDED(hr)) {
1771 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1772 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1773 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1774 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1775 } else {
1776 /* otherwise, try to determine content rect from the background type and props */
1777 int bgtype = BT_BORDERFILL;
1778 *pExtentRect = *pContentRect;
1780 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1781 if(bgtype == BT_BORDERFILL) {
1782 int bordersize = 1;
1784 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1785 InflateRect(pExtentRect, bordersize, bordersize);
1786 } else if ((bgtype == BT_IMAGEFILE)
1787 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1788 TMT_SIZINGMARGINS, NULL, &margin)))) {
1789 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1790 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1791 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1792 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1794 /* If nothing was found, leave unchanged */
1797 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1799 return S_OK;
1802 static inline void flush_rgn_data( HRGN rgn, RGNDATA *data )
1804 HRGN tmp = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1806 CombineRgn( rgn, rgn, tmp, RGN_OR );
1807 DeleteObject( tmp );
1808 data->rdh.nCount = 0;
1811 static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len )
1813 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
1815 if (len <= 0) return;
1816 rect->left = x;
1817 rect->top = y;
1818 rect->right = x + len;
1819 rect->bottom = y + 1;
1820 data->rdh.nCount++;
1821 if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT))
1822 flush_rgn_data( rgn, data );
1825 static HRESULT create_image_bg_region(HTHEME theme, int part, int state, const RECT *rect, HRGN *rgn)
1827 RECT r;
1828 HDC dc;
1829 HBITMAP bmp;
1830 HRGN hrgn;
1831 BOOL istrans;
1832 COLORREF transcolour;
1833 HBRUSH transbrush;
1834 unsigned int x, y, start;
1835 BITMAPINFO bitmapinfo;
1836 DWORD *bits;
1837 char buffer[4096];
1838 RGNDATA *data = (RGNDATA *)buffer;
1840 if (FAILED(GetThemeBool(theme, part, state, TMT_TRANSPARENT, &istrans)) || !istrans) {
1841 *rgn = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
1842 return S_OK;
1845 r = *rect;
1846 OffsetRect(&r, -r.left, -r.top);
1848 if (FAILED(GetThemeColor(theme, part, state, TMT_TRANSPARENTCOLOR, &transcolour)))
1849 transcolour = RGB(255, 0, 255); /* defaults to magenta */
1851 dc = CreateCompatibleDC(NULL);
1852 if (!dc) {
1853 WARN("CreateCompatibleDC failed\n");
1854 return E_FAIL;
1857 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1858 bitmapinfo.bmiHeader.biWidth = rect->right - rect->left;
1859 bitmapinfo.bmiHeader.biHeight = -(rect->bottom - rect->top);
1860 bitmapinfo.bmiHeader.biPlanes = 1;
1861 bitmapinfo.bmiHeader.biBitCount = 32;
1862 bitmapinfo.bmiHeader.biCompression = BI_RGB;
1863 bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * 4;
1864 bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
1865 bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
1866 bitmapinfo.bmiHeader.biClrUsed = 0;
1867 bitmapinfo.bmiHeader.biClrImportant = 0;
1869 bmp = CreateDIBSection(dc, &bitmapinfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1870 if (!bmp) {
1871 WARN("CreateDIBSection failed\n");
1872 DeleteDC(dc);
1873 return E_FAIL;
1876 SelectObject(dc, bmp);
1878 transbrush = CreateSolidBrush(transcolour);
1879 FillRect(dc, &r, transbrush);
1880 DeleteObject(transbrush);
1882 if (FAILED(DrawThemeBackground(theme, dc, part, state, &r, NULL))) {
1883 WARN("DrawThemeBackground failed\n");
1884 DeleteObject(bmp);
1885 DeleteDC(dc);
1886 return E_FAIL;
1889 data->rdh.dwSize = sizeof(data->rdh);
1890 data->rdh.iType = RDH_RECTANGLES;
1891 data->rdh.nCount = 0;
1892 data->rdh.nRgnSize = sizeof(buffer) - sizeof(data->rdh);
1894 hrgn = CreateRectRgn(0, 0, 0, 0);
1896 for (y = 0; y < r.bottom; y++, bits += r.right) {
1897 x = 0;
1898 while (x < r.right) {
1899 while (x < r.right && (bits[x] & 0xffffff) == transcolour) x++;
1900 start = x;
1901 while (x < r.right && !((bits[x] & 0xffffff) == transcolour)) x++;
1902 add_row( hrgn, data, rect->left + start, rect->top + y, x - start );
1906 if (data->rdh.nCount > 0) flush_rgn_data(hrgn, data);
1908 *rgn = hrgn;
1910 DeleteObject(bmp);
1911 DeleteDC(dc);
1913 return S_OK;
1916 /***********************************************************************
1917 * GetThemeBackgroundRegion (UXTHEME.@)
1919 * Calculate the background region, taking into consideration transparent areas
1920 * of the background image.
1922 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1923 int iStateId, const RECT *pRect,
1924 HRGN *pRegion)
1926 HRESULT hr = S_OK;
1927 int bgtype = BT_BORDERFILL;
1929 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1930 if(!hTheme)
1931 return E_HANDLE;
1932 if(!pRect || !pRegion)
1933 return E_POINTER;
1935 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1936 if(bgtype == BT_IMAGEFILE) {
1937 hr = create_image_bg_region(hTheme, iPartId, iStateId, pRect, pRegion);
1939 else if(bgtype == BT_BORDERFILL) {
1940 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1941 if(!*pRegion)
1942 hr = HRESULT_FROM_WIN32(GetLastError());
1944 else {
1945 FIXME("Unknown background type\n");
1946 /* This should never happen, and hence I don't know what to return */
1947 hr = E_FAIL;
1949 return hr;
1952 /* compute part size for "borderfill" backgrounds */
1953 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1954 int iStateId, THEMESIZE eSize, POINT* psz)
1956 HRESULT hr = S_OK;
1957 int bordersize = 1;
1959 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1960 &bordersize)))
1962 psz->x = psz->y = 2*bordersize;
1963 if (eSize != TS_MIN)
1965 psz->x++;
1966 psz->y++;
1969 return hr;
1972 /***********************************************************************
1973 * GetThemePartSize (UXTHEME.@)
1975 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1976 int iStateId, RECT *prc, THEMESIZE eSize,
1977 SIZE *psz)
1979 int bgtype = BT_BORDERFILL;
1980 HRESULT hr = S_OK;
1981 POINT size = {1, 1};
1983 if(!hTheme)
1984 return E_HANDLE;
1986 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1987 if (bgtype == BT_NONE)
1988 /* do nothing */;
1989 else if(bgtype == BT_IMAGEFILE)
1990 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1991 else if(bgtype == BT_BORDERFILL)
1992 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1993 else {
1994 FIXME("Unknown background type\n");
1995 /* This should never happen, and hence I don't know what to return */
1996 hr = E_FAIL;
1998 psz->cx = size.x;
1999 psz->cy = size.y;
2000 return hr;
2004 /***********************************************************************
2005 * GetThemeTextExtent (UXTHEME.@)
2007 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2008 int iStateId, LPCWSTR pszText, int iCharCount,
2009 DWORD dwTextFlags, const RECT *pBoundingRect,
2010 RECT *pExtentRect)
2012 HRESULT hr;
2013 HFONT hFont = NULL;
2014 HGDIOBJ oldFont = NULL;
2015 LOGFONTW logfont;
2016 RECT rt = {0,0,0xFFFF,0xFFFF};
2018 TRACE("%d %d\n", iPartId, iStateId);
2019 if(!hTheme)
2020 return E_HANDLE;
2022 if(pBoundingRect)
2023 rt = *pBoundingRect;
2025 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2026 if(SUCCEEDED(hr)) {
2027 hFont = CreateFontIndirectW(&logfont);
2028 if(!hFont)
2029 TRACE("Failed to create font\n");
2031 if(hFont)
2032 oldFont = SelectObject(hdc, hFont);
2034 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2035 *pExtentRect = rt;
2037 if(hFont) {
2038 SelectObject(hdc, oldFont);
2039 DeleteObject(hFont);
2041 return S_OK;
2044 /***********************************************************************
2045 * GetThemeTextMetrics (UXTHEME.@)
2047 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2048 int iStateId, TEXTMETRICW *ptm)
2050 HRESULT hr;
2051 HFONT hFont = NULL;
2052 HGDIOBJ oldFont = NULL;
2053 LOGFONTW logfont;
2055 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2056 if(!hTheme)
2057 return E_HANDLE;
2059 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2060 if(SUCCEEDED(hr)) {
2061 hFont = CreateFontIndirectW(&logfont);
2062 if(!hFont)
2063 TRACE("Failed to create font\n");
2065 if(hFont)
2066 oldFont = SelectObject(hdc, hFont);
2068 if(!GetTextMetricsW(hdc, ptm))
2069 hr = HRESULT_FROM_WIN32(GetLastError());
2071 if(hFont) {
2072 SelectObject(hdc, oldFont);
2073 DeleteObject(hFont);
2075 return hr;
2078 /***********************************************************************
2079 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2081 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2082 int iStateId)
2084 int bgtype = BT_BORDERFILL;
2085 RECT rect = {0, 0, 0, 0};
2086 HBITMAP bmpSrc;
2087 RECT rcSrc;
2088 BOOL hasAlpha;
2089 INT transparent;
2090 COLORREF transparentcolor;
2092 TRACE("(%d,%d)\n", iPartId, iStateId);
2094 if(!hTheme)
2095 return FALSE;
2097 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2099 if (bgtype != BT_IMAGEFILE) return FALSE;
2101 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2102 &bmpSrc, &rcSrc, &hasAlpha)))
2103 return FALSE;
2105 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2106 &transparentcolor, FALSE);
2107 return (transparent != ALPHABLEND_NONE);