dwmapi: Clear DWM_TIMING_INFO structure before returning.
[wine.git] / dlls / uxtheme / draw.c
blob3ddb9b3bf4d67fef8e95fa815274f20565dcbdf2
1 /*
2 * Win32 5.1 Theme drawing
4 * Copyright (C) 2003 Kevin Koltzau
5 * Copyright 2021 Zhiyi Zhang for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include <stdlib.h>
25 #include <stdarg.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "wingdi.h"
31 #include "commctrl.h"
32 #include "commoncontrols.h"
33 #include "vfwmsgs.h"
34 #include "uxtheme.h"
35 #include "vssym32.h"
37 #include "msstyles.h"
38 #include "uxthemedll.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
44 /***********************************************************************
45 * Defines and global variables
48 extern ATOM atDialogThemeEnabled;
50 /***********************************************************************/
52 /***********************************************************************
53 * EnableThemeDialogTexture (UXTHEME.@)
55 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD new_flag)
57 DWORD old_flag = 0;
58 BOOL res;
60 TRACE("(%p,%#lx\n", hwnd, new_flag);
62 new_flag &= ETDT_VALIDBITS;
64 if (new_flag == 0)
65 return S_OK;
67 if (new_flag & ETDT_DISABLE)
69 new_flag = ETDT_DISABLE;
70 old_flag = 0;
73 if (new_flag & ~ETDT_DISABLE)
75 old_flag = HandleToUlong(GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled)));
76 old_flag &= ~ETDT_DISABLE;
79 new_flag = new_flag | old_flag;
80 res = SetPropW(hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled), UlongToHandle(new_flag));
81 return res ? S_OK : HRESULT_FROM_WIN32(GetLastError());
84 /***********************************************************************
85 * IsThemeDialogTextureEnabled (UXTHEME.@)
87 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
89 DWORD dwDialogTextureFlags;
91 TRACE("(%p)\n", hwnd);
93 dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
94 return dwDialogTextureFlags && !(dwDialogTextureFlags & ETDT_DISABLE);
97 /***********************************************************************
98 * DrawThemeParentBackground (UXTHEME.@)
100 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
102 RECT rt;
103 POINT org;
104 HWND hParent;
105 HRGN clip = NULL;
106 int hasClip = -1;
108 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
109 hParent = GetParent(hwnd);
110 if(!hParent)
111 hParent = hwnd;
112 if(prc) {
113 rt = *prc;
114 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
116 clip = CreateRectRgn(0,0,1,1);
117 hasClip = GetClipRgn(hdc, clip);
118 if(hasClip == -1)
119 TRACE("Failed to get original clipping region\n");
120 else
121 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
123 else {
124 GetClientRect(hwnd, &rt);
125 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
128 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
130 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
131 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
133 SetViewportOrgEx(hdc, org.x, org.y, NULL);
134 if(prc) {
135 if(hasClip == 0)
136 SelectClipRgn(hdc, NULL);
137 else if(hasClip == 1)
138 SelectClipRgn(hdc, clip);
139 DeleteObject(clip);
141 return S_OK;
145 /***********************************************************************
146 * DrawThemeBackground (UXTHEME.@)
148 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
149 int iStateId, const RECT *pRect,
150 const RECT *pClipRect)
152 DTBGOPTS opts;
153 opts.dwSize = sizeof(DTBGOPTS);
154 opts.dwFlags = 0;
155 if(pClipRect) {
156 opts.dwFlags |= DTBG_CLIPRECT;
157 opts.rcClip = *pClipRect;
159 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
162 /* Map integer 1..7 to TMT_MINDPI1..TMT_MINDPI7 */
163 static int mindpi_index_to_property(int index)
165 return index <= 5 ? TMT_MINDPI1 + index - 1 : TMT_MINDPI6 + index - 6;
168 /* Map integer 1..7 to TMT_MINSIZE1..TMT_MINSIZE7 */
169 static int minsize_index_to_property(int index)
171 return index <= 5 ? TMT_MINSIZE1 + index - 1 : TMT_MINSIZE6 + index - 6;
174 /* Map integer 1..7 to TMT_IMAGEFILE1..TMT_IMAGEFILE7 */
175 static int imagefile_index_to_property(int index)
177 return index <= 5 ? TMT_IMAGEFILE1 + index - 1 : TMT_IMAGEFILE6 + index - 6;
180 /***********************************************************************
181 * UXTHEME_SelectImage
183 * Select the image to use
185 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, int iPartId, int iStateId,
186 const RECT *pRect, BOOL glyph, int *imageDpi)
188 int imageselecttype = IST_NONE, glyphtype = GT_NONE;
189 PTHEME_PROPERTY tp = NULL;
190 int i;
192 if (imageDpi)
193 *imageDpi = 96;
195 /* Try TMT_IMAGEFILE first when drawing part background and the part contains glyph images.
196 * Otherwise, search TMT_IMAGEFILE1~7 and then TMT_IMAGEFILE or TMT_GLYPHIMAGEFILE */
197 if (!glyph)
199 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
200 if (glyphtype == GT_IMAGEGLYPH &&
201 (tp = MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE)))
202 return tp;
205 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
207 if(imageselecttype == IST_DPI) {
208 int reqdpi = 0;
209 int dpi = MSSTYLES_GetThemeDPI(hTheme);
210 for (i = 7; i >= 1; i--)
212 reqdpi = 0;
213 if (SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, mindpi_index_to_property(i),
214 &reqdpi)))
216 if (reqdpi != 0 && dpi >= reqdpi)
218 TRACE("Using %d DPI, image %d\n", reqdpi, imagefile_index_to_property(i));
220 if (imageDpi)
221 *imageDpi = reqdpi;
223 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME,
224 imagefile_index_to_property(i));
228 /* If an image couldn't be selected, choose the first one */
229 tp = MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
231 else if(imageselecttype == IST_SIZE) {
232 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
233 POINT reqsize;
234 for (i = 7; i >= 1; i--)
236 PTHEME_PROPERTY fileProp;
238 fileProp = MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME,
239 imagefile_index_to_property(i));
240 if (!fileProp) continue;
241 if (FAILED(GetThemePosition(hTheme, iPartId, iStateId, minsize_index_to_property(i),
242 &reqsize)))
244 /* fall back to size of Nth image */
245 WCHAR szPath[MAX_PATH];
246 int imagelayout = IL_HORIZONTAL;
247 int imagecount = 1;
248 BITMAP bmp;
249 HBITMAP hBmp;
250 BOOL hasAlpha;
252 lstrcpynW(szPath, fileProp->lpValue, min(fileProp->dwValueLen+1, ARRAY_SIZE(szPath)));
253 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
254 if(!hBmp) continue;
256 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
257 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
259 GetObjectW(hBmp, sizeof(bmp), &bmp);
260 if(imagelayout == IL_VERTICAL) {
261 reqsize.x = bmp.bmWidth;
262 reqsize.y = bmp.bmHeight/imagecount;
264 else {
265 reqsize.x = bmp.bmWidth/imagecount;
266 reqsize.y = bmp.bmHeight;
269 if(reqsize.x <= size.x && reqsize.y <= size.y) {
270 TRACE("Using image size %ldx%ld, image %d\n", reqsize.x, reqsize.y,
271 imagefile_index_to_property(i));
272 return fileProp;
275 /* If an image couldn't be selected, choose the smallest one */
276 tp = MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
279 if (!tp)
280 tp = MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME,
281 glyph ? TMT_GLYPHIMAGEFILE : TMT_IMAGEFILE);
282 return tp;
285 /***********************************************************************
286 * UXTHEME_LoadImage
288 * Load image for part/state
290 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, int iPartId, int iStateId, const RECT *pRect,
291 BOOL glyph, HBITMAP *hBmp, RECT *bmpRect, BOOL *hasImageAlpha,
292 int *imageDpi)
294 int imagelayout = IL_HORIZONTAL;
295 int imagecount = 1;
296 int imagenum;
297 BITMAP bmp;
298 WCHAR szPath[MAX_PATH];
299 PTHEME_PROPERTY tp;
301 tp = UXTHEME_SelectImage(hTheme, iPartId, iStateId, pRect, glyph, imageDpi);
302 if(!tp) {
303 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
304 return E_PROP_ID_UNSUPPORTED;
306 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, ARRAY_SIZE(szPath)));
307 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
308 if(!*hBmp) {
309 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
310 return HRESULT_FROM_WIN32(GetLastError());
313 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
314 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
316 imagenum = max (min (imagecount, iStateId), 1) - 1;
317 GetObjectW(*hBmp, sizeof(bmp), &bmp);
319 if(imagecount < 1) imagecount = 1;
321 if(imagelayout == IL_VERTICAL) {
322 int height = bmp.bmHeight/imagecount;
323 bmpRect->left = 0;
324 bmpRect->right = bmp.bmWidth;
325 bmpRect->top = imagenum * height;
326 bmpRect->bottom = bmpRect->top + height;
328 else {
329 int width = bmp.bmWidth/imagecount;
330 bmpRect->left = imagenum * width;
331 bmpRect->right = bmpRect->left + width;
332 bmpRect->top = 0;
333 bmpRect->bottom = bmp.bmHeight;
335 return S_OK;
338 /***********************************************************************
339 * UXTHEME_StretchBlt
341 * Pseudo TransparentBlt/StretchBlt
343 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
344 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
345 INT transparent, COLORREF transcolor)
347 static const BLENDFUNCTION blendFunc =
349 AC_SRC_OVER, /* BlendOp */
350 0, /* BlendFlag */
351 255, /* SourceConstantAlpha */
352 AC_SRC_ALPHA /* AlphaFormat */
355 BOOL ret = TRUE;
356 int old_stretch_mode;
357 POINT old_brush_org;
359 old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
360 SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
362 if (transparent == ALPHABLEND_BINARY) {
363 /* Ensure we don't pass any negative values to TransparentBlt */
364 ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
365 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
366 transcolor);
367 } else if ((transparent == ALPHABLEND_NONE) ||
368 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
369 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
370 blendFunc))
372 ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
373 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
374 SRCCOPY);
377 SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
378 SetStretchBltMode(hdcDst, old_stretch_mode);
380 return ret;
383 /***********************************************************************
384 * UXTHEME_Blt
386 * Simplify sending same width/height for both source and dest
388 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
389 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
390 INT transparent, COLORREF transcolor)
392 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
393 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
394 transparent, transcolor);
397 /***********************************************************************
398 * UXTHEME_SizedBlt
400 * Stretches or tiles, depending on sizingtype.
402 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
403 int nWidthDst, int nHeightDst,
404 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
405 int nWidthSrc, int nHeightSrc,
406 int sizingtype,
407 INT transparent, COLORREF transcolor)
409 if (sizingtype == ST_TILE)
411 HDC hdcTemp;
412 BOOL result = FALSE;
414 if (!nWidthSrc || !nHeightSrc) return TRUE;
416 /* For destination width/height less than or equal to source
417 width/height, do not bother with memory bitmap optimization */
418 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
420 int bltWidth = min (nWidthDst, nWidthSrc);
421 int bltHeight = min (nHeightDst, nHeightSrc);
423 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
424 hdcSrc, nXOriginSrc, nYOriginSrc,
425 transparent, transcolor);
428 /* Create a DC with a bitmap consisting of a tiling of the source
429 bitmap, with standard GDI functions. This is faster than an
430 iteration with UXTHEME_Blt(). */
431 hdcTemp = CreateCompatibleDC(hdcSrc);
432 if (hdcTemp != 0)
434 HBITMAP bitmapTemp;
435 HBITMAP bitmapOrig;
436 int nWidthTemp, nHeightTemp;
437 int xOfs, xRemaining;
438 int yOfs, yRemaining;
439 int growSize;
441 /* Calculate temp dimensions of integer multiples of source dimensions */
442 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
443 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
444 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
445 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
447 /* Initial copy of bitmap */
448 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
450 /* Extend bitmap in the X direction. Growth of width is exponential */
451 xOfs = nWidthSrc;
452 xRemaining = nWidthTemp - nWidthSrc;
453 growSize = nWidthSrc;
454 while (xRemaining > 0)
456 growSize = min(growSize, xRemaining);
457 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
458 xOfs += growSize;
459 xRemaining -= growSize;
460 growSize *= 2;
463 /* Extend bitmap in the Y direction. Growth of height is exponential */
464 yOfs = nHeightSrc;
465 yRemaining = nHeightTemp - nHeightSrc;
466 growSize = nHeightSrc;
467 while (yRemaining > 0)
469 growSize = min(growSize, yRemaining);
470 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
471 yOfs += growSize;
472 yRemaining -= growSize;
473 growSize *= 2;
476 /* Use temporary hdc for source */
477 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
478 hdcTemp, 0, 0,
479 transparent, transcolor);
481 SelectObject(hdcTemp, bitmapOrig);
482 DeleteObject(bitmapTemp);
484 DeleteDC(hdcTemp);
485 return result;
487 else
489 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
490 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
491 transparent, transcolor);
495 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
496 * depend on whether the image has full alpha or whether it is
497 * color-transparent or just opaque. */
498 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
499 BOOL hasImageAlpha, INT* transparent,
500 COLORREF* transparentcolor, BOOL glyph)
502 if (hasImageAlpha)
504 *transparent = ALPHABLEND_FULL;
505 *transparentcolor = RGB (255, 0, 255);
507 else
509 BOOL trans = FALSE;
510 GetThemeBool(hTheme, iPartId, iStateId,
511 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
512 if(trans) {
513 *transparent = ALPHABLEND_BINARY;
514 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
515 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
516 transparentcolor))) {
517 /* If image is transparent, but no color was specified, use magenta */
518 *transparentcolor = RGB(255, 0, 255);
521 else
522 *transparent = ALPHABLEND_NONE;
526 /***********************************************************************
527 * UXTHEME_DrawImageGlyph
529 * Draw an imagefile glyph
531 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
532 int iStateId, RECT *pRect,
533 const DTBGOPTS *pOptions)
535 HRESULT hr;
536 HBITMAP bmpSrc = NULL;
537 HDC hdcSrc = NULL;
538 HGDIOBJ oldSrc = NULL;
539 RECT rcSrc;
540 INT transparent = 0;
541 COLORREF transparentcolor;
542 int valign = VA_CENTER;
543 int halign = HA_CENTER;
544 POINT dstSize;
545 POINT srcSize;
546 POINT topleft;
547 BOOL hasAlpha;
549 hr = UXTHEME_LoadImage(hTheme, iPartId, iStateId, pRect, TRUE, &bmpSrc, &rcSrc, &hasAlpha,
550 NULL);
551 if(FAILED(hr)) return hr;
552 hdcSrc = CreateCompatibleDC(hdc);
553 if(!hdcSrc) {
554 hr = HRESULT_FROM_WIN32(GetLastError());
555 return hr;
557 oldSrc = SelectObject(hdcSrc, bmpSrc);
559 dstSize.x = pRect->right-pRect->left;
560 dstSize.y = pRect->bottom-pRect->top;
561 srcSize.x = rcSrc.right-rcSrc.left;
562 srcSize.y = rcSrc.bottom-rcSrc.top;
564 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
565 &transparentcolor, TRUE);
566 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
567 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
569 topleft.x = pRect->left;
570 topleft.y = pRect->top;
571 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
572 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
573 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
574 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
576 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
577 hdcSrc, rcSrc.left, rcSrc.top,
578 transparent, transparentcolor)) {
579 hr = HRESULT_FROM_WIN32(GetLastError());
582 SelectObject(hdcSrc, oldSrc);
583 DeleteDC(hdcSrc);
584 return hr;
587 /***********************************************************************
588 * UXTHEME_DrawGlyph
590 * Draw glyph on top of background, if appropriate
592 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
593 int iStateId, RECT *pRect,
594 const DTBGOPTS *pOptions)
596 int glyphtype = GT_NONE;
598 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
600 if(glyphtype == GT_IMAGEGLYPH) {
601 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
603 else if(glyphtype == GT_FONTGLYPH) {
604 /* I don't know what a font glyph is, I've never seen it used in any themes */
605 FIXME("Font glyph\n");
607 return S_OK;
610 /***********************************************************************
611 * get_image_part_size
613 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
615 static HRESULT get_image_part_size(HTHEME hTheme, int iPartId, int iStateId, RECT *prc,
616 THEMESIZE eSize, POINT *psz)
618 int imageDpi, dstDpi;
619 HRESULT hr = S_OK;
620 HBITMAP bmpSrc;
621 RECT rcSrc;
622 BOOL hasAlpha;
624 hr = UXTHEME_LoadImage(hTheme, iPartId, iStateId, prc, FALSE, &bmpSrc, &rcSrc, &hasAlpha,
625 &imageDpi);
626 if (FAILED(hr)) return hr;
628 switch (eSize)
630 case TS_DRAW:
632 int sizingType = ST_STRETCH, scalingType = TSST_NONE, stretchMark = 0;
633 POINT srcSize;
635 srcSize.x = rcSrc.right - rcSrc.left;
636 srcSize.y = rcSrc.bottom - rcSrc.top;
638 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingType);
639 if (sizingType == ST_TRUESIZE)
641 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_TRUESIZESCALINGTYPE, &scalingType);
642 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &stretchMark);
643 if (scalingType == TSST_DPI)
645 /* Scale to DPI only if the destination DPI exceeds the source DPI by
646 * stretchMark percent */
647 dstDpi = MSSTYLES_GetThemeDPI(hTheme);
648 if (dstDpi != imageDpi && MulDiv(100, dstDpi, imageDpi) >= stretchMark + 100)
650 srcSize.x = MulDiv(srcSize.x, dstDpi, imageDpi);
651 srcSize.y = MulDiv(srcSize.y, dstDpi, imageDpi);
655 *psz = srcSize;
657 if (prc != NULL)
659 POINT dstSize;
660 BOOL uniformsizing = FALSE;
662 dstSize.x = prc->right - prc->left;
663 dstSize.y = prc->bottom - prc->top;
665 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
666 if(uniformsizing) {
667 /* Scale height and width equally */
668 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
669 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
670 else
671 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
674 if (sizingType == ST_TRUESIZE)
676 if ((dstSize.x < 0 || dstSize.y < 0)
677 || (dstSize.x < srcSize.x && dstSize.y < srcSize.y)
678 || (scalingType == TSST_SIZE
679 && MulDiv(100, dstSize.x, srcSize.x) >= stretchMark + 100
680 && MulDiv(100, dstSize.y, srcSize.y) >= stretchMark + 100))
682 *psz = dstSize;
685 else
687 psz->x = abs(dstSize.x);
688 psz->y = abs(dstSize.y);
692 break;
694 case TS_MIN:
695 /* FIXME: couldn't figure how native uxtheme computes min size */
696 case TS_TRUE:
697 psz->x = rcSrc.right - rcSrc.left;
698 psz->y = rcSrc.bottom - rcSrc.top;
699 break;
701 return hr;
704 /***********************************************************************
705 * UXTHEME_DrawImageBackground
707 * Draw an imagefile background
709 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
710 int iStateId, RECT *pRect,
711 const DTBGOPTS *pOptions)
713 int destCenterWidth, srcCenterWidth, destCenterHeight, srcCenterHeight;
714 HRESULT hr = S_OK;
715 HBITMAP bmpSrc;
716 HGDIOBJ oldSrc;
717 HDC hdcSrc;
718 RECT rcSrc;
719 RECT rcDst;
720 POINT dstSize;
721 POINT srcSize;
722 POINT drawSize;
723 int sizingtype = ST_STRETCH;
724 INT transparent;
725 COLORREF transparentcolor = 0;
726 BOOL hasAlpha;
728 hr = UXTHEME_LoadImage(hTheme, iPartId, iStateId, pRect, FALSE, &bmpSrc, &rcSrc, &hasAlpha,
729 NULL);
730 if(FAILED(hr)) return hr;
731 hdcSrc = CreateCompatibleDC(hdc);
732 if(!hdcSrc) {
733 hr = HRESULT_FROM_WIN32(GetLastError());
734 return hr;
736 oldSrc = SelectObject(hdcSrc, bmpSrc);
738 rcDst = *pRect;
740 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
741 &transparentcolor, FALSE);
743 dstSize.x = rcDst.right-rcDst.left;
744 dstSize.y = rcDst.bottom-rcDst.top;
745 srcSize.x = rcSrc.right-rcSrc.left;
746 srcSize.y = rcSrc.bottom-rcSrc.top;
748 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
749 if(sizingtype == ST_TRUESIZE) {
750 int valign = VA_CENTER, halign = HA_CENTER;
752 get_image_part_size(hTheme, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
753 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
754 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
756 if (halign == HA_CENTER)
757 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
758 else if (halign == HA_RIGHT)
759 rcDst.left = rcDst.right - drawSize.x;
760 if (valign == VA_CENTER)
761 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
762 else if (valign == VA_BOTTOM)
763 rcDst.top = rcDst.bottom - drawSize.y;
764 rcDst.right = rcDst.left + drawSize.x;
765 rcDst.bottom = rcDst.top + drawSize.y;
766 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
767 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
768 transparent, transparentcolor))
769 hr = HRESULT_FROM_WIN32(GetLastError());
771 else {
772 HDC hdcDst = NULL;
773 MARGINS sm;
774 POINT org;
776 dstSize.x = abs(dstSize.x);
777 dstSize.y = abs(dstSize.y);
779 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
781 /* Resize sizing margins if destination is smaller */
782 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
783 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
784 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
785 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
788 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
789 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
790 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
794 hdcDst = hdc;
795 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
797 /* Upper left corner */
798 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
799 hdcSrc, rcSrc.left, rcSrc.top,
800 transparent, transparentcolor)) {
801 hr = HRESULT_FROM_WIN32(GetLastError());
802 goto draw_error;
804 /* Upper right corner */
805 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
806 sm.cxRightWidth, sm.cyTopHeight,
807 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
808 transparent, transparentcolor)) {
809 hr = HRESULT_FROM_WIN32(GetLastError());
810 goto draw_error;
812 /* Lower left corner */
813 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
814 sm.cxLeftWidth, sm.cyBottomHeight,
815 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
816 transparent, transparentcolor)) {
817 hr = HRESULT_FROM_WIN32(GetLastError());
818 goto draw_error;
820 /* Lower right corner */
821 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
822 sm.cxRightWidth, sm.cyBottomHeight,
823 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
824 transparent, transparentcolor)) {
825 hr = HRESULT_FROM_WIN32(GetLastError());
826 goto draw_error;
829 destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
830 srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
831 destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
832 srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
834 if(destCenterWidth > 0) {
835 /* Center top */
836 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
837 destCenterWidth, sm.cyTopHeight,
838 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
839 srcCenterWidth, sm.cyTopHeight,
840 sizingtype, transparent, transparentcolor)) {
841 hr = HRESULT_FROM_WIN32(GetLastError());
842 goto draw_error;
844 /* Center bottom */
845 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
846 destCenterWidth, sm.cyBottomHeight,
847 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
848 srcCenterWidth, sm.cyBottomHeight,
849 sizingtype, transparent, transparentcolor)) {
850 hr = HRESULT_FROM_WIN32(GetLastError());
851 goto draw_error;
854 if(destCenterHeight > 0) {
855 /* Left center */
856 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
857 sm.cxLeftWidth, destCenterHeight,
858 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
859 sm.cxLeftWidth, srcCenterHeight,
860 sizingtype,
861 transparent, transparentcolor)) {
862 hr = HRESULT_FROM_WIN32(GetLastError());
863 goto draw_error;
865 /* Right center */
866 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
867 sm.cxRightWidth, destCenterHeight,
868 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
869 sm.cxRightWidth, srcCenterHeight,
870 sizingtype, transparent, transparentcolor)) {
871 hr = HRESULT_FROM_WIN32(GetLastError());
872 goto draw_error;
875 if(destCenterHeight > 0 && destCenterWidth > 0) {
876 BOOL borderonly = FALSE;
877 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
878 if(!borderonly) {
879 /* Center */
880 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
881 destCenterWidth, destCenterHeight,
882 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
883 srcCenterWidth, srcCenterHeight,
884 sizingtype, transparent, transparentcolor)) {
885 hr = HRESULT_FROM_WIN32(GetLastError());
886 goto draw_error;
891 draw_error:
892 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
894 SelectObject(hdcSrc, oldSrc);
895 DeleteDC(hdcSrc);
896 *pRect = rcDst;
897 return hr;
900 /***********************************************************************
901 * UXTHEME_DrawBorderRectangle
903 * Draw the bounding rectangle for a borderfill background
905 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
906 int iStateId, RECT *pRect,
907 const DTBGOPTS *pOptions)
909 HRESULT hr = S_OK;
910 HPEN hPen;
911 HGDIOBJ oldPen;
912 COLORREF bordercolor = RGB(0,0,0);
913 int bordersize = 1;
915 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
916 if(bordersize > 0) {
917 POINT ptCorners[5];
918 ptCorners[0].x = pRect->left;
919 ptCorners[0].y = pRect->top;
920 ptCorners[1].x = pRect->right-1;
921 ptCorners[1].y = pRect->top;
922 ptCorners[2].x = pRect->right-1;
923 ptCorners[2].y = pRect->bottom-1;
924 ptCorners[3].x = pRect->left;
925 ptCorners[3].y = pRect->bottom-1;
926 ptCorners[4].x = pRect->left;
927 ptCorners[4].y = pRect->top;
929 InflateRect(pRect, -bordersize, -bordersize);
930 if(pOptions->dwFlags & DTBG_OMITBORDER)
931 return S_OK;
932 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
933 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
934 if(!hPen)
935 return HRESULT_FROM_WIN32(GetLastError());
936 oldPen = SelectObject(hdc, hPen);
938 if(!Polyline(hdc, ptCorners, 5))
939 hr = HRESULT_FROM_WIN32(GetLastError());
941 SelectObject(hdc, oldPen);
942 DeleteObject(hPen);
944 return hr;
947 /***********************************************************************
948 * UXTHEME_DrawBackgroundFill
950 * Fill a borderfill background rectangle
952 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
953 int iStateId, RECT *pRect,
954 const DTBGOPTS *pOptions)
956 HRESULT hr = S_OK;
957 int filltype = FT_SOLID;
959 TRACE("(%d,%d,%ld)\n", iPartId, iStateId, pOptions->dwFlags);
961 if(pOptions->dwFlags & DTBG_OMITCONTENT)
962 return S_OK;
964 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
966 if(filltype == FT_SOLID) {
967 HBRUSH hBrush;
968 COLORREF fillcolor = RGB(255,255,255);
970 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
971 hBrush = CreateSolidBrush(fillcolor);
972 if(!FillRect(hdc, pRect, hBrush))
973 hr = HRESULT_FROM_WIN32(GetLastError());
974 DeleteObject(hBrush);
976 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
977 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
978 the gradient ratios (no idea how those work)
979 Few themes use this, and the ones I've seen only use 2 colors with
980 a gradient ratio of 0 and 255 respectively
983 COLORREF gradient1 = RGB(0,0,0);
984 COLORREF gradient2 = RGB(255,255,255);
985 TRIVERTEX vert[2];
986 GRADIENT_RECT gRect;
988 FIXME("Gradient implementation not complete\n");
990 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
991 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
993 vert[0].x = pRect->left;
994 vert[0].y = pRect->top;
995 vert[0].Red = GetRValue(gradient1) << 8;
996 vert[0].Green = GetGValue(gradient1) << 8;
997 vert[0].Blue = GetBValue(gradient1) << 8;
998 vert[0].Alpha = 0xff00;
1000 vert[1].x = pRect->right;
1001 vert[1].y = pRect->bottom;
1002 vert[1].Red = GetRValue(gradient2) << 8;
1003 vert[1].Green = GetGValue(gradient2) << 8;
1004 vert[1].Blue = GetBValue(gradient2) << 8;
1005 vert[1].Alpha = 0xff00;
1007 gRect.UpperLeft = 0;
1008 gRect.LowerRight = 1;
1009 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
1011 else if(filltype == FT_RADIALGRADIENT) {
1012 /* I've never seen this used in a theme */
1013 FIXME("Radial gradient\n");
1015 else if(filltype == FT_TILEIMAGE) {
1016 /* I've never seen this used in a theme */
1017 FIXME("Tile image\n");
1019 return hr;
1022 /***********************************************************************
1023 * UXTHEME_DrawBorderBackground
1025 * Draw an imagefile background
1027 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
1028 int iStateId, const RECT *pRect,
1029 const DTBGOPTS *pOptions)
1031 HRESULT hr;
1032 RECT rt;
1034 rt = *pRect;
1036 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1037 if(FAILED(hr))
1038 return hr;
1039 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1042 /***********************************************************************
1043 * DrawThemeBackgroundEx (UXTHEME.@)
1045 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1046 int iStateId, const RECT *pRect,
1047 const DTBGOPTS *pOptions)
1049 HRESULT hr;
1050 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1051 const DTBGOPTS *opts;
1052 HRGN clip = NULL;
1053 int hasClip = -1;
1054 int bgtype = BT_BORDERFILL;
1055 RECT rt;
1057 TRACE("(%p,%p,%d,%d,%ld,%ld)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1058 if(!hTheme)
1059 return E_HANDLE;
1061 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1062 if (bgtype == BT_NONE) return S_OK;
1064 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1065 opts = pOptions;
1066 if(!opts) opts = &defaultOpts;
1068 if(opts->dwFlags & DTBG_CLIPRECT) {
1069 clip = CreateRectRgn(0,0,1,1);
1070 hasClip = GetClipRgn(hdc, clip);
1071 if(hasClip == -1)
1072 TRACE("Failed to get original clipping region\n");
1073 else
1074 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1076 rt = *pRect;
1078 if(bgtype == BT_IMAGEFILE)
1079 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1080 else if(bgtype == BT_BORDERFILL)
1081 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1082 else {
1083 FIXME("Unknown background type\n");
1084 /* This should never happen, and hence I don't know what to return */
1085 hr = E_FAIL;
1087 if(SUCCEEDED(hr))
1088 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1089 if(opts->dwFlags & DTBG_CLIPRECT) {
1090 if(hasClip == 0)
1091 SelectClipRgn(hdc, NULL);
1092 else if(hasClip == 1)
1093 SelectClipRgn(hdc, clip);
1094 DeleteObject(clip);
1096 return hr;
1100 * DrawThemeEdge() implementation
1102 * Since it basically is DrawEdge() with different colors, I copied its code
1103 * from user32's uitools.c.
1106 enum
1108 EDGE_LIGHT,
1109 EDGE_HIGHLIGHT,
1110 EDGE_SHADOW,
1111 EDGE_DARKSHADOW,
1112 EDGE_FILL,
1114 EDGE_WINDOW,
1115 EDGE_WINDOWFRAME,
1117 EDGE_NUMCOLORS
1120 static const struct
1122 int themeProp;
1123 int sysColor;
1124 } EdgeColorMap[EDGE_NUMCOLORS] = {
1125 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1126 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1127 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1128 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1129 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1130 {-1, COLOR_WINDOW},
1131 {-1, COLOR_WINDOWFRAME}
1134 static const signed char LTInnerNormal[] = {
1135 -1, -1, -1, -1,
1136 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1137 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1138 -1, -1, -1, -1
1141 static const signed char LTOuterNormal[] = {
1142 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1143 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1144 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1145 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1148 static const signed char RBInnerNormal[] = {
1149 -1, -1, -1, -1,
1150 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1151 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1152 -1, -1, -1, -1
1155 static const signed char RBOuterNormal[] = {
1156 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1157 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1158 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1159 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1162 static const signed char LTInnerSoft[] = {
1163 -1, -1, -1, -1,
1164 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1165 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1166 -1, -1, -1, -1
1169 static const signed char LTOuterSoft[] = {
1170 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1171 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1172 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1173 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1176 #define RBInnerSoft RBInnerNormal /* These are the same */
1177 #define RBOuterSoft RBOuterNormal
1179 static const signed char LTRBOuterMono[] = {
1180 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1181 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1182 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1183 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1186 static const signed char LTRBInnerMono[] = {
1187 -1, -1, -1, -1,
1188 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1189 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1190 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1193 static const signed char LTRBOuterFlat[] = {
1194 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1195 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1196 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1197 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1200 static const signed char LTRBInnerFlat[] = {
1201 -1, -1, -1, -1,
1202 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1203 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1204 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1207 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1209 COLORREF col;
1210 if ((EdgeColorMap[edgeType].themeProp == -1)
1211 || FAILED (GetThemeColor (theme, part, state,
1212 EdgeColorMap[edgeType].themeProp, &col)))
1213 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1214 return col;
1217 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1219 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1222 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1224 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1227 /***********************************************************************
1228 * draw_diag_edge
1230 * Same as DrawEdge invoked with BF_DIAGONAL
1232 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1233 const RECT* rc, UINT uType,
1234 UINT uFlags, LPRECT contentsRect)
1236 POINT Points[4];
1237 signed char InnerI, OuterI;
1238 HPEN InnerPen, OuterPen;
1239 POINT SavePoint;
1240 HPEN SavePen;
1241 int spx, spy;
1242 int epx, epy;
1243 int Width = rc->right - rc->left;
1244 int Height= rc->bottom - rc->top;
1245 int SmallDiam = Width > Height ? Height : Width;
1246 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1247 || (uType & BDR_OUTER) == BDR_OUTER)
1248 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1249 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1250 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1252 /* Init some vars */
1253 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1254 SavePen = SelectObject(hdc, InnerPen);
1255 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1257 /* Determine the colors of the edges */
1258 if(uFlags & BF_MONO)
1260 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1261 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1263 else if(uFlags & BF_FLAT)
1265 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1266 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1268 else if(uFlags & BF_SOFT)
1270 if(uFlags & BF_BOTTOM)
1272 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1273 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1275 else
1277 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1278 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1281 else
1283 if(uFlags & BF_BOTTOM)
1285 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1286 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1288 else
1290 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1291 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1295 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1296 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1298 MoveToEx(hdc, 0, 0, &SavePoint);
1300 /* Don't ask me why, but this is what is visible... */
1301 /* This must be possible to do much simpler, but I fail to */
1302 /* see the logic in the MS implementation (sigh...). */
1303 /* So, this might look a bit brute force here (and it is), but */
1304 /* it gets the job done;) */
1306 switch(uFlags & BF_RECT)
1308 case 0:
1309 case BF_LEFT:
1310 case BF_BOTTOM:
1311 case BF_BOTTOMLEFT:
1312 /* Left bottom endpoint */
1313 epx = rc->left-1;
1314 spx = epx + SmallDiam;
1315 epy = rc->bottom;
1316 spy = epy - SmallDiam;
1317 break;
1319 case BF_TOPLEFT:
1320 case BF_BOTTOMRIGHT:
1321 /* Left top endpoint */
1322 epx = rc->left-1;
1323 spx = epx + SmallDiam;
1324 epy = rc->top-1;
1325 spy = epy + SmallDiam;
1326 break;
1328 case BF_TOP:
1329 case BF_RIGHT:
1330 case BF_TOPRIGHT:
1331 case BF_RIGHT|BF_LEFT:
1332 case BF_RIGHT|BF_LEFT|BF_TOP:
1333 case BF_BOTTOM|BF_TOP:
1334 case BF_BOTTOM|BF_TOP|BF_LEFT:
1335 case BF_BOTTOMRIGHT|BF_LEFT:
1336 case BF_BOTTOMRIGHT|BF_TOP:
1337 case BF_RECT:
1338 /* Right top endpoint */
1339 spx = rc->left;
1340 epx = spx + SmallDiam;
1341 spy = rc->bottom-1;
1342 epy = spy - SmallDiam;
1343 break;
1346 MoveToEx(hdc, spx, spy, NULL);
1347 SelectObject(hdc, OuterPen);
1348 LineTo(hdc, epx, epy);
1350 SelectObject(hdc, InnerPen);
1352 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1354 case BF_DIAGONAL_ENDBOTTOMLEFT:
1355 case (BF_DIAGONAL|BF_BOTTOM):
1356 case BF_DIAGONAL:
1357 case (BF_DIAGONAL|BF_LEFT):
1358 MoveToEx(hdc, spx-1, spy, NULL);
1359 LineTo(hdc, epx, epy-1);
1360 Points[0].x = spx-add;
1361 Points[0].y = spy;
1362 Points[1].x = rc->left;
1363 Points[1].y = rc->top;
1364 Points[2].x = epx+1;
1365 Points[2].y = epy-1-add;
1366 Points[3] = Points[2];
1367 break;
1369 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1370 MoveToEx(hdc, spx-1, spy, NULL);
1371 LineTo(hdc, epx, epy+1);
1372 Points[0].x = spx-add;
1373 Points[0].y = spy;
1374 Points[1].x = rc->left;
1375 Points[1].y = rc->bottom-1;
1376 Points[2].x = epx+1;
1377 Points[2].y = epy+1+add;
1378 Points[3] = Points[2];
1379 break;
1381 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1382 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1383 case BF_DIAGONAL_ENDTOPRIGHT:
1384 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1385 MoveToEx(hdc, spx+1, spy, NULL);
1386 LineTo(hdc, epx, epy+1);
1387 Points[0].x = epx-1;
1388 Points[0].y = epy+1+add;
1389 Points[1].x = rc->right-1;
1390 Points[1].y = rc->top+add;
1391 Points[2].x = rc->right-1;
1392 Points[2].y = rc->bottom-1;
1393 Points[3].x = spx+add;
1394 Points[3].y = spy;
1395 break;
1397 case BF_DIAGONAL_ENDTOPLEFT:
1398 MoveToEx(hdc, spx, spy-1, NULL);
1399 LineTo(hdc, epx+1, epy);
1400 Points[0].x = epx+1+add;
1401 Points[0].y = epy+1;
1402 Points[1].x = rc->right-1;
1403 Points[1].y = rc->top;
1404 Points[2].x = rc->right-1;
1405 Points[2].y = rc->bottom-1-add;
1406 Points[3].x = spx;
1407 Points[3].y = spy-add;
1408 break;
1410 case (BF_DIAGONAL|BF_TOP):
1411 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1412 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1413 MoveToEx(hdc, spx+1, spy-1, NULL);
1414 LineTo(hdc, epx, epy);
1415 Points[0].x = epx-1;
1416 Points[0].y = epy+1;
1417 Points[1].x = rc->right-1;
1418 Points[1].y = rc->top;
1419 Points[2].x = rc->right-1;
1420 Points[2].y = rc->bottom-1-add;
1421 Points[3].x = spx+add;
1422 Points[3].y = spy-add;
1423 break;
1425 case (BF_DIAGONAL|BF_RIGHT):
1426 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1427 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1428 MoveToEx(hdc, spx, spy, NULL);
1429 LineTo(hdc, epx-1, epy+1);
1430 Points[0].x = spx;
1431 Points[0].y = spy;
1432 Points[1].x = rc->left;
1433 Points[1].y = rc->top+add;
1434 Points[2].x = epx-1-add;
1435 Points[2].y = epy+1+add;
1436 Points[3] = Points[2];
1437 break;
1440 /* Fill the interior if asked */
1441 if((uFlags & BF_MIDDLE) && retval)
1443 HBRUSH hbsave;
1444 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1445 theme, part, state);
1446 HPEN hpsave;
1447 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1448 theme, part, state);
1449 hbsave = SelectObject(hdc, hb);
1450 hpsave = SelectObject(hdc, hp);
1451 Polygon(hdc, Points, 4);
1452 SelectObject(hdc, hbsave);
1453 SelectObject(hdc, hpsave);
1454 DeleteObject (hp);
1455 DeleteObject (hb);
1458 /* Adjust rectangle if asked */
1459 if(uFlags & BF_ADJUST)
1461 *contentsRect = *rc;
1462 if(uFlags & BF_LEFT) contentsRect->left += add;
1463 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1464 if(uFlags & BF_TOP) contentsRect->top += add;
1465 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1468 /* Cleanup */
1469 SelectObject(hdc, SavePen);
1470 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1471 if(InnerI != -1) DeleteObject (InnerPen);
1472 if(OuterI != -1) DeleteObject (OuterPen);
1474 return retval;
1477 /***********************************************************************
1478 * draw_rect_edge
1480 * Same as DrawEdge invoked without BF_DIAGONAL
1482 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1483 const RECT* rc, UINT uType,
1484 UINT uFlags, LPRECT contentsRect)
1486 signed char LTInnerI, LTOuterI;
1487 signed char RBInnerI, RBOuterI;
1488 HPEN LTInnerPen, LTOuterPen;
1489 HPEN RBInnerPen, RBOuterPen;
1490 RECT InnerRect = *rc;
1491 POINT SavePoint;
1492 HPEN SavePen;
1493 int LBpenplus = 0;
1494 int LTpenplus = 0;
1495 int RTpenplus = 0;
1496 int RBpenplus = 0;
1497 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1498 || (uType & BDR_OUTER) == BDR_OUTER)
1499 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1501 /* Init some vars */
1502 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1503 SavePen = SelectObject(hdc, LTInnerPen);
1505 /* Determine the colors of the edges */
1506 if(uFlags & BF_MONO)
1508 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1509 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1511 else if(uFlags & BF_FLAT)
1513 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1514 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1516 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1518 else if(uFlags & BF_SOFT)
1520 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1521 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1522 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1523 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1525 else
1527 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1528 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1529 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1530 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1533 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1534 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1535 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1536 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1538 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1539 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1540 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1541 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1543 MoveToEx(hdc, 0, 0, &SavePoint);
1545 /* Draw the outer edge */
1546 SelectObject(hdc, LTOuterPen);
1547 if(uFlags & BF_TOP)
1549 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1550 LineTo(hdc, InnerRect.right, InnerRect.top);
1552 if(uFlags & BF_LEFT)
1554 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1555 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1557 SelectObject(hdc, RBOuterPen);
1558 if(uFlags & BF_BOTTOM)
1560 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1561 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1563 if(uFlags & BF_RIGHT)
1565 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1566 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1569 /* Draw the inner edge */
1570 SelectObject(hdc, LTInnerPen);
1571 if(uFlags & BF_TOP)
1573 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1574 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1576 if(uFlags & BF_LEFT)
1578 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1579 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1581 SelectObject(hdc, RBInnerPen);
1582 if(uFlags & BF_BOTTOM)
1584 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1585 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1587 if(uFlags & BF_RIGHT)
1589 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1590 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1593 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1595 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1596 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1598 if(uFlags & BF_LEFT) InnerRect.left += add;
1599 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1600 if(uFlags & BF_TOP) InnerRect.top += add;
1601 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1603 if((uFlags & BF_MIDDLE) && retval)
1605 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1606 theme, part, state);
1607 FillRect(hdc, &InnerRect, br);
1608 DeleteObject (br);
1611 if(uFlags & BF_ADJUST)
1612 *contentsRect = InnerRect;
1615 /* Cleanup */
1616 SelectObject(hdc, SavePen);
1617 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1618 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1619 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1620 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1621 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1622 return retval;
1626 /***********************************************************************
1627 * DrawThemeEdge (UXTHEME.@)
1629 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1630 * difference is that it does not rely on the system colors alone, but
1631 * also allows color specification in the theme.
1633 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1634 int iStateId, const RECT *pDestRect, UINT uEdge,
1635 UINT uFlags, RECT *pContentRect)
1637 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1638 if(!hTheme)
1639 return E_HANDLE;
1641 if(uFlags & BF_DIAGONAL)
1642 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1643 uEdge, uFlags, pContentRect);
1644 else
1645 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1646 uEdge, uFlags, pContentRect);
1650 /***********************************************************************
1651 * DrawThemeIcon (UXTHEME.@)
1653 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1654 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1656 INT effect = ICE_NONE, saturation = 0, alpha = 128;
1657 IImageList *image_list = (IImageList *)himl;
1658 IMAGELISTDRAWPARAMS params = {0};
1659 COLORREF color = 0;
1661 TRACE("%p %p %d %d %s %p %d\n", hTheme, hdc, iPartId, iStateId, wine_dbgstr_rect(pRect), himl,
1662 iImageIndex);
1664 if (!hTheme)
1665 return E_HANDLE;
1667 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_ICONEFFECT, &effect);
1668 switch (effect)
1670 case ICE_NONE:
1671 params.fState = ILS_NORMAL;
1672 break;
1673 case ICE_GLOW:
1674 GetThemeColor(hTheme, iPartId, iStateId, TMT_GLOWCOLOR, &color);
1675 params.fState = ILS_GLOW;
1676 params.crEffect = color;
1677 break;
1678 case ICE_SHADOW:
1679 GetThemeColor(hTheme, iPartId, iStateId, TMT_SHADOWCOLOR, &color);
1680 params.fState = ILS_SHADOW;
1681 params.crEffect = color;
1682 break;
1683 case ICE_PULSE:
1684 GetThemeInt(hTheme, iPartId, iStateId, TMT_SATURATION, &saturation);
1685 params.fState = ILS_SATURATE;
1686 params.Frame = saturation;
1687 break;
1688 case ICE_ALPHA:
1689 GetThemeInt(hTheme, iPartId, iStateId, TMT_ALPHALEVEL, &alpha);
1690 params.fState = ILS_ALPHA;
1691 params.Frame = alpha;
1692 break;
1695 params.cbSize = sizeof(params);
1696 params.himl = himl;
1697 params.i = iImageIndex;
1698 params.hdcDst = hdc;
1699 params.x = pRect->left;
1700 params.y = pRect->top;
1701 params.cx = pRect->right - pRect->left;
1702 params.cy = pRect->bottom - pRect->top;
1703 params.rgbBk = CLR_NONE;
1704 params.rgbFg = CLR_NONE;
1705 params.fStyle = ILD_TRANSPARENT;
1706 return IImageList_Draw(image_list, &params);
1709 /***********************************************************************
1710 * DrawThemeText (UXTHEME.@)
1712 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1713 LPCWSTR pszText, int iCharCount, DWORD flags,
1714 DWORD flags2, const RECT *pRect)
1716 DTTOPTS opts = { 0 };
1717 RECT rt;
1719 TRACE("%d %d\n", iPartId, iStateId);
1721 rt = *pRect;
1723 opts.dwSize = sizeof(opts);
1724 if (flags2 & DTT_GRAYED) {
1725 opts.dwFlags = DTT_TEXTCOLOR;
1726 opts.crText = GetSysColor(COLOR_GRAYTEXT);
1728 return DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, flags, &rt, &opts);
1731 /***********************************************************************
1732 * DrawThemeTextEx (UXTHEME.@)
1734 HRESULT WINAPI DrawThemeTextEx(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1735 LPCWSTR pszText, int iCharCount, DWORD flags, RECT *rect, const DTTOPTS *options)
1737 HRESULT hr;
1738 HFONT hFont = NULL;
1739 HGDIOBJ oldFont = NULL;
1740 LOGFONTW logfont;
1741 COLORREF textColor;
1742 COLORREF oldTextColor;
1743 int oldBkMode;
1744 int fontProp;
1746 TRACE("%p %p %d %d %s:%d 0x%08lx %p %p\n", hTheme, hdc, iPartId, iStateId,
1747 debugstr_wn(pszText, iCharCount), iCharCount, flags, rect, options);
1749 if(!hTheme)
1750 return E_HANDLE;
1752 if (options->dwFlags & ~(DTT_TEXTCOLOR | DTT_FONTPROP))
1753 FIXME("unsupported flags 0x%08lx\n", options->dwFlags);
1755 if (options->dwFlags & DTT_FONTPROP)
1756 fontProp = options->iFontPropId;
1757 else
1758 fontProp = TMT_FONT;
1760 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, fontProp, &logfont);
1761 if(SUCCEEDED(hr)) {
1762 hFont = CreateFontIndirectW(&logfont);
1763 if(!hFont)
1764 TRACE("Failed to create font\n");
1767 if(hFont)
1768 oldFont = SelectObject(hdc, hFont);
1770 if (options->dwFlags & DTT_TEXTCOLOR)
1771 textColor = options->crText;
1772 else {
1773 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1774 textColor = GetTextColor(hdc);
1776 oldTextColor = SetTextColor(hdc, textColor);
1777 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1778 DrawTextW(hdc, pszText, iCharCount, rect, flags);
1779 SetBkMode(hdc, oldBkMode);
1780 SetTextColor(hdc, oldTextColor);
1782 if(hFont) {
1783 SelectObject(hdc, oldFont);
1784 DeleteObject(hFont);
1786 return S_OK;
1789 /***********************************************************************
1790 * GetThemeBackgroundContentRect (UXTHEME.@)
1792 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1793 int iStateId,
1794 const RECT *pBoundingRect,
1795 RECT *pContentRect)
1797 MARGINS margin;
1798 HRESULT hr;
1800 TRACE("(%d,%d)\n", iPartId, iStateId);
1801 if(!hTheme)
1802 return E_HANDLE;
1804 /* try content margins property... */
1805 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1806 if(SUCCEEDED(hr)) {
1807 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1808 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1809 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1810 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1811 } else {
1812 /* otherwise, try to determine content rect from the background type and props */
1813 int bgtype = BT_BORDERFILL;
1814 *pContentRect = *pBoundingRect;
1816 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1817 if(bgtype == BT_BORDERFILL) {
1818 int bordersize = 1;
1820 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1821 InflateRect(pContentRect, -bordersize, -bordersize);
1822 } else if ((bgtype == BT_IMAGEFILE)
1823 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1824 TMT_SIZINGMARGINS, NULL, &margin)))) {
1825 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1826 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1827 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1828 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1830 /* If nothing was found, leave unchanged */
1833 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1835 return S_OK;
1838 /***********************************************************************
1839 * GetThemeBackgroundExtent (UXTHEME.@)
1841 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1842 int iStateId, const RECT *pContentRect,
1843 RECT *pExtentRect)
1845 MARGINS margin;
1846 HRESULT hr;
1848 TRACE("(%d,%d)\n", iPartId, iStateId);
1849 if(!hTheme)
1850 return E_HANDLE;
1852 /* try content margins property... */
1853 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1854 if(SUCCEEDED(hr)) {
1855 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1856 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1857 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1858 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1859 } else {
1860 /* otherwise, try to determine content rect from the background type and props */
1861 int bgtype = BT_BORDERFILL;
1862 *pExtentRect = *pContentRect;
1864 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1865 if(bgtype == BT_BORDERFILL) {
1866 int bordersize = 1;
1868 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1869 InflateRect(pExtentRect, bordersize, bordersize);
1870 } else if ((bgtype == BT_IMAGEFILE)
1871 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1872 TMT_SIZINGMARGINS, NULL, &margin)))) {
1873 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1874 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1875 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1876 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1878 /* If nothing was found, leave unchanged */
1881 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1883 return S_OK;
1886 static inline void flush_rgn_data( HRGN rgn, RGNDATA *data )
1888 HRGN tmp = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1890 CombineRgn( rgn, rgn, tmp, RGN_OR );
1891 DeleteObject( tmp );
1892 data->rdh.nCount = 0;
1895 static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len )
1897 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
1899 if (len <= 0) return;
1900 rect->left = x;
1901 rect->top = y;
1902 rect->right = x + len;
1903 rect->bottom = y + 1;
1904 data->rdh.nCount++;
1905 if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT))
1906 flush_rgn_data( rgn, data );
1909 static HRESULT create_image_bg_region(HTHEME theme, int part, int state, const RECT *rect, HRGN *rgn)
1911 RECT r;
1912 HDC dc;
1913 HBITMAP bmp;
1914 HRGN hrgn;
1915 BOOL istrans;
1916 COLORREF transcolour;
1917 HBRUSH transbrush;
1918 unsigned int x, y, start;
1919 BITMAPINFO bitmapinfo;
1920 DWORD *bits;
1921 char buffer[4096];
1922 RGNDATA *data = (RGNDATA *)buffer;
1924 if (FAILED(GetThemeBool(theme, part, state, TMT_TRANSPARENT, &istrans)) || !istrans) {
1925 *rgn = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
1926 return S_OK;
1929 r = *rect;
1930 OffsetRect(&r, -r.left, -r.top);
1932 if (FAILED(GetThemeColor(theme, part, state, TMT_TRANSPARENTCOLOR, &transcolour)))
1933 transcolour = RGB(255, 0, 255); /* defaults to magenta */
1935 dc = CreateCompatibleDC(NULL);
1936 if (!dc) {
1937 WARN("CreateCompatibleDC failed\n");
1938 return E_FAIL;
1941 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1942 bitmapinfo.bmiHeader.biWidth = rect->right - rect->left;
1943 bitmapinfo.bmiHeader.biHeight = -(rect->bottom - rect->top);
1944 bitmapinfo.bmiHeader.biPlanes = 1;
1945 bitmapinfo.bmiHeader.biBitCount = 32;
1946 bitmapinfo.bmiHeader.biCompression = BI_RGB;
1947 bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * 4;
1948 bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
1949 bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
1950 bitmapinfo.bmiHeader.biClrUsed = 0;
1951 bitmapinfo.bmiHeader.biClrImportant = 0;
1953 bmp = CreateDIBSection(dc, &bitmapinfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1954 if (!bmp) {
1955 WARN("CreateDIBSection failed\n");
1956 DeleteDC(dc);
1957 return E_FAIL;
1960 SelectObject(dc, bmp);
1962 transbrush = CreateSolidBrush(transcolour);
1963 FillRect(dc, &r, transbrush);
1964 DeleteObject(transbrush);
1966 if (FAILED(DrawThemeBackground(theme, dc, part, state, &r, NULL))) {
1967 WARN("DrawThemeBackground failed\n");
1968 DeleteObject(bmp);
1969 DeleteDC(dc);
1970 return E_FAIL;
1973 data->rdh.dwSize = sizeof(data->rdh);
1974 data->rdh.iType = RDH_RECTANGLES;
1975 data->rdh.nCount = 0;
1976 data->rdh.nRgnSize = sizeof(buffer) - sizeof(data->rdh);
1978 hrgn = CreateRectRgn(0, 0, 0, 0);
1980 for (y = 0; y < r.bottom; y++, bits += r.right) {
1981 x = 0;
1982 while (x < r.right) {
1983 while (x < r.right && (bits[x] & 0xffffff) == transcolour) x++;
1984 start = x;
1985 while (x < r.right && !((bits[x] & 0xffffff) == transcolour)) x++;
1986 add_row( hrgn, data, rect->left + start, rect->top + y, x - start );
1990 if (data->rdh.nCount > 0) flush_rgn_data(hrgn, data);
1992 *rgn = hrgn;
1994 DeleteObject(bmp);
1995 DeleteDC(dc);
1997 return S_OK;
2000 /***********************************************************************
2001 * GetThemeBackgroundRegion (UXTHEME.@)
2003 * Calculate the background region, taking into consideration transparent areas
2004 * of the background image.
2006 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
2007 int iStateId, const RECT *pRect,
2008 HRGN *pRegion)
2010 HRESULT hr = S_OK;
2011 int bgtype = BT_BORDERFILL;
2013 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
2014 if(!hTheme)
2015 return E_HANDLE;
2016 if(!pRect || !pRegion)
2017 return E_POINTER;
2019 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2020 if(bgtype == BT_IMAGEFILE) {
2021 hr = create_image_bg_region(hTheme, iPartId, iStateId, pRect, pRegion);
2023 else if(bgtype == BT_BORDERFILL) {
2024 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
2025 if(!*pRegion)
2026 hr = HRESULT_FROM_WIN32(GetLastError());
2028 else {
2029 FIXME("Unknown background type\n");
2030 /* This should never happen, and hence I don't know what to return */
2031 hr = E_FAIL;
2033 return hr;
2036 /* compute part size for "borderfill" backgrounds */
2037 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
2038 int iStateId, THEMESIZE eSize, POINT* psz)
2040 HRESULT hr = S_OK;
2041 int bordersize = 1;
2043 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
2044 &bordersize)))
2046 psz->x = psz->y = 2*bordersize;
2047 if (eSize != TS_MIN)
2049 psz->x++;
2050 psz->y++;
2053 return hr;
2056 /***********************************************************************
2057 * GetThemePartSize (UXTHEME.@)
2059 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
2060 int iStateId, RECT *prc, THEMESIZE eSize,
2061 SIZE *psz)
2063 int bgtype = BT_BORDERFILL;
2064 HRESULT hr = S_OK;
2065 POINT size = {1, 1};
2067 if(!hTheme)
2068 return E_HANDLE;
2070 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2071 if (bgtype == BT_NONE)
2072 /* do nothing */;
2073 else if(bgtype == BT_IMAGEFILE)
2074 hr = get_image_part_size(hTheme, iPartId, iStateId, prc, eSize, &size);
2075 else if(bgtype == BT_BORDERFILL)
2076 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
2077 else {
2078 FIXME("Unknown background type\n");
2079 /* This should never happen, and hence I don't know what to return */
2080 hr = E_FAIL;
2082 psz->cx = size.x;
2083 psz->cy = size.y;
2084 return hr;
2088 /***********************************************************************
2089 * GetThemeTextExtent (UXTHEME.@)
2091 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2092 int iStateId, LPCWSTR pszText, int iCharCount,
2093 DWORD dwTextFlags, const RECT *pBoundingRect,
2094 RECT *pExtentRect)
2096 HRESULT hr;
2097 HFONT hFont = NULL;
2098 HGDIOBJ oldFont = NULL;
2099 LOGFONTW logfont;
2100 RECT rt = {0,0,0xFFFF,0xFFFF};
2102 TRACE("%d %d\n", iPartId, iStateId);
2103 if(!hTheme)
2104 return E_HANDLE;
2106 if(pBoundingRect)
2107 rt = *pBoundingRect;
2109 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2110 if(SUCCEEDED(hr)) {
2111 hFont = CreateFontIndirectW(&logfont);
2112 if(!hFont)
2113 TRACE("Failed to create font\n");
2115 if(hFont)
2116 oldFont = SelectObject(hdc, hFont);
2118 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2119 *pExtentRect = rt;
2121 if(hFont) {
2122 SelectObject(hdc, oldFont);
2123 DeleteObject(hFont);
2125 return S_OK;
2128 /***********************************************************************
2129 * GetThemeTextMetrics (UXTHEME.@)
2131 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2132 int iStateId, TEXTMETRICW *ptm)
2134 HRESULT hr;
2135 HFONT hFont = NULL;
2136 HGDIOBJ oldFont = NULL;
2137 LOGFONTW logfont;
2139 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2140 if(!hTheme)
2141 return E_HANDLE;
2143 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2144 if(SUCCEEDED(hr)) {
2145 hFont = CreateFontIndirectW(&logfont);
2146 if(!hFont)
2147 TRACE("Failed to create font\n");
2149 if(hFont)
2150 oldFont = SelectObject(hdc, hFont);
2152 if(!GetTextMetricsW(hdc, ptm))
2153 hr = HRESULT_FROM_WIN32(GetLastError());
2155 if(hFont) {
2156 SelectObject(hdc, oldFont);
2157 DeleteObject(hFont);
2159 return hr;
2162 /***********************************************************************
2163 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2165 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2166 int iStateId)
2168 int bgtype = BT_BORDERFILL;
2169 RECT rect = {0, 0, 0, 0};
2170 HBITMAP bmpSrc;
2171 RECT rcSrc;
2172 BOOL hasAlpha;
2173 INT transparent;
2174 COLORREF transparentcolor;
2176 TRACE("(%d,%d)\n", iPartId, iStateId);
2178 if(!hTheme)
2179 return FALSE;
2181 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2183 if (bgtype != BT_IMAGEFILE) return FALSE;
2185 if (FAILED(UXTHEME_LoadImage(hTheme, iPartId, iStateId, &rect, FALSE, &bmpSrc, &rcSrc,
2186 &hasAlpha, NULL)))
2187 return FALSE;
2189 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2190 &transparentcolor, FALSE);
2191 return (transparent != ALPHABLEND_NONE);