sapi: Add SpMMAudioOut stub.
[wine.git] / dlls / uxtheme / draw.c
blob3286d70d4d15d69b0a75e4edaeb161b688e86943
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 /* Reset alpha values in hdc to 0xFF if the background is opaque */
527 static void reset_dc_alpha_values(HTHEME htheme, HDC hdc, int part_id, int state_id,
528 const RECT *rect)
530 static const RGBQUAD bitmap_bits = {0x0, 0x0, 0x0, 0xFF};
531 BITMAPINFO bitmap_info = {{0}};
532 RECT image_rect;
533 BOOL has_alpha;
534 HBITMAP hbmp;
535 int bg_type;
537 if (GetDeviceCaps(hdc, BITSPIXEL) != 32)
538 return;
540 if (FAILED(GetThemeEnumValue(htheme, part_id, state_id, TMT_BGTYPE, &bg_type))
541 || bg_type != BT_IMAGEFILE)
542 return;
544 if (FAILED(UXTHEME_LoadImage(htheme, part_id, state_id, rect, FALSE, &hbmp, &image_rect,
545 &has_alpha, NULL)) || has_alpha)
546 return;
548 bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
549 bitmap_info.bmiHeader.biWidth = 1;
550 bitmap_info.bmiHeader.biHeight = 1;
551 bitmap_info.bmiHeader.biPlanes = 1;
552 bitmap_info.bmiHeader.biBitCount = 32;
553 bitmap_info.bmiHeader.biCompression = BI_RGB;
554 StretchDIBits(hdc, rect->left, rect->top, abs(rect->right - rect->left),
555 abs(rect->bottom - rect->top), 0, 0, 1, 1, &bitmap_bits, &bitmap_info,
556 DIB_RGB_COLORS, SRCPAINT);
559 /***********************************************************************
560 * UXTHEME_DrawImageGlyph
562 * Draw an imagefile glyph
564 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
565 int iStateId, RECT *pRect,
566 const DTBGOPTS *pOptions)
568 HRESULT hr;
569 HBITMAP bmpSrc = NULL;
570 HDC hdcSrc = NULL;
571 HGDIOBJ oldSrc = NULL;
572 RECT rcSrc;
573 INT transparent = 0;
574 COLORREF transparentcolor;
575 int valign = VA_CENTER;
576 int halign = HA_CENTER;
577 POINT dstSize;
578 POINT srcSize;
579 POINT topleft;
580 BOOL hasAlpha;
582 hr = UXTHEME_LoadImage(hTheme, iPartId, iStateId, pRect, TRUE, &bmpSrc, &rcSrc, &hasAlpha,
583 NULL);
584 if(FAILED(hr)) return hr;
585 hdcSrc = CreateCompatibleDC(hdc);
586 if(!hdcSrc) {
587 hr = HRESULT_FROM_WIN32(GetLastError());
588 return hr;
590 oldSrc = SelectObject(hdcSrc, bmpSrc);
592 dstSize.x = pRect->right-pRect->left;
593 dstSize.y = pRect->bottom-pRect->top;
594 srcSize.x = rcSrc.right-rcSrc.left;
595 srcSize.y = rcSrc.bottom-rcSrc.top;
597 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
598 &transparentcolor, TRUE);
599 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
600 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
602 topleft.x = pRect->left;
603 topleft.y = pRect->top;
604 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
605 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
606 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
607 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
609 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
610 hdcSrc, rcSrc.left, rcSrc.top,
611 transparent, transparentcolor)) {
612 hr = HRESULT_FROM_WIN32(GetLastError());
615 SelectObject(hdcSrc, oldSrc);
616 DeleteDC(hdcSrc);
618 /* Don't transfer alpha values from the glyph when drawing opaque background */
619 if (SUCCEEDED(hr) && hasAlpha)
620 reset_dc_alpha_values(hTheme, hdc, iPartId, iStateId, pRect);
622 return hr;
625 /***********************************************************************
626 * UXTHEME_DrawGlyph
628 * Draw glyph on top of background, if appropriate
630 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
631 int iStateId, RECT *pRect,
632 const DTBGOPTS *pOptions)
634 int glyphtype = GT_NONE;
636 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
638 if(glyphtype == GT_IMAGEGLYPH) {
639 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
641 else if(glyphtype == GT_FONTGLYPH) {
642 /* I don't know what a font glyph is, I've never seen it used in any themes */
643 FIXME("Font glyph\n");
645 return S_OK;
648 /***********************************************************************
649 * get_image_part_size
651 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
653 static HRESULT get_image_part_size(HTHEME hTheme, int iPartId, int iStateId, RECT *prc,
654 THEMESIZE eSize, POINT *psz)
656 int imageDpi, dstDpi;
657 HRESULT hr = S_OK;
658 HBITMAP bmpSrc;
659 RECT rcSrc;
660 BOOL hasAlpha;
662 hr = UXTHEME_LoadImage(hTheme, iPartId, iStateId, prc, FALSE, &bmpSrc, &rcSrc, &hasAlpha,
663 &imageDpi);
664 if (FAILED(hr)) return hr;
666 switch (eSize)
668 case TS_DRAW:
670 int sizingType = ST_STRETCH, scalingType = TSST_NONE, stretchMark = 0;
671 POINT srcSize;
673 srcSize.x = rcSrc.right - rcSrc.left;
674 srcSize.y = rcSrc.bottom - rcSrc.top;
676 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingType);
677 if (sizingType == ST_TRUESIZE)
679 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_TRUESIZESCALINGTYPE, &scalingType);
680 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &stretchMark);
681 if (scalingType == TSST_DPI)
683 /* Scale to DPI only if the destination DPI exceeds the source DPI by
684 * stretchMark percent */
685 dstDpi = MSSTYLES_GetThemeDPI(hTheme);
686 if (dstDpi != imageDpi && MulDiv(100, dstDpi, imageDpi) >= stretchMark + 100)
688 srcSize.x = MulDiv(srcSize.x, dstDpi, imageDpi);
689 srcSize.y = MulDiv(srcSize.y, dstDpi, imageDpi);
693 *psz = srcSize;
695 if (prc != NULL)
697 POINT dstSize;
698 BOOL uniformsizing = FALSE;
700 dstSize.x = prc->right - prc->left;
701 dstSize.y = prc->bottom - prc->top;
703 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
704 if(uniformsizing) {
705 /* Scale height and width equally */
706 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
707 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
708 else
709 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
712 if (sizingType == ST_TRUESIZE)
714 if ((dstSize.x < 0 || dstSize.y < 0)
715 || (dstSize.x < srcSize.x && dstSize.y < srcSize.y)
716 || (scalingType == TSST_SIZE
717 && MulDiv(100, dstSize.x, srcSize.x) >= stretchMark + 100
718 && MulDiv(100, dstSize.y, srcSize.y) >= stretchMark + 100))
720 *psz = dstSize;
723 else
725 psz->x = abs(dstSize.x);
726 psz->y = abs(dstSize.y);
730 break;
732 case TS_MIN:
733 /* FIXME: couldn't figure how native uxtheme computes min size */
734 case TS_TRUE:
735 psz->x = rcSrc.right - rcSrc.left;
736 psz->y = rcSrc.bottom - rcSrc.top;
737 break;
739 return hr;
742 /***********************************************************************
743 * UXTHEME_DrawImageBackground
745 * Draw an imagefile background
747 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
748 int iStateId, RECT *pRect,
749 const DTBGOPTS *pOptions)
751 int destCenterWidth, srcCenterWidth, destCenterHeight, srcCenterHeight;
752 HRESULT hr = S_OK;
753 HBITMAP bmpSrc;
754 HGDIOBJ oldSrc;
755 HDC hdcSrc;
756 RECT rcSrc;
757 RECT rcDst;
758 POINT dstSize;
759 POINT srcSize;
760 POINT drawSize;
761 int sizingtype = ST_STRETCH;
762 INT transparent;
763 COLORREF transparentcolor = 0;
764 BOOL hasAlpha;
766 hr = UXTHEME_LoadImage(hTheme, iPartId, iStateId, pRect, FALSE, &bmpSrc, &rcSrc, &hasAlpha,
767 NULL);
768 if(FAILED(hr)) return hr;
769 hdcSrc = CreateCompatibleDC(hdc);
770 if(!hdcSrc) {
771 hr = HRESULT_FROM_WIN32(GetLastError());
772 return hr;
774 oldSrc = SelectObject(hdcSrc, bmpSrc);
776 rcDst = *pRect;
778 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
779 &transparentcolor, FALSE);
781 dstSize.x = rcDst.right-rcDst.left;
782 dstSize.y = rcDst.bottom-rcDst.top;
783 srcSize.x = rcSrc.right-rcSrc.left;
784 srcSize.y = rcSrc.bottom-rcSrc.top;
786 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
787 if(sizingtype == ST_TRUESIZE) {
788 int valign = VA_CENTER, halign = HA_CENTER;
790 get_image_part_size(hTheme, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
791 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
792 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
794 if (halign == HA_CENTER)
795 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
796 else if (halign == HA_RIGHT)
797 rcDst.left = rcDst.right - drawSize.x;
798 if (valign == VA_CENTER)
799 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
800 else if (valign == VA_BOTTOM)
801 rcDst.top = rcDst.bottom - drawSize.y;
802 rcDst.right = rcDst.left + drawSize.x;
803 rcDst.bottom = rcDst.top + drawSize.y;
804 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
805 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
806 transparent, transparentcolor))
807 hr = HRESULT_FROM_WIN32(GetLastError());
809 else {
810 HDC hdcDst = NULL;
811 MARGINS sm;
812 POINT org;
814 dstSize.x = abs(dstSize.x);
815 dstSize.y = abs(dstSize.y);
817 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
819 /* Resize sizing margins if destination is smaller */
820 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
821 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
822 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
823 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
826 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
827 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
828 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
832 hdcDst = hdc;
833 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
835 /* Upper left corner */
836 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
837 hdcSrc, rcSrc.left, rcSrc.top,
838 transparent, transparentcolor)) {
839 hr = HRESULT_FROM_WIN32(GetLastError());
840 goto draw_error;
842 /* Upper right corner */
843 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
844 sm.cxRightWidth, sm.cyTopHeight,
845 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
846 transparent, transparentcolor)) {
847 hr = HRESULT_FROM_WIN32(GetLastError());
848 goto draw_error;
850 /* Lower left corner */
851 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
852 sm.cxLeftWidth, sm.cyBottomHeight,
853 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
854 transparent, transparentcolor)) {
855 hr = HRESULT_FROM_WIN32(GetLastError());
856 goto draw_error;
858 /* Lower right corner */
859 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
860 sm.cxRightWidth, sm.cyBottomHeight,
861 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
862 transparent, transparentcolor)) {
863 hr = HRESULT_FROM_WIN32(GetLastError());
864 goto draw_error;
867 destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
868 srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
869 destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
870 srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
872 if(destCenterWidth > 0) {
873 /* Center top */
874 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
875 destCenterWidth, sm.cyTopHeight,
876 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
877 srcCenterWidth, sm.cyTopHeight,
878 sizingtype, transparent, transparentcolor)) {
879 hr = HRESULT_FROM_WIN32(GetLastError());
880 goto draw_error;
882 /* Center bottom */
883 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
884 destCenterWidth, sm.cyBottomHeight,
885 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
886 srcCenterWidth, sm.cyBottomHeight,
887 sizingtype, transparent, transparentcolor)) {
888 hr = HRESULT_FROM_WIN32(GetLastError());
889 goto draw_error;
892 if(destCenterHeight > 0) {
893 /* Left center */
894 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
895 sm.cxLeftWidth, destCenterHeight,
896 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
897 sm.cxLeftWidth, srcCenterHeight,
898 sizingtype,
899 transparent, transparentcolor)) {
900 hr = HRESULT_FROM_WIN32(GetLastError());
901 goto draw_error;
903 /* Right center */
904 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
905 sm.cxRightWidth, destCenterHeight,
906 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
907 sm.cxRightWidth, srcCenterHeight,
908 sizingtype, transparent, transparentcolor)) {
909 hr = HRESULT_FROM_WIN32(GetLastError());
910 goto draw_error;
913 if(destCenterHeight > 0 && destCenterWidth > 0) {
914 BOOL borderonly = FALSE;
915 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
916 if(!borderonly) {
917 /* Center */
918 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
919 destCenterWidth, destCenterHeight,
920 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
921 srcCenterWidth, srcCenterHeight,
922 sizingtype, transparent, transparentcolor)) {
923 hr = HRESULT_FROM_WIN32(GetLastError());
924 goto draw_error;
929 draw_error:
930 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
932 SelectObject(hdcSrc, oldSrc);
933 DeleteDC(hdcSrc);
934 *pRect = rcDst;
935 return hr;
938 /***********************************************************************
939 * UXTHEME_DrawBorderRectangle
941 * Draw the bounding rectangle for a borderfill background
943 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
944 int iStateId, RECT *pRect,
945 const DTBGOPTS *pOptions)
947 HRESULT hr = S_OK;
948 HPEN hPen;
949 HGDIOBJ oldPen;
950 COLORREF bordercolor = RGB(0,0,0);
951 int bordersize = 1;
953 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
954 if(bordersize > 0) {
955 POINT ptCorners[5];
956 ptCorners[0].x = pRect->left;
957 ptCorners[0].y = pRect->top;
958 ptCorners[1].x = pRect->right-1;
959 ptCorners[1].y = pRect->top;
960 ptCorners[2].x = pRect->right-1;
961 ptCorners[2].y = pRect->bottom-1;
962 ptCorners[3].x = pRect->left;
963 ptCorners[3].y = pRect->bottom-1;
964 ptCorners[4].x = pRect->left;
965 ptCorners[4].y = pRect->top;
967 InflateRect(pRect, -bordersize, -bordersize);
968 if(pOptions->dwFlags & DTBG_OMITBORDER)
969 return S_OK;
970 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
971 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
972 if(!hPen)
973 return HRESULT_FROM_WIN32(GetLastError());
974 oldPen = SelectObject(hdc, hPen);
976 if(!Polyline(hdc, ptCorners, 5))
977 hr = HRESULT_FROM_WIN32(GetLastError());
979 SelectObject(hdc, oldPen);
980 DeleteObject(hPen);
982 return hr;
985 /***********************************************************************
986 * UXTHEME_DrawBackgroundFill
988 * Fill a borderfill background rectangle
990 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
991 int iStateId, RECT *pRect,
992 const DTBGOPTS *pOptions)
994 HRESULT hr = S_OK;
995 int filltype = FT_SOLID;
997 TRACE("(%d,%d,%ld)\n", iPartId, iStateId, pOptions->dwFlags);
999 if(pOptions->dwFlags & DTBG_OMITCONTENT)
1000 return S_OK;
1002 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
1004 if(filltype == FT_SOLID) {
1005 HBRUSH hBrush;
1006 COLORREF fillcolor = RGB(255,255,255);
1008 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
1009 hBrush = CreateSolidBrush(fillcolor);
1010 if(!FillRect(hdc, pRect, hBrush))
1011 hr = HRESULT_FROM_WIN32(GetLastError());
1012 DeleteObject(hBrush);
1014 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
1015 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
1016 the gradient ratios (no idea how those work)
1017 Few themes use this, and the ones I've seen only use 2 colors with
1018 a gradient ratio of 0 and 255 respectively
1021 COLORREF gradient1 = RGB(0,0,0);
1022 COLORREF gradient2 = RGB(255,255,255);
1023 TRIVERTEX vert[2];
1024 GRADIENT_RECT gRect;
1026 FIXME("Gradient implementation not complete\n");
1028 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
1029 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
1031 vert[0].x = pRect->left;
1032 vert[0].y = pRect->top;
1033 vert[0].Red = GetRValue(gradient1) << 8;
1034 vert[0].Green = GetGValue(gradient1) << 8;
1035 vert[0].Blue = GetBValue(gradient1) << 8;
1036 vert[0].Alpha = 0xff00;
1038 vert[1].x = pRect->right;
1039 vert[1].y = pRect->bottom;
1040 vert[1].Red = GetRValue(gradient2) << 8;
1041 vert[1].Green = GetGValue(gradient2) << 8;
1042 vert[1].Blue = GetBValue(gradient2) << 8;
1043 vert[1].Alpha = 0xff00;
1045 gRect.UpperLeft = 0;
1046 gRect.LowerRight = 1;
1047 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
1049 else if(filltype == FT_RADIALGRADIENT) {
1050 /* I've never seen this used in a theme */
1051 FIXME("Radial gradient\n");
1053 else if(filltype == FT_TILEIMAGE) {
1054 /* I've never seen this used in a theme */
1055 FIXME("Tile image\n");
1057 return hr;
1060 /***********************************************************************
1061 * UXTHEME_DrawBorderBackground
1063 * Draw an imagefile background
1065 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
1066 int iStateId, const RECT *pRect,
1067 const DTBGOPTS *pOptions)
1069 HRESULT hr;
1070 RECT rt;
1072 rt = *pRect;
1074 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1075 if(FAILED(hr))
1076 return hr;
1077 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1080 /***********************************************************************
1081 * DrawThemeBackgroundEx (UXTHEME.@)
1083 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1084 int iStateId, const RECT *pRect,
1085 const DTBGOPTS *pOptions)
1087 HRESULT hr;
1088 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1089 const DTBGOPTS *opts;
1090 HRGN clip = NULL;
1091 int hasClip = -1;
1092 int bgtype = BT_BORDERFILL;
1093 RECT rt;
1095 TRACE("(%p,%p,%d,%d,%ld,%ld)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1096 if(!hTheme)
1097 return E_HANDLE;
1099 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1100 if (bgtype == BT_NONE) return S_OK;
1102 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1103 opts = pOptions;
1104 if(!opts) opts = &defaultOpts;
1106 if(opts->dwFlags & DTBG_CLIPRECT) {
1107 clip = CreateRectRgn(0,0,1,1);
1108 hasClip = GetClipRgn(hdc, clip);
1109 if(hasClip == -1)
1110 TRACE("Failed to get original clipping region\n");
1111 else
1112 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1114 rt = *pRect;
1116 if(bgtype == BT_IMAGEFILE)
1117 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1118 else if(bgtype == BT_BORDERFILL)
1119 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1120 else {
1121 FIXME("Unknown background type\n");
1122 /* This should never happen, and hence I don't know what to return */
1123 hr = E_FAIL;
1125 if(SUCCEEDED(hr))
1126 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1127 if(opts->dwFlags & DTBG_CLIPRECT) {
1128 if(hasClip == 0)
1129 SelectClipRgn(hdc, NULL);
1130 else if(hasClip == 1)
1131 SelectClipRgn(hdc, clip);
1132 DeleteObject(clip);
1134 return hr;
1138 * DrawThemeEdge() implementation
1140 * Since it basically is DrawEdge() with different colors, I copied its code
1141 * from user32's uitools.c.
1144 enum
1146 EDGE_LIGHT,
1147 EDGE_HIGHLIGHT,
1148 EDGE_SHADOW,
1149 EDGE_DARKSHADOW,
1150 EDGE_FILL,
1152 EDGE_WINDOW,
1153 EDGE_WINDOWFRAME,
1155 EDGE_NUMCOLORS
1158 static const struct
1160 int themeProp;
1161 int sysColor;
1162 } EdgeColorMap[EDGE_NUMCOLORS] = {
1163 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1164 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1165 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1166 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1167 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1168 {-1, COLOR_WINDOW},
1169 {-1, COLOR_WINDOWFRAME}
1172 static const signed char LTInnerNormal[] = {
1173 -1, -1, -1, -1,
1174 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1175 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1176 -1, -1, -1, -1
1179 static const signed char LTOuterNormal[] = {
1180 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1181 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1182 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1183 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1186 static const signed char RBInnerNormal[] = {
1187 -1, -1, -1, -1,
1188 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1189 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1190 -1, -1, -1, -1
1193 static const signed char RBOuterNormal[] = {
1194 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1195 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1196 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1197 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1200 static const signed char LTInnerSoft[] = {
1201 -1, -1, -1, -1,
1202 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1203 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1204 -1, -1, -1, -1
1207 static const signed char LTOuterSoft[] = {
1208 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1209 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1210 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1211 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1214 #define RBInnerSoft RBInnerNormal /* These are the same */
1215 #define RBOuterSoft RBOuterNormal
1217 static const signed char LTRBOuterMono[] = {
1218 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1219 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1220 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1221 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1224 static const signed char LTRBInnerMono[] = {
1225 -1, -1, -1, -1,
1226 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1227 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1228 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1231 static const signed char LTRBOuterFlat[] = {
1232 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1233 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1234 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1235 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1238 static const signed char LTRBInnerFlat[] = {
1239 -1, -1, -1, -1,
1240 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1241 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1242 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1245 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1247 COLORREF col;
1248 if ((EdgeColorMap[edgeType].themeProp == -1)
1249 || FAILED (GetThemeColor (theme, part, state,
1250 EdgeColorMap[edgeType].themeProp, &col)))
1251 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1252 return col;
1255 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1257 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1260 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1262 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1265 /***********************************************************************
1266 * draw_diag_edge
1268 * Same as DrawEdge invoked with BF_DIAGONAL
1270 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1271 const RECT* rc, UINT uType,
1272 UINT uFlags, LPRECT contentsRect)
1274 POINT Points[4];
1275 signed char InnerI, OuterI;
1276 HPEN InnerPen, OuterPen;
1277 POINT SavePoint;
1278 HPEN SavePen;
1279 int spx, spy;
1280 int epx, epy;
1281 int Width = rc->right - rc->left;
1282 int Height= rc->bottom - rc->top;
1283 int SmallDiam = Width > Height ? Height : Width;
1284 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1285 || (uType & BDR_OUTER) == BDR_OUTER)
1286 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1287 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1288 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1290 /* Init some vars */
1291 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1292 SavePen = SelectObject(hdc, InnerPen);
1293 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1295 /* Determine the colors of the edges */
1296 if(uFlags & BF_MONO)
1298 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1299 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1301 else if(uFlags & BF_FLAT)
1303 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1304 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1306 else if(uFlags & BF_SOFT)
1308 if(uFlags & BF_BOTTOM)
1310 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1311 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1313 else
1315 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1316 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1319 else
1321 if(uFlags & BF_BOTTOM)
1323 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1324 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1326 else
1328 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1329 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1333 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1334 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1336 MoveToEx(hdc, 0, 0, &SavePoint);
1338 /* Don't ask me why, but this is what is visible... */
1339 /* This must be possible to do much simpler, but I fail to */
1340 /* see the logic in the MS implementation (sigh...). */
1341 /* So, this might look a bit brute force here (and it is), but */
1342 /* it gets the job done;) */
1344 switch(uFlags & BF_RECT)
1346 case 0:
1347 case BF_LEFT:
1348 case BF_BOTTOM:
1349 case BF_BOTTOMLEFT:
1350 /* Left bottom endpoint */
1351 epx = rc->left-1;
1352 spx = epx + SmallDiam;
1353 epy = rc->bottom;
1354 spy = epy - SmallDiam;
1355 break;
1357 case BF_TOPLEFT:
1358 case BF_BOTTOMRIGHT:
1359 /* Left top endpoint */
1360 epx = rc->left-1;
1361 spx = epx + SmallDiam;
1362 epy = rc->top-1;
1363 spy = epy + SmallDiam;
1364 break;
1366 case BF_TOP:
1367 case BF_RIGHT:
1368 case BF_TOPRIGHT:
1369 case BF_RIGHT|BF_LEFT:
1370 case BF_RIGHT|BF_LEFT|BF_TOP:
1371 case BF_BOTTOM|BF_TOP:
1372 case BF_BOTTOM|BF_TOP|BF_LEFT:
1373 case BF_BOTTOMRIGHT|BF_LEFT:
1374 case BF_BOTTOMRIGHT|BF_TOP:
1375 case BF_RECT:
1376 /* Right top endpoint */
1377 spx = rc->left;
1378 epx = spx + SmallDiam;
1379 spy = rc->bottom-1;
1380 epy = spy - SmallDiam;
1381 break;
1384 MoveToEx(hdc, spx, spy, NULL);
1385 SelectObject(hdc, OuterPen);
1386 LineTo(hdc, epx, epy);
1388 SelectObject(hdc, InnerPen);
1390 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1392 case BF_DIAGONAL_ENDBOTTOMLEFT:
1393 case (BF_DIAGONAL|BF_BOTTOM):
1394 case BF_DIAGONAL:
1395 case (BF_DIAGONAL|BF_LEFT):
1396 MoveToEx(hdc, spx-1, spy, NULL);
1397 LineTo(hdc, epx, epy-1);
1398 Points[0].x = spx-add;
1399 Points[0].y = spy;
1400 Points[1].x = rc->left;
1401 Points[1].y = rc->top;
1402 Points[2].x = epx+1;
1403 Points[2].y = epy-1-add;
1404 Points[3] = Points[2];
1405 break;
1407 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1408 MoveToEx(hdc, spx-1, spy, NULL);
1409 LineTo(hdc, epx, epy+1);
1410 Points[0].x = spx-add;
1411 Points[0].y = spy;
1412 Points[1].x = rc->left;
1413 Points[1].y = rc->bottom-1;
1414 Points[2].x = epx+1;
1415 Points[2].y = epy+1+add;
1416 Points[3] = Points[2];
1417 break;
1419 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1420 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1421 case BF_DIAGONAL_ENDTOPRIGHT:
1422 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1423 MoveToEx(hdc, spx+1, spy, NULL);
1424 LineTo(hdc, epx, epy+1);
1425 Points[0].x = epx-1;
1426 Points[0].y = epy+1+add;
1427 Points[1].x = rc->right-1;
1428 Points[1].y = rc->top+add;
1429 Points[2].x = rc->right-1;
1430 Points[2].y = rc->bottom-1;
1431 Points[3].x = spx+add;
1432 Points[3].y = spy;
1433 break;
1435 case BF_DIAGONAL_ENDTOPLEFT:
1436 MoveToEx(hdc, spx, spy-1, NULL);
1437 LineTo(hdc, epx+1, epy);
1438 Points[0].x = epx+1+add;
1439 Points[0].y = epy+1;
1440 Points[1].x = rc->right-1;
1441 Points[1].y = rc->top;
1442 Points[2].x = rc->right-1;
1443 Points[2].y = rc->bottom-1-add;
1444 Points[3].x = spx;
1445 Points[3].y = spy-add;
1446 break;
1448 case (BF_DIAGONAL|BF_TOP):
1449 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1450 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1451 MoveToEx(hdc, spx+1, spy-1, NULL);
1452 LineTo(hdc, epx, epy);
1453 Points[0].x = epx-1;
1454 Points[0].y = epy+1;
1455 Points[1].x = rc->right-1;
1456 Points[1].y = rc->top;
1457 Points[2].x = rc->right-1;
1458 Points[2].y = rc->bottom-1-add;
1459 Points[3].x = spx+add;
1460 Points[3].y = spy-add;
1461 break;
1463 case (BF_DIAGONAL|BF_RIGHT):
1464 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1465 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1466 MoveToEx(hdc, spx, spy, NULL);
1467 LineTo(hdc, epx-1, epy+1);
1468 Points[0].x = spx;
1469 Points[0].y = spy;
1470 Points[1].x = rc->left;
1471 Points[1].y = rc->top+add;
1472 Points[2].x = epx-1-add;
1473 Points[2].y = epy+1+add;
1474 Points[3] = Points[2];
1475 break;
1478 /* Fill the interior if asked */
1479 if((uFlags & BF_MIDDLE) && retval)
1481 HBRUSH hbsave;
1482 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1483 theme, part, state);
1484 HPEN hpsave;
1485 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1486 theme, part, state);
1487 hbsave = SelectObject(hdc, hb);
1488 hpsave = SelectObject(hdc, hp);
1489 Polygon(hdc, Points, 4);
1490 SelectObject(hdc, hbsave);
1491 SelectObject(hdc, hpsave);
1492 DeleteObject (hp);
1493 DeleteObject (hb);
1496 /* Adjust rectangle if asked */
1497 if(uFlags & BF_ADJUST)
1499 *contentsRect = *rc;
1500 if(uFlags & BF_LEFT) contentsRect->left += add;
1501 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1502 if(uFlags & BF_TOP) contentsRect->top += add;
1503 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1506 /* Cleanup */
1507 SelectObject(hdc, SavePen);
1508 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1509 if(InnerI != -1) DeleteObject (InnerPen);
1510 if(OuterI != -1) DeleteObject (OuterPen);
1512 return retval;
1515 /***********************************************************************
1516 * draw_rect_edge
1518 * Same as DrawEdge invoked without BF_DIAGONAL
1520 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1521 const RECT* rc, UINT uType,
1522 UINT uFlags, LPRECT contentsRect)
1524 signed char LTInnerI, LTOuterI;
1525 signed char RBInnerI, RBOuterI;
1526 HPEN LTInnerPen, LTOuterPen;
1527 HPEN RBInnerPen, RBOuterPen;
1528 RECT InnerRect = *rc;
1529 POINT SavePoint;
1530 HPEN SavePen;
1531 int LBpenplus = 0;
1532 int LTpenplus = 0;
1533 int RTpenplus = 0;
1534 int RBpenplus = 0;
1535 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1536 || (uType & BDR_OUTER) == BDR_OUTER)
1537 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1539 /* Init some vars */
1540 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1541 SavePen = SelectObject(hdc, LTInnerPen);
1543 /* Determine the colors of the edges */
1544 if(uFlags & BF_MONO)
1546 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1547 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1549 else if(uFlags & BF_FLAT)
1551 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1552 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1554 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1556 else if(uFlags & BF_SOFT)
1558 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1559 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1560 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1561 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1563 else
1565 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1566 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1567 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1568 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1571 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1572 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1573 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1574 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1576 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1577 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1578 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1579 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1581 MoveToEx(hdc, 0, 0, &SavePoint);
1583 /* Draw the outer edge */
1584 SelectObject(hdc, LTOuterPen);
1585 if(uFlags & BF_TOP)
1587 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1588 LineTo(hdc, InnerRect.right, InnerRect.top);
1590 if(uFlags & BF_LEFT)
1592 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1593 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1595 SelectObject(hdc, RBOuterPen);
1596 if(uFlags & BF_BOTTOM)
1598 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1599 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1601 if(uFlags & BF_RIGHT)
1603 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1604 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1607 /* Draw the inner edge */
1608 SelectObject(hdc, LTInnerPen);
1609 if(uFlags & BF_TOP)
1611 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1612 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1614 if(uFlags & BF_LEFT)
1616 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1617 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1619 SelectObject(hdc, RBInnerPen);
1620 if(uFlags & BF_BOTTOM)
1622 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1623 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1625 if(uFlags & BF_RIGHT)
1627 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1628 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1631 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1633 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1634 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1636 if(uFlags & BF_LEFT) InnerRect.left += add;
1637 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1638 if(uFlags & BF_TOP) InnerRect.top += add;
1639 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1641 if((uFlags & BF_MIDDLE) && retval)
1643 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1644 theme, part, state);
1645 FillRect(hdc, &InnerRect, br);
1646 DeleteObject (br);
1649 if(uFlags & BF_ADJUST)
1650 *contentsRect = InnerRect;
1653 /* Cleanup */
1654 SelectObject(hdc, SavePen);
1655 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1656 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1657 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1658 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1659 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1660 return retval;
1664 /***********************************************************************
1665 * DrawThemeEdge (UXTHEME.@)
1667 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1668 * difference is that it does not rely on the system colors alone, but
1669 * also allows color specification in the theme.
1671 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1672 int iStateId, const RECT *pDestRect, UINT uEdge,
1673 UINT uFlags, RECT *pContentRect)
1675 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1676 if(!hTheme)
1677 return E_HANDLE;
1679 if(uFlags & BF_DIAGONAL)
1680 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1681 uEdge, uFlags, pContentRect);
1682 else
1683 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1684 uEdge, uFlags, pContentRect);
1688 /***********************************************************************
1689 * DrawThemeIcon (UXTHEME.@)
1691 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1692 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1694 INT effect = ICE_NONE, saturation = 0, alpha = 128;
1695 IImageList *image_list = (IImageList *)himl;
1696 IMAGELISTDRAWPARAMS params = {0};
1697 COLORREF color = 0;
1699 TRACE("%p %p %d %d %s %p %d\n", hTheme, hdc, iPartId, iStateId, wine_dbgstr_rect(pRect), himl,
1700 iImageIndex);
1702 if (!hTheme)
1703 return E_HANDLE;
1705 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_ICONEFFECT, &effect);
1706 switch (effect)
1708 case ICE_NONE:
1709 params.fState = ILS_NORMAL;
1710 break;
1711 case ICE_GLOW:
1712 GetThemeColor(hTheme, iPartId, iStateId, TMT_GLOWCOLOR, &color);
1713 params.fState = ILS_GLOW;
1714 params.crEffect = color;
1715 break;
1716 case ICE_SHADOW:
1717 GetThemeColor(hTheme, iPartId, iStateId, TMT_SHADOWCOLOR, &color);
1718 params.fState = ILS_SHADOW;
1719 params.crEffect = color;
1720 break;
1721 case ICE_PULSE:
1722 GetThemeInt(hTheme, iPartId, iStateId, TMT_SATURATION, &saturation);
1723 params.fState = ILS_SATURATE;
1724 params.Frame = saturation;
1725 break;
1726 case ICE_ALPHA:
1727 GetThemeInt(hTheme, iPartId, iStateId, TMT_ALPHALEVEL, &alpha);
1728 params.fState = ILS_ALPHA;
1729 params.Frame = alpha;
1730 break;
1733 params.cbSize = sizeof(params);
1734 params.himl = himl;
1735 params.i = iImageIndex;
1736 params.hdcDst = hdc;
1737 params.x = pRect->left;
1738 params.y = pRect->top;
1739 params.cx = pRect->right - pRect->left;
1740 params.cy = pRect->bottom - pRect->top;
1741 params.rgbBk = CLR_NONE;
1742 params.rgbFg = CLR_NONE;
1743 params.fStyle = ILD_TRANSPARENT;
1744 return IImageList_Draw(image_list, &params);
1747 /***********************************************************************
1748 * DrawThemeText (UXTHEME.@)
1750 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1751 LPCWSTR pszText, int iCharCount, DWORD flags,
1752 DWORD flags2, const RECT *pRect)
1754 DTTOPTS opts = { 0 };
1755 RECT rt;
1757 TRACE("%d %d\n", iPartId, iStateId);
1759 rt = *pRect;
1761 opts.dwSize = sizeof(opts);
1762 if (flags2 & DTT_GRAYED) {
1763 opts.dwFlags = DTT_TEXTCOLOR;
1764 opts.crText = GetSysColor(COLOR_GRAYTEXT);
1766 return DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, flags, &rt, &opts);
1769 /***********************************************************************
1770 * DrawThemeTextEx (UXTHEME.@)
1772 HRESULT WINAPI DrawThemeTextEx(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1773 LPCWSTR pszText, int iCharCount, DWORD flags, RECT *rect, const DTTOPTS *options)
1775 HRESULT hr;
1776 HFONT hFont = NULL;
1777 HGDIOBJ oldFont = NULL;
1778 LOGFONTW logfont;
1779 COLORREF textColor;
1780 COLORREF oldTextColor;
1781 int oldBkMode;
1782 int fontProp;
1784 TRACE("%p %p %d %d %s:%d 0x%08lx %p %p\n", hTheme, hdc, iPartId, iStateId,
1785 debugstr_wn(pszText, iCharCount), iCharCount, flags, rect, options);
1787 if(!hTheme)
1788 return E_HANDLE;
1790 if (options->dwFlags & ~(DTT_TEXTCOLOR | DTT_FONTPROP))
1791 FIXME("unsupported flags 0x%08lx\n", options->dwFlags);
1793 if (options->dwFlags & DTT_FONTPROP)
1794 fontProp = options->iFontPropId;
1795 else
1796 fontProp = TMT_FONT;
1798 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, fontProp, &logfont);
1799 if(SUCCEEDED(hr)) {
1800 hFont = CreateFontIndirectW(&logfont);
1801 if(!hFont)
1802 TRACE("Failed to create font\n");
1805 if(hFont)
1806 oldFont = SelectObject(hdc, hFont);
1808 if (options->dwFlags & DTT_TEXTCOLOR)
1809 textColor = options->crText;
1810 else {
1811 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1812 textColor = GetTextColor(hdc);
1814 oldTextColor = SetTextColor(hdc, textColor);
1815 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1816 DrawTextW(hdc, pszText, iCharCount, rect, flags);
1817 SetBkMode(hdc, oldBkMode);
1818 SetTextColor(hdc, oldTextColor);
1820 if(hFont) {
1821 SelectObject(hdc, oldFont);
1822 DeleteObject(hFont);
1824 return S_OK;
1827 /***********************************************************************
1828 * GetThemeBackgroundContentRect (UXTHEME.@)
1830 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1831 int iStateId,
1832 const RECT *pBoundingRect,
1833 RECT *pContentRect)
1835 MARGINS margin;
1836 HRESULT hr;
1838 TRACE("(%d,%d)\n", iPartId, iStateId);
1839 if(!hTheme)
1840 return E_HANDLE;
1842 /* try content margins property... */
1843 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1844 if(SUCCEEDED(hr)) {
1845 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1846 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1847 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1848 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1849 } else {
1850 /* otherwise, try to determine content rect from the background type and props */
1851 int bgtype = BT_BORDERFILL;
1852 *pContentRect = *pBoundingRect;
1854 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1855 if(bgtype == BT_BORDERFILL) {
1856 int bordersize = 1;
1858 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1859 InflateRect(pContentRect, -bordersize, -bordersize);
1860 } else if ((bgtype == BT_IMAGEFILE)
1861 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1862 TMT_SIZINGMARGINS, NULL, &margin)))) {
1863 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1864 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1865 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1866 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1868 /* If nothing was found, leave unchanged */
1871 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1873 return S_OK;
1876 /***********************************************************************
1877 * GetThemeBackgroundExtent (UXTHEME.@)
1879 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1880 int iStateId, const RECT *pContentRect,
1881 RECT *pExtentRect)
1883 MARGINS margin;
1884 HRESULT hr;
1886 TRACE("(%d,%d)\n", iPartId, iStateId);
1887 if(!hTheme)
1888 return E_HANDLE;
1890 /* try content margins property... */
1891 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1892 if(SUCCEEDED(hr)) {
1893 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1894 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1895 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1896 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1897 } else {
1898 /* otherwise, try to determine content rect from the background type and props */
1899 int bgtype = BT_BORDERFILL;
1900 *pExtentRect = *pContentRect;
1902 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1903 if(bgtype == BT_BORDERFILL) {
1904 int bordersize = 1;
1906 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1907 InflateRect(pExtentRect, bordersize, bordersize);
1908 } else if ((bgtype == BT_IMAGEFILE)
1909 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1910 TMT_SIZINGMARGINS, NULL, &margin)))) {
1911 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1912 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1913 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1914 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1916 /* If nothing was found, leave unchanged */
1919 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1921 return S_OK;
1924 static inline void flush_rgn_data( HRGN rgn, RGNDATA *data )
1926 HRGN tmp = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1928 CombineRgn( rgn, rgn, tmp, RGN_OR );
1929 DeleteObject( tmp );
1930 data->rdh.nCount = 0;
1933 static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len )
1935 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
1937 if (len <= 0) return;
1938 rect->left = x;
1939 rect->top = y;
1940 rect->right = x + len;
1941 rect->bottom = y + 1;
1942 data->rdh.nCount++;
1943 if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT))
1944 flush_rgn_data( rgn, data );
1947 static HRESULT create_image_bg_region(HTHEME theme, int part, int state, const RECT *rect, HRGN *rgn)
1949 RECT r;
1950 HDC dc;
1951 HBITMAP bmp;
1952 HRGN hrgn;
1953 BOOL istrans;
1954 COLORREF transcolour;
1955 HBRUSH transbrush;
1956 unsigned int x, y, start;
1957 BITMAPINFO bitmapinfo;
1958 DWORD *bits;
1959 char buffer[4096];
1960 RGNDATA *data = (RGNDATA *)buffer;
1962 if (FAILED(GetThemeBool(theme, part, state, TMT_TRANSPARENT, &istrans)) || !istrans) {
1963 *rgn = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
1964 return S_OK;
1967 r = *rect;
1968 OffsetRect(&r, -r.left, -r.top);
1970 if (FAILED(GetThemeColor(theme, part, state, TMT_TRANSPARENTCOLOR, &transcolour)))
1971 transcolour = RGB(255, 0, 255); /* defaults to magenta */
1973 dc = CreateCompatibleDC(NULL);
1974 if (!dc) {
1975 WARN("CreateCompatibleDC failed\n");
1976 return E_FAIL;
1979 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1980 bitmapinfo.bmiHeader.biWidth = rect->right - rect->left;
1981 bitmapinfo.bmiHeader.biHeight = -(rect->bottom - rect->top);
1982 bitmapinfo.bmiHeader.biPlanes = 1;
1983 bitmapinfo.bmiHeader.biBitCount = 32;
1984 bitmapinfo.bmiHeader.biCompression = BI_RGB;
1985 bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * 4;
1986 bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
1987 bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
1988 bitmapinfo.bmiHeader.biClrUsed = 0;
1989 bitmapinfo.bmiHeader.biClrImportant = 0;
1991 bmp = CreateDIBSection(dc, &bitmapinfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1992 if (!bmp) {
1993 WARN("CreateDIBSection failed\n");
1994 DeleteDC(dc);
1995 return E_FAIL;
1998 SelectObject(dc, bmp);
2000 transbrush = CreateSolidBrush(transcolour);
2001 FillRect(dc, &r, transbrush);
2002 DeleteObject(transbrush);
2004 if (FAILED(DrawThemeBackground(theme, dc, part, state, &r, NULL))) {
2005 WARN("DrawThemeBackground failed\n");
2006 DeleteObject(bmp);
2007 DeleteDC(dc);
2008 return E_FAIL;
2011 data->rdh.dwSize = sizeof(data->rdh);
2012 data->rdh.iType = RDH_RECTANGLES;
2013 data->rdh.nCount = 0;
2014 data->rdh.nRgnSize = sizeof(buffer) - sizeof(data->rdh);
2016 hrgn = CreateRectRgn(0, 0, 0, 0);
2018 for (y = 0; y < r.bottom; y++, bits += r.right) {
2019 x = 0;
2020 while (x < r.right) {
2021 while (x < r.right && (bits[x] & 0xffffff) == transcolour) x++;
2022 start = x;
2023 while (x < r.right && !((bits[x] & 0xffffff) == transcolour)) x++;
2024 add_row( hrgn, data, rect->left + start, rect->top + y, x - start );
2028 if (data->rdh.nCount > 0) flush_rgn_data(hrgn, data);
2030 *rgn = hrgn;
2032 DeleteObject(bmp);
2033 DeleteDC(dc);
2035 return S_OK;
2038 /***********************************************************************
2039 * GetThemeBackgroundRegion (UXTHEME.@)
2041 * Calculate the background region, taking into consideration transparent areas
2042 * of the background image.
2044 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
2045 int iStateId, const RECT *pRect,
2046 HRGN *pRegion)
2048 HRESULT hr = S_OK;
2049 int bgtype = BT_BORDERFILL;
2051 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
2052 if(!hTheme)
2053 return E_HANDLE;
2054 if(!pRect || !pRegion)
2055 return E_POINTER;
2057 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2058 if(bgtype == BT_IMAGEFILE) {
2059 hr = create_image_bg_region(hTheme, iPartId, iStateId, pRect, pRegion);
2061 else if(bgtype == BT_BORDERFILL) {
2062 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
2063 if(!*pRegion)
2064 hr = HRESULT_FROM_WIN32(GetLastError());
2066 else if (bgtype == BT_NONE)
2068 hr = E_UNEXPECTED;
2070 else {
2071 FIXME("Unknown background type %d\n", bgtype);
2072 /* This should never happen, and hence I don't know what to return */
2073 hr = E_FAIL;
2075 return hr;
2078 /* compute part size for "borderfill" backgrounds */
2079 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
2080 int iStateId, THEMESIZE eSize, POINT* psz)
2082 HRESULT hr = S_OK;
2083 int bordersize = 1;
2085 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
2086 &bordersize)))
2088 psz->x = psz->y = 2*bordersize;
2089 if (eSize != TS_MIN)
2091 psz->x++;
2092 psz->y++;
2095 return hr;
2098 /***********************************************************************
2099 * GetThemePartSize (UXTHEME.@)
2101 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
2102 int iStateId, RECT *prc, THEMESIZE eSize,
2103 SIZE *psz)
2105 int bgtype = BT_BORDERFILL;
2106 HRESULT hr = S_OK;
2107 POINT size = {1, 1};
2109 if(!hTheme)
2110 return E_HANDLE;
2112 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2113 if (bgtype == BT_NONE)
2114 /* do nothing */;
2115 else if(bgtype == BT_IMAGEFILE)
2116 hr = get_image_part_size(hTheme, iPartId, iStateId, prc, eSize, &size);
2117 else if(bgtype == BT_BORDERFILL)
2118 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
2119 else {
2120 FIXME("Unknown background type\n");
2121 /* This should never happen, and hence I don't know what to return */
2122 hr = E_FAIL;
2124 psz->cx = size.x;
2125 psz->cy = size.y;
2126 return hr;
2130 /***********************************************************************
2131 * GetThemeTextExtent (UXTHEME.@)
2133 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2134 int iStateId, LPCWSTR pszText, int iCharCount,
2135 DWORD dwTextFlags, const RECT *pBoundingRect,
2136 RECT *pExtentRect)
2138 HRESULT hr;
2139 HFONT hFont = NULL;
2140 HGDIOBJ oldFont = NULL;
2141 LOGFONTW logfont;
2142 RECT rt = {0,0,0xFFFF,0xFFFF};
2144 TRACE("%d %d\n", iPartId, iStateId);
2145 if(!hTheme)
2146 return E_HANDLE;
2148 if(pBoundingRect)
2149 rt = *pBoundingRect;
2151 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2152 if(SUCCEEDED(hr)) {
2153 hFont = CreateFontIndirectW(&logfont);
2154 if(!hFont)
2155 TRACE("Failed to create font\n");
2157 if(hFont)
2158 oldFont = SelectObject(hdc, hFont);
2160 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2161 *pExtentRect = rt;
2163 if(hFont) {
2164 SelectObject(hdc, oldFont);
2165 DeleteObject(hFont);
2167 return S_OK;
2170 /***********************************************************************
2171 * GetThemeTextMetrics (UXTHEME.@)
2173 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2174 int iStateId, TEXTMETRICW *ptm)
2176 HRESULT hr;
2177 HFONT hFont = NULL;
2178 HGDIOBJ oldFont = NULL;
2179 LOGFONTW logfont;
2181 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2182 if(!hTheme)
2183 return E_HANDLE;
2185 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2186 if(SUCCEEDED(hr)) {
2187 hFont = CreateFontIndirectW(&logfont);
2188 if(!hFont)
2189 TRACE("Failed to create font\n");
2191 if(hFont)
2192 oldFont = SelectObject(hdc, hFont);
2194 if(!GetTextMetricsW(hdc, ptm))
2195 hr = HRESULT_FROM_WIN32(GetLastError());
2197 if(hFont) {
2198 SelectObject(hdc, oldFont);
2199 DeleteObject(hFont);
2201 return hr;
2204 /***********************************************************************
2205 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2207 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2208 int iStateId)
2210 int bgtype = BT_BORDERFILL;
2211 RECT rect = {0, 0, 0, 0};
2212 HBITMAP bmpSrc;
2213 RECT rcSrc;
2214 BOOL hasAlpha;
2215 INT transparent;
2216 COLORREF transparentcolor;
2218 TRACE("(%d,%d)\n", iPartId, iStateId);
2220 if(!hTheme)
2221 return FALSE;
2223 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2225 if (bgtype != BT_IMAGEFILE) return FALSE;
2227 if (FAILED(UXTHEME_LoadImage(hTheme, iPartId, iStateId, &rect, FALSE, &bmpSrc, &rcSrc,
2228 &hasAlpha, NULL)))
2229 return FALSE;
2231 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2232 &transparentcolor, FALSE);
2233 return (transparent != ALPHABLEND_NONE);