wined3d: Validate (2D) texture dimensions in texture_init().
[wine.git] / dlls / uxtheme / draw.c
blobe3d3679d7b3bfd8daaf80d282972ade8606f20e1
1 /*
2 * Win32 5.1 Theme drawing
4 * Copyright (C) 2003 Kevin Koltzau
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "vfwmsgs.h"
31 #include "uxtheme.h"
32 #include "tmschema.h"
34 #include "msstyles.h"
35 #include "uxthemedll.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
41 /***********************************************************************
42 * Defines and global variables
45 extern ATOM atDialogThemeEnabled;
47 /***********************************************************************/
49 /***********************************************************************
50 * EnableThemeDialogTexture (UXTHEME.@)
52 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
54 static const WCHAR szTab[] = { 'T','a','b',0 };
55 BOOL res;
57 TRACE("(%p,0x%08x\n", hwnd, dwFlags);
58 res = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
59 UlongToHandle(dwFlags|0x80000000));
60 /* 0x80000000 serves as a "flags set" flag */
61 if (!res)
62 return HRESULT_FROM_WIN32(GetLastError());
63 if (dwFlags & ETDT_USETABTEXTURE)
64 return SetWindowTheme (hwnd, NULL, szTab);
65 else
66 return SetWindowTheme (hwnd, NULL, NULL);
69 /***********************************************************************
70 * IsThemeDialogTextureEnabled (UXTHEME.@)
72 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
74 DWORD dwDialogTextureFlags;
75 TRACE("(%p)\n", hwnd);
77 dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
78 if (dwDialogTextureFlags == 0)
79 /* Means EnableThemeDialogTexture wasn't called for this dialog */
80 return TRUE;
82 return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
85 /***********************************************************************
86 * DrawThemeParentBackground (UXTHEME.@)
88 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
90 RECT rt;
91 POINT org;
92 HWND hParent;
93 HRGN clip = NULL;
94 int hasClip = -1;
96 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
97 hParent = GetParent(hwnd);
98 if(!hParent)
99 hParent = hwnd;
100 if(prc) {
101 CopyRect(&rt, prc);
102 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
104 clip = CreateRectRgn(0,0,1,1);
105 hasClip = GetClipRgn(hdc, clip);
106 if(hasClip == -1)
107 TRACE("Failed to get original clipping region\n");
108 else
109 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
111 else {
112 GetClientRect(hwnd, &rt);
113 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
116 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
118 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
119 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
121 SetViewportOrgEx(hdc, org.x, org.y, NULL);
122 if(prc) {
123 if(hasClip == 0)
124 SelectClipRgn(hdc, NULL);
125 else if(hasClip == 1)
126 SelectClipRgn(hdc, clip);
127 DeleteObject(clip);
129 return S_OK;
133 /***********************************************************************
134 * DrawThemeBackground (UXTHEME.@)
136 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
137 int iStateId, const RECT *pRect,
138 const RECT *pClipRect)
140 DTBGOPTS opts;
141 opts.dwSize = sizeof(DTBGOPTS);
142 opts.dwFlags = 0;
143 if(pClipRect) {
144 opts.dwFlags |= DTBG_CLIPRECT;
145 CopyRect(&opts.rcClip, pClipRect);
147 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
150 /***********************************************************************
151 * UXTHEME_SelectImage
153 * Select the image to use
155 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
157 PTHEME_PROPERTY tp;
158 int imageselecttype = IST_NONE;
159 int i;
160 int image;
161 if(glyph)
162 image = TMT_GLYPHIMAGEFILE;
163 else
164 image = TMT_IMAGEFILE;
166 if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
167 return tp;
168 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
170 if(imageselecttype == IST_DPI) {
171 int reqdpi = 0;
172 int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
173 for(i=4; i>=0; i--) {
174 reqdpi = 0;
175 if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
176 if(reqdpi != 0 && screendpi >= reqdpi) {
177 TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
178 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
182 /* If an image couldn't be selected, choose the first one */
183 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
185 else if(imageselecttype == IST_SIZE) {
186 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
187 POINT reqsize;
188 for(i=4; i>=0; i--) {
189 PTHEME_PROPERTY fileProp =
190 MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
191 if (!fileProp) continue;
192 if(FAILED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
193 /* fall back to size of Nth image */
194 WCHAR szPath[MAX_PATH];
195 int imagelayout = IL_HORIZONTAL;
196 int imagecount = 1;
197 BITMAP bmp;
198 HBITMAP hBmp;
199 BOOL hasAlpha;
201 lstrcpynW(szPath, fileProp->lpValue,
202 min(fileProp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
203 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
204 if(!hBmp) continue;
206 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
207 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
209 GetObjectW(hBmp, sizeof(bmp), &bmp);
210 if(imagelayout == IL_VERTICAL) {
211 reqsize.x = bmp.bmWidth;
212 reqsize.y = bmp.bmHeight/imagecount;
214 else {
215 reqsize.x = bmp.bmWidth/imagecount;
216 reqsize.y = bmp.bmHeight;
219 if(reqsize.x <= size.x && reqsize.y <= size.y) {
220 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
221 return fileProp;
224 /* If an image couldn't be selected, choose the smallest one */
225 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
227 return NULL;
230 /***********************************************************************
231 * UXTHEME_LoadImage
233 * Load image for part/state
235 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
236 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
238 int imagelayout = IL_HORIZONTAL;
239 int imagecount = 1;
240 int imagenum;
241 BITMAP bmp;
242 WCHAR szPath[MAX_PATH];
243 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
244 if(!tp) {
245 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
246 return E_PROP_ID_UNSUPPORTED;
248 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
249 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
250 if(!*hBmp) {
251 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
252 return HRESULT_FROM_WIN32(GetLastError());
255 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
256 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
258 imagenum = max (min (imagecount, iStateId), 1) - 1;
259 GetObjectW(*hBmp, sizeof(bmp), &bmp);
261 if(imagecount < 1) imagecount = 1;
263 if(imagelayout == IL_VERTICAL) {
264 int height = bmp.bmHeight/imagecount;
265 bmpRect->left = 0;
266 bmpRect->right = bmp.bmWidth;
267 bmpRect->top = imagenum * height;
268 bmpRect->bottom = bmpRect->top + height;
270 else {
271 int width = bmp.bmWidth/imagecount;
272 bmpRect->left = imagenum * width;
273 bmpRect->right = bmpRect->left + width;
274 bmpRect->top = 0;
275 bmpRect->bottom = bmp.bmHeight;
277 return S_OK;
280 /***********************************************************************
281 * UXTHEME_StretchBlt
283 * Pseudo TransparentBlt/StretchBlt
285 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
286 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
287 INT transparent, COLORREF transcolor)
289 static const BLENDFUNCTION blendFunc =
291 AC_SRC_OVER, /* BlendOp */
292 0, /* BlendFlag */
293 255, /* SourceConstantAlpha */
294 AC_SRC_ALPHA /* AlphaFormat */
297 BOOL ret = TRUE;
298 int old_stretch_mode;
299 POINT old_brush_org;
301 old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
302 SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
304 if (transparent == ALPHABLEND_BINARY) {
305 /* Ensure we don't pass any negative values to TransparentBlt */
306 ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
307 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
308 transcolor);
309 } else if ((transparent == ALPHABLEND_NONE) ||
310 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
311 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
312 blendFunc))
314 ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
315 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
316 SRCCOPY);
319 SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
320 SetStretchBltMode(hdcDst, old_stretch_mode);
322 return ret;
325 /***********************************************************************
326 * UXTHEME_Blt
328 * Simplify sending same width/height for both source and dest
330 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
331 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
332 INT transparent, COLORREF transcolor)
334 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
335 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
336 transparent, transcolor);
339 /***********************************************************************
340 * UXTHEME_SizedBlt
342 * Stretches or tiles, depending on sizingtype.
344 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
345 int nWidthDst, int nHeightDst,
346 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
347 int nWidthSrc, int nHeightSrc,
348 int sizingtype,
349 INT transparent, COLORREF transcolor)
351 if (sizingtype == ST_TILE)
353 HDC hdcTemp;
354 BOOL result = FALSE;
356 if (!nWidthSrc || !nHeightSrc) return TRUE;
358 /* For destination width/height less than or equal to source
359 width/height, do not bother with memory bitmap optimization */
360 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
362 int bltWidth = min (nWidthDst, nWidthSrc);
363 int bltHeight = min (nHeightDst, nHeightSrc);
365 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
366 hdcSrc, nXOriginSrc, nYOriginSrc,
367 transparent, transcolor);
370 /* Create a DC with a bitmap consisting of a tiling of the source
371 bitmap, with standard GDI functions. This is faster than an
372 iteration with UXTHEME_Blt(). */
373 hdcTemp = CreateCompatibleDC(hdcSrc);
374 if (hdcTemp != 0)
376 HBITMAP bitmapTemp;
377 HBITMAP bitmapOrig;
378 int nWidthTemp, nHeightTemp;
379 int xOfs, xRemaining;
380 int yOfs, yRemaining;
381 int growSize;
383 /* Calculate temp dimensions of integer multiples of source dimensions */
384 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
385 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
386 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
387 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
389 /* Initial copy of bitmap */
390 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
392 /* Extend bitmap in the X direction. Growth of width is exponential */
393 xOfs = nWidthSrc;
394 xRemaining = nWidthTemp - nWidthSrc;
395 growSize = nWidthSrc;
396 while (xRemaining > 0)
398 growSize = min(growSize, xRemaining);
399 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
400 xOfs += growSize;
401 xRemaining -= growSize;
402 growSize *= 2;
405 /* Extend bitmap in the Y direction. Growth of height is exponential */
406 yOfs = nHeightSrc;
407 yRemaining = nHeightTemp - nHeightSrc;
408 growSize = nHeightSrc;
409 while (yRemaining > 0)
411 growSize = min(growSize, yRemaining);
412 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
413 yOfs += growSize;
414 yRemaining -= growSize;
415 growSize *= 2;
418 /* Use temporary hdc for source */
419 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
420 hdcTemp, 0, 0,
421 transparent, transcolor);
423 SelectObject(hdcTemp, bitmapOrig);
424 DeleteObject(bitmapTemp);
426 DeleteDC(hdcTemp);
427 return result;
429 else
431 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
432 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
433 transparent, transcolor);
437 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
438 * depend on whether the image has full alpha or whether it is
439 * color-transparent or just opaque. */
440 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
441 BOOL hasImageAlpha, INT* transparent,
442 COLORREF* transparentcolor, BOOL glyph)
444 if (hasImageAlpha)
446 *transparent = ALPHABLEND_FULL;
447 *transparentcolor = RGB (255, 0, 255);
449 else
451 BOOL trans = FALSE;
452 GetThemeBool(hTheme, iPartId, iStateId,
453 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
454 if(trans) {
455 *transparent = ALPHABLEND_BINARY;
456 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
457 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
458 transparentcolor))) {
459 /* If image is transparent, but no color was specified, use magenta */
460 *transparentcolor = RGB(255, 0, 255);
463 else
464 *transparent = ALPHABLEND_NONE;
468 /***********************************************************************
469 * UXTHEME_DrawImageGlyph
471 * Draw an imagefile glyph
473 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
474 int iStateId, RECT *pRect,
475 const DTBGOPTS *pOptions)
477 HRESULT hr;
478 HBITMAP bmpSrc = NULL;
479 HDC hdcSrc = NULL;
480 HGDIOBJ oldSrc = NULL;
481 RECT rcSrc;
482 INT transparent = 0;
483 COLORREF transparentcolor;
484 int valign = VA_CENTER;
485 int halign = HA_CENTER;
486 POINT dstSize;
487 POINT srcSize;
488 POINT topleft;
489 BOOL hasAlpha;
491 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
492 &bmpSrc, &rcSrc, &hasAlpha);
493 if(FAILED(hr)) return hr;
494 hdcSrc = CreateCompatibleDC(hdc);
495 if(!hdcSrc) {
496 hr = HRESULT_FROM_WIN32(GetLastError());
497 return hr;
499 oldSrc = SelectObject(hdcSrc, bmpSrc);
501 dstSize.x = pRect->right-pRect->left;
502 dstSize.y = pRect->bottom-pRect->top;
503 srcSize.x = rcSrc.right-rcSrc.left;
504 srcSize.y = rcSrc.bottom-rcSrc.top;
506 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
507 &transparentcolor, TRUE);
508 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
509 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
511 topleft.x = pRect->left;
512 topleft.y = pRect->top;
513 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
514 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
515 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
516 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
518 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
519 hdcSrc, rcSrc.left, rcSrc.top,
520 transparent, transparentcolor)) {
521 hr = HRESULT_FROM_WIN32(GetLastError());
524 SelectObject(hdcSrc, oldSrc);
525 DeleteDC(hdcSrc);
526 return hr;
529 /***********************************************************************
530 * UXTHEME_DrawImageGlyph
532 * Draw glyph on top of background, if appropriate
534 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
535 int iStateId, RECT *pRect,
536 const DTBGOPTS *pOptions)
538 int glyphtype = GT_NONE;
540 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
542 if(glyphtype == GT_IMAGEGLYPH) {
543 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
545 else if(glyphtype == GT_FONTGLYPH) {
546 /* I don't know what a font glyph is, I've never seen it used in any themes */
547 FIXME("Font glyph\n");
549 return S_OK;
552 /***********************************************************************
553 * get_image_part_size
555 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
557 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
558 int iStateId, RECT *prc, THEMESIZE eSize,
559 POINT *psz)
561 HRESULT hr = S_OK;
562 HBITMAP bmpSrc;
563 RECT rcSrc;
564 BOOL hasAlpha;
566 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
567 &bmpSrc, &rcSrc, &hasAlpha);
568 if (FAILED(hr)) return hr;
570 switch (eSize)
572 case TS_DRAW:
573 if (prc != NULL)
575 RECT rcDst;
576 POINT dstSize;
577 POINT srcSize;
578 int sizingtype = ST_STRETCH;
579 BOOL uniformsizing = FALSE;
581 CopyRect(&rcDst, prc);
583 dstSize.x = rcDst.right-rcDst.left;
584 dstSize.y = rcDst.bottom-rcDst.top;
585 srcSize.x = rcSrc.right-rcSrc.left;
586 srcSize.y = rcSrc.bottom-rcSrc.top;
588 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
589 if(uniformsizing) {
590 /* Scale height and width equally */
591 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
593 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
594 rcDst.bottom = rcDst.top + dstSize.y;
596 else
598 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
599 rcDst.right = rcDst.left + dstSize.x;
603 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
604 if(sizingtype == ST_TRUESIZE) {
605 int truesizestretchmark = 100;
607 if(dstSize.x < 0 || dstSize.y < 0) {
608 BOOL mirrorimage = TRUE;
609 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
610 if(mirrorimage) {
611 if(dstSize.x < 0) {
612 rcDst.left += dstSize.x;
613 rcDst.right += dstSize.x;
615 if(dstSize.y < 0) {
616 rcDst.top += dstSize.y;
617 rcDst.bottom += dstSize.y;
621 /* Whatever TrueSizeStretchMark does - it does not seem to
622 * be what's outlined below. It appears as if native
623 * uxtheme always stretches if dest is smaller than source
624 * (ie as if TrueSizeStretchMark==100 with the code below) */
625 #if 0
626 /* Only stretch when target exceeds source by truesizestretchmark percent */
627 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
628 #endif
629 if(dstSize.x < 0 || dstSize.y < 0 ||
630 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
631 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
632 memcpy (psz, &dstSize, sizeof (SIZE));
634 else {
635 memcpy (psz, &srcSize, sizeof (SIZE));
638 else
640 psz->x = abs(dstSize.x);
641 psz->y = abs(dstSize.y);
643 break;
645 /* else fall through */
646 case TS_MIN:
647 /* FIXME: couldn't figure how native uxtheme computes min size */
648 case TS_TRUE:
649 psz->x = rcSrc.right - rcSrc.left;
650 psz->y = rcSrc.bottom - rcSrc.top;
651 break;
653 return hr;
656 /***********************************************************************
657 * UXTHEME_DrawImageBackground
659 * Draw an imagefile background
661 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
662 int iStateId, RECT *pRect,
663 const DTBGOPTS *pOptions)
665 HRESULT hr = S_OK;
666 HBITMAP bmpSrc, bmpSrcResized = NULL;
667 HGDIOBJ oldSrc;
668 HDC hdcSrc, hdcOrigSrc = NULL;
669 RECT rcSrc;
670 RECT rcDst;
671 POINT dstSize;
672 POINT srcSize;
673 POINT drawSize;
674 int sizingtype = ST_STRETCH;
675 INT transparent;
676 COLORREF transparentcolor = 0;
677 BOOL hasAlpha;
679 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
680 &bmpSrc, &rcSrc, &hasAlpha);
681 if(FAILED(hr)) return hr;
682 hdcSrc = CreateCompatibleDC(hdc);
683 if(!hdcSrc) {
684 hr = HRESULT_FROM_WIN32(GetLastError());
685 return hr;
687 oldSrc = SelectObject(hdcSrc, bmpSrc);
689 CopyRect(&rcDst, pRect);
691 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
692 &transparentcolor, FALSE);
694 dstSize.x = rcDst.right-rcDst.left;
695 dstSize.y = rcDst.bottom-rcDst.top;
696 srcSize.x = rcSrc.right-rcSrc.left;
697 srcSize.y = rcSrc.bottom-rcSrc.top;
699 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
700 if(sizingtype == ST_TRUESIZE) {
701 int valign = VA_CENTER, halign = HA_CENTER;
703 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
704 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
705 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
707 if (halign == HA_CENTER)
708 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
709 else if (halign == HA_RIGHT)
710 rcDst.left = rcDst.right - drawSize.x;
711 if (valign == VA_CENTER)
712 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
713 else if (valign == VA_BOTTOM)
714 rcDst.top = rcDst.bottom - drawSize.y;
715 rcDst.right = rcDst.left + drawSize.x;
716 rcDst.bottom = rcDst.top + drawSize.y;
717 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
718 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
719 transparent, transparentcolor))
720 hr = HRESULT_FROM_WIN32(GetLastError());
722 else {
723 HDC hdcDst = NULL;
724 MARGINS sm;
725 POINT org;
727 dstSize.x = abs(dstSize.x);
728 dstSize.y = abs(dstSize.y);
730 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
732 /* Resize source image if destination smaller than margins */
733 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
734 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
735 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
736 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
737 srcSize.y = dstSize.y;
740 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
741 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
742 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
743 srcSize.x = dstSize.x;
746 hdcOrigSrc = hdcSrc;
747 hdcSrc = CreateCompatibleDC(NULL);
748 bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
749 SelectObject(hdcSrc, bmpSrcResized);
751 UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
752 rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
754 rcSrc.left = 0;
755 rcSrc.top = 0;
756 rcSrc.right = srcSize.x;
757 rcSrc.bottom = srcSize.y;
760 hdcDst = hdc;
761 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
763 /* Upper left corner */
764 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
765 hdcSrc, rcSrc.left, rcSrc.top,
766 transparent, transparentcolor)) {
767 hr = HRESULT_FROM_WIN32(GetLastError());
768 goto draw_error;
770 /* Upper right corner */
771 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
772 sm.cxRightWidth, sm.cyTopHeight,
773 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
774 transparent, transparentcolor)) {
775 hr = HRESULT_FROM_WIN32(GetLastError());
776 goto draw_error;
778 /* Lower left corner */
779 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
780 sm.cxLeftWidth, sm.cyBottomHeight,
781 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
782 transparent, transparentcolor)) {
783 hr = HRESULT_FROM_WIN32(GetLastError());
784 goto draw_error;
786 /* Lower right corner */
787 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
788 sm.cxRightWidth, sm.cyBottomHeight,
789 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
790 transparent, transparentcolor)) {
791 hr = HRESULT_FROM_WIN32(GetLastError());
792 goto draw_error;
795 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
796 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
797 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
798 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
799 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
801 if(destCenterWidth > 0) {
802 /* Center top */
803 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
804 destCenterWidth, sm.cyTopHeight,
805 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
806 srcCenterWidth, sm.cyTopHeight,
807 sizingtype, transparent, transparentcolor)) {
808 hr = HRESULT_FROM_WIN32(GetLastError());
809 goto draw_error;
811 /* Center bottom */
812 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
813 destCenterWidth, sm.cyBottomHeight,
814 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
815 srcCenterWidth, sm.cyBottomHeight,
816 sizingtype, transparent, transparentcolor)) {
817 hr = HRESULT_FROM_WIN32(GetLastError());
818 goto draw_error;
821 if(destCenterHeight > 0) {
822 /* Left center */
823 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
824 sm.cxLeftWidth, destCenterHeight,
825 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
826 sm.cxLeftWidth, srcCenterHeight,
827 sizingtype,
828 transparent, transparentcolor)) {
829 hr = HRESULT_FROM_WIN32(GetLastError());
830 goto draw_error;
832 /* Right center */
833 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
834 sm.cxRightWidth, destCenterHeight,
835 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
836 sm.cxRightWidth, srcCenterHeight,
837 sizingtype, transparent, transparentcolor)) {
838 hr = HRESULT_FROM_WIN32(GetLastError());
839 goto draw_error;
842 if(destCenterHeight > 0 && destCenterWidth > 0) {
843 BOOL borderonly = FALSE;
844 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
845 if(!borderonly) {
846 /* Center */
847 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
848 destCenterWidth, destCenterHeight,
849 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
850 srcCenterWidth, srcCenterHeight,
851 sizingtype, transparent, transparentcolor)) {
852 hr = HRESULT_FROM_WIN32(GetLastError());
853 goto draw_error;
859 draw_error:
860 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
862 SelectObject(hdcSrc, oldSrc);
863 DeleteDC(hdcSrc);
864 if (bmpSrcResized) DeleteObject(bmpSrcResized);
865 if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
866 CopyRect(pRect, &rcDst);
867 return hr;
870 /***********************************************************************
871 * UXTHEME_DrawBorderRectangle
873 * Draw the bounding rectangle for a borderfill background
875 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
876 int iStateId, RECT *pRect,
877 const DTBGOPTS *pOptions)
879 HRESULT hr = S_OK;
880 HPEN hPen;
881 HGDIOBJ oldPen;
882 COLORREF bordercolor = RGB(0,0,0);
883 int bordersize = 1;
885 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
886 if(bordersize > 0) {
887 POINT ptCorners[5];
888 ptCorners[0].x = pRect->left;
889 ptCorners[0].y = pRect->top;
890 ptCorners[1].x = pRect->right-1;
891 ptCorners[1].y = pRect->top;
892 ptCorners[2].x = pRect->right-1;
893 ptCorners[2].y = pRect->bottom-1;
894 ptCorners[3].x = pRect->left;
895 ptCorners[3].y = pRect->bottom-1;
896 ptCorners[4].x = pRect->left;
897 ptCorners[4].y = pRect->top;
899 InflateRect(pRect, -bordersize, -bordersize);
900 if(pOptions->dwFlags & DTBG_OMITBORDER)
901 return S_OK;
902 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
903 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
904 if(!hPen)
905 return HRESULT_FROM_WIN32(GetLastError());
906 oldPen = SelectObject(hdc, hPen);
908 if(!Polyline(hdc, ptCorners, 5))
909 hr = HRESULT_FROM_WIN32(GetLastError());
911 SelectObject(hdc, oldPen);
912 DeleteObject(hPen);
914 return hr;
917 /***********************************************************************
918 * UXTHEME_DrawBackgroundFill
920 * Fill a borderfill background rectangle
922 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
923 int iStateId, RECT *pRect,
924 const DTBGOPTS *pOptions)
926 HRESULT hr = S_OK;
927 int filltype = FT_SOLID;
929 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
931 if(pOptions->dwFlags & DTBG_OMITCONTENT)
932 return S_OK;
934 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
936 if(filltype == FT_SOLID) {
937 HBRUSH hBrush;
938 COLORREF fillcolor = RGB(255,255,255);
940 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
941 hBrush = CreateSolidBrush(fillcolor);
942 if(!FillRect(hdc, pRect, hBrush))
943 hr = HRESULT_FROM_WIN32(GetLastError());
944 DeleteObject(hBrush);
946 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
947 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
948 the gradient ratios (no idea how those work)
949 Few themes use this, and the ones I've seen only use 2 colors with
950 a gradient ratio of 0 and 255 respectively
953 COLORREF gradient1 = RGB(0,0,0);
954 COLORREF gradient2 = RGB(255,255,255);
955 TRIVERTEX vert[2];
956 GRADIENT_RECT gRect;
958 FIXME("Gradient implementation not complete\n");
960 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
961 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
963 vert[0].x = pRect->left;
964 vert[0].y = pRect->top;
965 vert[0].Red = GetRValue(gradient1) << 8;
966 vert[0].Green = GetGValue(gradient1) << 8;
967 vert[0].Blue = GetBValue(gradient1) << 8;
968 vert[0].Alpha = 0xff00;
970 vert[1].x = pRect->right;
971 vert[1].y = pRect->bottom;
972 vert[1].Red = GetRValue(gradient2) << 8;
973 vert[1].Green = GetGValue(gradient2) << 8;
974 vert[1].Blue = GetBValue(gradient2) << 8;
975 vert[1].Alpha = 0xff00;
977 gRect.UpperLeft = 0;
978 gRect.LowerRight = 1;
979 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
981 else if(filltype == FT_RADIALGRADIENT) {
982 /* I've never seen this used in a theme */
983 FIXME("Radial gradient\n");
985 else if(filltype == FT_TILEIMAGE) {
986 /* I've never seen this used in a theme */
987 FIXME("Tile image\n");
989 return hr;
992 /***********************************************************************
993 * UXTHEME_DrawBorderBackground
995 * Draw an imagefile background
997 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
998 int iStateId, const RECT *pRect,
999 const DTBGOPTS *pOptions)
1001 HRESULT hr;
1002 RECT rt;
1004 CopyRect(&rt, pRect);
1006 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1007 if(FAILED(hr))
1008 return hr;
1009 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1012 /***********************************************************************
1013 * DrawThemeBackgroundEx (UXTHEME.@)
1015 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1016 int iStateId, const RECT *pRect,
1017 const DTBGOPTS *pOptions)
1019 HRESULT hr;
1020 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1021 const DTBGOPTS *opts;
1022 HRGN clip = NULL;
1023 int hasClip = -1;
1024 int bgtype = BT_BORDERFILL;
1025 RECT rt;
1027 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1028 if(!hTheme)
1029 return E_HANDLE;
1031 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1032 if (bgtype == BT_NONE) return S_OK;
1034 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1035 opts = pOptions;
1036 if(!opts) opts = &defaultOpts;
1038 if(opts->dwFlags & DTBG_CLIPRECT) {
1039 clip = CreateRectRgn(0,0,1,1);
1040 hasClip = GetClipRgn(hdc, clip);
1041 if(hasClip == -1)
1042 TRACE("Failed to get original clipping region\n");
1043 else
1044 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1046 CopyRect(&rt, pRect);
1048 if(bgtype == BT_IMAGEFILE)
1049 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1050 else if(bgtype == BT_BORDERFILL)
1051 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1052 else {
1053 FIXME("Unknown background type\n");
1054 /* This should never happen, and hence I don't know what to return */
1055 hr = E_FAIL;
1057 if(SUCCEEDED(hr))
1058 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1059 if(opts->dwFlags & DTBG_CLIPRECT) {
1060 if(hasClip == 0)
1061 SelectClipRgn(hdc, NULL);
1062 else if(hasClip == 1)
1063 SelectClipRgn(hdc, clip);
1064 DeleteObject(clip);
1066 return hr;
1070 * DrawThemeEdge() implementation
1072 * Since it basically is DrawEdge() with different colors, I copied its code
1073 * from user32's uitools.c.
1076 enum
1078 EDGE_LIGHT,
1079 EDGE_HIGHLIGHT,
1080 EDGE_SHADOW,
1081 EDGE_DARKSHADOW,
1082 EDGE_FILL,
1084 EDGE_WINDOW,
1085 EDGE_WINDOWFRAME,
1087 EDGE_NUMCOLORS
1090 static const struct
1092 int themeProp;
1093 int sysColor;
1094 } EdgeColorMap[EDGE_NUMCOLORS] = {
1095 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1096 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1097 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1098 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1099 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1100 {-1, COLOR_WINDOW},
1101 {-1, COLOR_WINDOWFRAME}
1104 static const signed char LTInnerNormal[] = {
1105 -1, -1, -1, -1,
1106 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1107 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1108 -1, -1, -1, -1
1111 static const signed char LTOuterNormal[] = {
1112 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1113 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1114 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1115 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1118 static const signed char RBInnerNormal[] = {
1119 -1, -1, -1, -1,
1120 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1121 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1122 -1, -1, -1, -1
1125 static const signed char RBOuterNormal[] = {
1126 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1127 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1128 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1129 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1132 static const signed char LTInnerSoft[] = {
1133 -1, -1, -1, -1,
1134 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1135 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1136 -1, -1, -1, -1
1139 static const signed char LTOuterSoft[] = {
1140 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1141 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1142 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1143 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1146 #define RBInnerSoft RBInnerNormal /* These are the same */
1147 #define RBOuterSoft RBOuterNormal
1149 static const signed char LTRBOuterMono[] = {
1150 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1151 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1152 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1153 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1156 static const signed char LTRBInnerMono[] = {
1157 -1, -1, -1, -1,
1158 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1159 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1160 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1163 static const signed char LTRBOuterFlat[] = {
1164 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1165 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1166 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1167 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1170 static const signed char LTRBInnerFlat[] = {
1171 -1, -1, -1, -1,
1172 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1173 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1174 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1177 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1179 COLORREF col;
1180 if ((EdgeColorMap[edgeType].themeProp == -1)
1181 || FAILED (GetThemeColor (theme, part, state,
1182 EdgeColorMap[edgeType].themeProp, &col)))
1183 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1184 return col;
1187 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1189 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1192 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1194 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1197 /***********************************************************************
1198 * draw_diag_edge
1200 * Same as DrawEdge invoked with BF_DIAGONAL
1202 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1203 const RECT* rc, UINT uType,
1204 UINT uFlags, LPRECT contentsRect)
1206 POINT Points[4];
1207 signed char InnerI, OuterI;
1208 HPEN InnerPen, OuterPen;
1209 POINT SavePoint;
1210 HPEN SavePen;
1211 int spx, spy;
1212 int epx, epy;
1213 int Width = rc->right - rc->left;
1214 int Height= rc->bottom - rc->top;
1215 int SmallDiam = Width > Height ? Height : Width;
1216 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1217 || (uType & BDR_OUTER) == BDR_OUTER)
1218 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1219 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1220 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1222 /* Init some vars */
1223 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1224 SavePen = SelectObject(hdc, InnerPen);
1225 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1227 /* Determine the colors of the edges */
1228 if(uFlags & BF_MONO)
1230 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1231 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1233 else if(uFlags & BF_FLAT)
1235 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1236 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1238 else if(uFlags & BF_SOFT)
1240 if(uFlags & BF_BOTTOM)
1242 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1243 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1245 else
1247 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1248 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1251 else
1253 if(uFlags & BF_BOTTOM)
1255 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1256 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1258 else
1260 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1261 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1265 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1266 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1268 MoveToEx(hdc, 0, 0, &SavePoint);
1270 /* Don't ask me why, but this is what is visible... */
1271 /* This must be possible to do much simpler, but I fail to */
1272 /* see the logic in the MS implementation (sigh...). */
1273 /* So, this might look a bit brute force here (and it is), but */
1274 /* it gets the job done;) */
1276 switch(uFlags & BF_RECT)
1278 case 0:
1279 case BF_LEFT:
1280 case BF_BOTTOM:
1281 case BF_BOTTOMLEFT:
1282 /* Left bottom endpoint */
1283 epx = rc->left-1;
1284 spx = epx + SmallDiam;
1285 epy = rc->bottom;
1286 spy = epy - SmallDiam;
1287 break;
1289 case BF_TOPLEFT:
1290 case BF_BOTTOMRIGHT:
1291 /* Left top endpoint */
1292 epx = rc->left-1;
1293 spx = epx + SmallDiam;
1294 epy = rc->top-1;
1295 spy = epy + SmallDiam;
1296 break;
1298 case BF_TOP:
1299 case BF_RIGHT:
1300 case BF_TOPRIGHT:
1301 case BF_RIGHT|BF_LEFT:
1302 case BF_RIGHT|BF_LEFT|BF_TOP:
1303 case BF_BOTTOM|BF_TOP:
1304 case BF_BOTTOM|BF_TOP|BF_LEFT:
1305 case BF_BOTTOMRIGHT|BF_LEFT:
1306 case BF_BOTTOMRIGHT|BF_TOP:
1307 case BF_RECT:
1308 /* Right top endpoint */
1309 spx = rc->left;
1310 epx = spx + SmallDiam;
1311 spy = rc->bottom-1;
1312 epy = spy - SmallDiam;
1313 break;
1316 MoveToEx(hdc, spx, spy, NULL);
1317 SelectObject(hdc, OuterPen);
1318 LineTo(hdc, epx, epy);
1320 SelectObject(hdc, InnerPen);
1322 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1324 case BF_DIAGONAL_ENDBOTTOMLEFT:
1325 case (BF_DIAGONAL|BF_BOTTOM):
1326 case BF_DIAGONAL:
1327 case (BF_DIAGONAL|BF_LEFT):
1328 MoveToEx(hdc, spx-1, spy, NULL);
1329 LineTo(hdc, epx, epy-1);
1330 Points[0].x = spx-add;
1331 Points[0].y = spy;
1332 Points[1].x = rc->left;
1333 Points[1].y = rc->top;
1334 Points[2].x = epx+1;
1335 Points[2].y = epy-1-add;
1336 Points[3] = Points[2];
1337 break;
1339 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1340 MoveToEx(hdc, spx-1, spy, NULL);
1341 LineTo(hdc, epx, epy+1);
1342 Points[0].x = spx-add;
1343 Points[0].y = spy;
1344 Points[1].x = rc->left;
1345 Points[1].y = rc->bottom-1;
1346 Points[2].x = epx+1;
1347 Points[2].y = epy+1+add;
1348 Points[3] = Points[2];
1349 break;
1351 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1352 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1353 case BF_DIAGONAL_ENDTOPRIGHT:
1354 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1355 MoveToEx(hdc, spx+1, spy, NULL);
1356 LineTo(hdc, epx, epy+1);
1357 Points[0].x = epx-1;
1358 Points[0].y = epy+1+add;
1359 Points[1].x = rc->right-1;
1360 Points[1].y = rc->top+add;
1361 Points[2].x = rc->right-1;
1362 Points[2].y = rc->bottom-1;
1363 Points[3].x = spx+add;
1364 Points[3].y = spy;
1365 break;
1367 case BF_DIAGONAL_ENDTOPLEFT:
1368 MoveToEx(hdc, spx, spy-1, NULL);
1369 LineTo(hdc, epx+1, epy);
1370 Points[0].x = epx+1+add;
1371 Points[0].y = epy+1;
1372 Points[1].x = rc->right-1;
1373 Points[1].y = rc->top;
1374 Points[2].x = rc->right-1;
1375 Points[2].y = rc->bottom-1-add;
1376 Points[3].x = spx;
1377 Points[3].y = spy-add;
1378 break;
1380 case (BF_DIAGONAL|BF_TOP):
1381 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1382 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1383 MoveToEx(hdc, spx+1, spy-1, NULL);
1384 LineTo(hdc, epx, epy);
1385 Points[0].x = epx-1;
1386 Points[0].y = epy+1;
1387 Points[1].x = rc->right-1;
1388 Points[1].y = rc->top;
1389 Points[2].x = rc->right-1;
1390 Points[2].y = rc->bottom-1-add;
1391 Points[3].x = spx+add;
1392 Points[3].y = spy-add;
1393 break;
1395 case (BF_DIAGONAL|BF_RIGHT):
1396 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1397 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1398 MoveToEx(hdc, spx, spy, NULL);
1399 LineTo(hdc, epx-1, epy+1);
1400 Points[0].x = spx;
1401 Points[0].y = spy;
1402 Points[1].x = rc->left;
1403 Points[1].y = rc->top+add;
1404 Points[2].x = epx-1-add;
1405 Points[2].y = epy+1+add;
1406 Points[3] = Points[2];
1407 break;
1410 /* Fill the interior if asked */
1411 if((uFlags & BF_MIDDLE) && retval)
1413 HBRUSH hbsave;
1414 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1415 theme, part, state);
1416 HPEN hpsave;
1417 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1418 theme, part, state);
1419 hbsave = SelectObject(hdc, hb);
1420 hpsave = SelectObject(hdc, hp);
1421 Polygon(hdc, Points, 4);
1422 SelectObject(hdc, hbsave);
1423 SelectObject(hdc, hpsave);
1424 DeleteObject (hp);
1425 DeleteObject (hb);
1428 /* Adjust rectangle if asked */
1429 if(uFlags & BF_ADJUST)
1431 *contentsRect = *rc;
1432 if(uFlags & BF_LEFT) contentsRect->left += add;
1433 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1434 if(uFlags & BF_TOP) contentsRect->top += add;
1435 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1438 /* Cleanup */
1439 SelectObject(hdc, SavePen);
1440 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1441 if(InnerI != -1) DeleteObject (InnerPen);
1442 if(OuterI != -1) DeleteObject (OuterPen);
1444 return retval;
1447 /***********************************************************************
1448 * draw_rect_edge
1450 * Same as DrawEdge invoked without BF_DIAGONAL
1452 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1453 const RECT* rc, UINT uType,
1454 UINT uFlags, LPRECT contentsRect)
1456 signed char LTInnerI, LTOuterI;
1457 signed char RBInnerI, RBOuterI;
1458 HPEN LTInnerPen, LTOuterPen;
1459 HPEN RBInnerPen, RBOuterPen;
1460 RECT InnerRect = *rc;
1461 POINT SavePoint;
1462 HPEN SavePen;
1463 int LBpenplus = 0;
1464 int LTpenplus = 0;
1465 int RTpenplus = 0;
1466 int RBpenplus = 0;
1467 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1468 || (uType & BDR_OUTER) == BDR_OUTER)
1469 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1471 /* Init some vars */
1472 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1473 SavePen = SelectObject(hdc, LTInnerPen);
1475 /* Determine the colors of the edges */
1476 if(uFlags & BF_MONO)
1478 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1479 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1481 else if(uFlags & BF_FLAT)
1483 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1484 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1486 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1488 else if(uFlags & BF_SOFT)
1490 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1491 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1492 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1493 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1495 else
1497 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1498 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1499 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1500 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1503 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1504 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1505 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1506 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1508 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1509 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1510 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1511 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1513 MoveToEx(hdc, 0, 0, &SavePoint);
1515 /* Draw the outer edge */
1516 SelectObject(hdc, LTOuterPen);
1517 if(uFlags & BF_TOP)
1519 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1520 LineTo(hdc, InnerRect.right, InnerRect.top);
1522 if(uFlags & BF_LEFT)
1524 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1525 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1527 SelectObject(hdc, RBOuterPen);
1528 if(uFlags & BF_BOTTOM)
1530 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1531 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1533 if(uFlags & BF_RIGHT)
1535 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1536 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1539 /* Draw the inner edge */
1540 SelectObject(hdc, LTInnerPen);
1541 if(uFlags & BF_TOP)
1543 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1544 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1546 if(uFlags & BF_LEFT)
1548 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1549 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1551 SelectObject(hdc, RBInnerPen);
1552 if(uFlags & BF_BOTTOM)
1554 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1555 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1557 if(uFlags & BF_RIGHT)
1559 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1560 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1563 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1565 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1566 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1568 if(uFlags & BF_LEFT) InnerRect.left += add;
1569 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1570 if(uFlags & BF_TOP) InnerRect.top += add;
1571 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1573 if((uFlags & BF_MIDDLE) && retval)
1575 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1576 theme, part, state);
1577 FillRect(hdc, &InnerRect, br);
1578 DeleteObject (br);
1581 if(uFlags & BF_ADJUST)
1582 *contentsRect = InnerRect;
1585 /* Cleanup */
1586 SelectObject(hdc, SavePen);
1587 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1588 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1589 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1590 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1591 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1592 return retval;
1596 /***********************************************************************
1597 * DrawThemeEdge (UXTHEME.@)
1599 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1600 * difference is that it does not rely on the system colors alone, but
1601 * also allows color specification in the theme.
1603 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1604 int iStateId, const RECT *pDestRect, UINT uEdge,
1605 UINT uFlags, RECT *pContentRect)
1607 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1608 if(!hTheme)
1609 return E_HANDLE;
1611 if(uFlags & BF_DIAGONAL)
1612 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1613 uEdge, uFlags, pContentRect);
1614 else
1615 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1616 uEdge, uFlags, pContentRect);
1620 /***********************************************************************
1621 * DrawThemeIcon (UXTHEME.@)
1623 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1624 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1626 FIXME("%d %d: stub\n", iPartId, iStateId);
1627 if(!hTheme)
1628 return E_HANDLE;
1629 return ERROR_CALL_NOT_IMPLEMENTED;
1632 /***********************************************************************
1633 * DrawThemeText (UXTHEME.@)
1635 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1636 LPCWSTR pszText, int iCharCount, DWORD flags,
1637 DWORD flags2, const RECT *pRect)
1639 DTTOPTS opts;
1640 RECT rt;
1642 TRACE("%d %d\n", iPartId, iStateId);
1644 CopyRect(&rt, pRect);
1646 opts.dwSize = sizeof(opts);
1647 if (flags2 & DTT_GRAYED) {
1648 opts.dwFlags = DTT_TEXTCOLOR;
1649 opts.crText = GetSysColor(COLOR_GRAYTEXT);
1651 return DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, flags, &rt, &opts);
1654 /***********************************************************************
1655 * DrawThemeTextEx (UXTHEME.@)
1657 HRESULT WINAPI DrawThemeTextEx(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1658 LPCWSTR pszText, int iCharCount, DWORD flags, RECT *rect, const DTTOPTS *options)
1660 HRESULT hr;
1661 HFONT hFont = NULL;
1662 HGDIOBJ oldFont = NULL;
1663 LOGFONTW logfont;
1664 COLORREF textColor;
1665 COLORREF oldTextColor;
1666 int oldBkMode;
1668 TRACE("%p %p %d %d %s:%d 0x%08x %p %p\n", hTheme, hdc, iPartId, iStateId,
1669 debugstr_wn(pszText, iCharCount), iCharCount, flags, rect, options);
1671 if(!hTheme)
1672 return E_HANDLE;
1674 if (options->dwFlags & ~DTT_TEXTCOLOR)
1675 FIXME("unsupported flags 0x%08x\n", options->dwFlags);
1677 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1678 if(SUCCEEDED(hr)) {
1679 hFont = CreateFontIndirectW(&logfont);
1680 if(!hFont)
1681 TRACE("Failed to create font\n");
1684 if(hFont)
1685 oldFont = SelectObject(hdc, hFont);
1687 if (options->dwFlags & DTT_TEXTCOLOR)
1688 textColor = options->crText;
1689 else {
1690 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1691 textColor = GetTextColor(hdc);
1693 oldTextColor = SetTextColor(hdc, textColor);
1694 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1695 DrawTextW(hdc, pszText, iCharCount, rect, flags);
1696 SetBkMode(hdc, oldBkMode);
1697 SetTextColor(hdc, oldTextColor);
1699 if(hFont) {
1700 SelectObject(hdc, oldFont);
1701 DeleteObject(hFont);
1703 return S_OK;
1706 /***********************************************************************
1707 * GetThemeBackgroundContentRect (UXTHEME.@)
1709 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1710 int iStateId,
1711 const RECT *pBoundingRect,
1712 RECT *pContentRect)
1714 MARGINS margin;
1715 HRESULT hr;
1717 TRACE("(%d,%d)\n", iPartId, iStateId);
1718 if(!hTheme)
1719 return E_HANDLE;
1721 /* try content margins property... */
1722 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1723 if(SUCCEEDED(hr)) {
1724 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1725 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1726 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1727 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1728 } else {
1729 /* otherwise, try to determine content rect from the background type and props */
1730 int bgtype = BT_BORDERFILL;
1731 *pContentRect = *pBoundingRect;
1733 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1734 if(bgtype == BT_BORDERFILL) {
1735 int bordersize = 1;
1737 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1738 InflateRect(pContentRect, -bordersize, -bordersize);
1739 } else if ((bgtype == BT_IMAGEFILE)
1740 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1741 TMT_SIZINGMARGINS, NULL, &margin)))) {
1742 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1743 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1744 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1745 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1747 /* If nothing was found, leave unchanged */
1750 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pContentRect->left, pContentRect->top, pContentRect->right, pContentRect->bottom);
1752 return S_OK;
1755 /***********************************************************************
1756 * GetThemeBackgroundExtent (UXTHEME.@)
1758 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1759 int iStateId, const RECT *pContentRect,
1760 RECT *pExtentRect)
1762 MARGINS margin;
1763 HRESULT hr;
1765 TRACE("(%d,%d)\n", iPartId, iStateId);
1766 if(!hTheme)
1767 return E_HANDLE;
1769 /* try content margins property... */
1770 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1771 if(SUCCEEDED(hr)) {
1772 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1773 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1774 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1775 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1776 } else {
1777 /* otherwise, try to determine content rect from the background type and props */
1778 int bgtype = BT_BORDERFILL;
1779 *pExtentRect = *pContentRect;
1781 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1782 if(bgtype == BT_BORDERFILL) {
1783 int bordersize = 1;
1785 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1786 InflateRect(pExtentRect, bordersize, bordersize);
1787 } else if ((bgtype == BT_IMAGEFILE)
1788 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1789 TMT_SIZINGMARGINS, NULL, &margin)))) {
1790 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1791 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1792 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1793 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1795 /* If nothing was found, leave unchanged */
1798 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pExtentRect->left, pExtentRect->top, pExtentRect->right, pExtentRect->bottom);
1800 return S_OK;
1803 static inline void flush_rgn_data( HRGN rgn, RGNDATA *data )
1805 HRGN tmp = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1807 CombineRgn( rgn, rgn, tmp, RGN_OR );
1808 DeleteObject( tmp );
1809 data->rdh.nCount = 0;
1812 static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len )
1814 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
1816 if (len <= 0) return;
1817 rect->left = x;
1818 rect->top = y;
1819 rect->right = x + len;
1820 rect->bottom = y + 1;
1821 data->rdh.nCount++;
1822 if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT))
1823 flush_rgn_data( rgn, data );
1826 static HRESULT create_image_bg_region(HTHEME theme, int part, int state, const RECT *rect, HRGN *rgn)
1828 RECT r;
1829 HDC dc;
1830 HBITMAP bmp;
1831 HRGN hrgn;
1832 BOOL istrans;
1833 COLORREF transcolour;
1834 HBRUSH transbrush;
1835 unsigned int x, y, start;
1836 BITMAPINFO bitmapinfo;
1837 DWORD *bits;
1838 char buffer[4096];
1839 RGNDATA *data = (RGNDATA *)buffer;
1841 if (FAILED(GetThemeBool(theme, part, state, TMT_TRANSPARENT, &istrans)) || !istrans) {
1842 *rgn = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
1843 return S_OK;
1846 r = *rect;
1847 OffsetRect(&r, -r.left, -r.top);
1849 if (FAILED(GetThemeColor(theme, part, state, TMT_TRANSPARENTCOLOR, &transcolour)))
1850 transcolour = RGB(255, 0, 255); /* defaults to magenta */
1852 dc = CreateCompatibleDC(NULL);
1853 if (!dc) {
1854 WARN("CreateCompatibleDC failed\n");
1855 return E_FAIL;
1858 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1859 bitmapinfo.bmiHeader.biWidth = rect->right - rect->left;
1860 bitmapinfo.bmiHeader.biHeight = -(rect->bottom - rect->top);
1861 bitmapinfo.bmiHeader.biPlanes = 1;
1862 bitmapinfo.bmiHeader.biBitCount = 32;
1863 bitmapinfo.bmiHeader.biCompression = BI_RGB;
1864 bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * 4;
1865 bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
1866 bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
1867 bitmapinfo.bmiHeader.biClrUsed = 0;
1868 bitmapinfo.bmiHeader.biClrImportant = 0;
1870 bmp = CreateDIBSection(dc, &bitmapinfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1871 if (!bmp) {
1872 WARN("CreateDIBSection failed\n");
1873 DeleteDC(dc);
1874 return E_FAIL;
1877 SelectObject(dc, bmp);
1879 transbrush = CreateSolidBrush(transcolour);
1880 FillRect(dc, &r, transbrush);
1881 DeleteObject(transbrush);
1883 if (FAILED(DrawThemeBackground(theme, dc, part, state, &r, NULL))) {
1884 WARN("DrawThemeBackground failed\n");
1885 DeleteObject(bmp);
1886 DeleteDC(dc);
1887 return E_FAIL;
1890 data->rdh.dwSize = sizeof(data->rdh);
1891 data->rdh.iType = RDH_RECTANGLES;
1892 data->rdh.nCount = 0;
1893 data->rdh.nRgnSize = sizeof(buffer) - sizeof(data->rdh);
1895 hrgn = CreateRectRgn(0, 0, 0, 0);
1897 for (y = 0; y < r.bottom; y++, bits += r.right) {
1898 x = 0;
1899 while (x < r.right) {
1900 while (x < r.right && (bits[x] & 0xffffff) == transcolour) x++;
1901 start = x;
1902 while (x < r.right && !((bits[x] & 0xffffff) == transcolour)) x++;
1903 add_row( hrgn, data, rect->left + start, rect->top + y, x - start );
1907 if (data->rdh.nCount > 0) flush_rgn_data(hrgn, data);
1909 *rgn = hrgn;
1911 DeleteObject(bmp);
1912 DeleteDC(dc);
1914 return S_OK;
1917 /***********************************************************************
1918 * GetThemeBackgroundRegion (UXTHEME.@)
1920 * Calculate the background region, taking into consideration transparent areas
1921 * of the background image.
1923 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1924 int iStateId, const RECT *pRect,
1925 HRGN *pRegion)
1927 HRESULT hr = S_OK;
1928 int bgtype = BT_BORDERFILL;
1930 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1931 if(!hTheme)
1932 return E_HANDLE;
1933 if(!pRect || !pRegion)
1934 return E_POINTER;
1936 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1937 if(bgtype == BT_IMAGEFILE) {
1938 hr = create_image_bg_region(hTheme, iPartId, iStateId, pRect, pRegion);
1940 else if(bgtype == BT_BORDERFILL) {
1941 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1942 if(!*pRegion)
1943 hr = HRESULT_FROM_WIN32(GetLastError());
1945 else {
1946 FIXME("Unknown background type\n");
1947 /* This should never happen, and hence I don't know what to return */
1948 hr = E_FAIL;
1950 return hr;
1953 /* compute part size for "borderfill" backgrounds */
1954 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1955 int iStateId, THEMESIZE eSize, POINT* psz)
1957 HRESULT hr = S_OK;
1958 int bordersize = 1;
1960 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1961 &bordersize)))
1963 psz->x = psz->y = 2*bordersize;
1964 if (eSize != TS_MIN)
1966 psz->x++;
1967 psz->y++;
1970 return hr;
1973 /***********************************************************************
1974 * GetThemePartSize (UXTHEME.@)
1976 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1977 int iStateId, RECT *prc, THEMESIZE eSize,
1978 SIZE *psz)
1980 int bgtype = BT_BORDERFILL;
1981 HRESULT hr = S_OK;
1982 POINT size = {1, 1};
1984 if(!hTheme)
1985 return E_HANDLE;
1987 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1988 if (bgtype == BT_NONE)
1989 /* do nothing */;
1990 else if(bgtype == BT_IMAGEFILE)
1991 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1992 else if(bgtype == BT_BORDERFILL)
1993 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1994 else {
1995 FIXME("Unknown background type\n");
1996 /* This should never happen, and hence I don't know what to return */
1997 hr = E_FAIL;
1999 psz->cx = size.x;
2000 psz->cy = size.y;
2001 return hr;
2005 /***********************************************************************
2006 * GetThemeTextExtent (UXTHEME.@)
2008 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2009 int iStateId, LPCWSTR pszText, int iCharCount,
2010 DWORD dwTextFlags, const RECT *pBoundingRect,
2011 RECT *pExtentRect)
2013 HRESULT hr;
2014 HFONT hFont = NULL;
2015 HGDIOBJ oldFont = NULL;
2016 LOGFONTW logfont;
2017 RECT rt = {0,0,0xFFFF,0xFFFF};
2019 TRACE("%d %d: stub\n", iPartId, iStateId);
2020 if(!hTheme)
2021 return E_HANDLE;
2023 if(pBoundingRect)
2024 CopyRect(&rt, pBoundingRect);
2026 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2027 if(SUCCEEDED(hr)) {
2028 hFont = CreateFontIndirectW(&logfont);
2029 if(!hFont)
2030 TRACE("Failed to create font\n");
2032 if(hFont)
2033 oldFont = SelectObject(hdc, hFont);
2035 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2036 CopyRect(pExtentRect, &rt);
2038 if(hFont) {
2039 SelectObject(hdc, oldFont);
2040 DeleteObject(hFont);
2042 return S_OK;
2045 /***********************************************************************
2046 * GetThemeTextMetrics (UXTHEME.@)
2048 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2049 int iStateId, TEXTMETRICW *ptm)
2051 HRESULT hr;
2052 HFONT hFont = NULL;
2053 HGDIOBJ oldFont = NULL;
2054 LOGFONTW logfont;
2056 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2057 if(!hTheme)
2058 return E_HANDLE;
2060 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2061 if(SUCCEEDED(hr)) {
2062 hFont = CreateFontIndirectW(&logfont);
2063 if(!hFont)
2064 TRACE("Failed to create font\n");
2066 if(hFont)
2067 oldFont = SelectObject(hdc, hFont);
2069 if(!GetTextMetricsW(hdc, ptm))
2070 hr = HRESULT_FROM_WIN32(GetLastError());
2072 if(hFont) {
2073 SelectObject(hdc, oldFont);
2074 DeleteObject(hFont);
2076 return hr;
2079 /***********************************************************************
2080 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2082 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2083 int iStateId)
2085 int bgtype = BT_BORDERFILL;
2086 RECT rect = {0, 0, 0, 0};
2087 HBITMAP bmpSrc;
2088 RECT rcSrc;
2089 BOOL hasAlpha;
2090 INT transparent;
2091 COLORREF transparentcolor;
2093 TRACE("(%d,%d)\n", iPartId, iStateId);
2095 if(!hTheme)
2096 return FALSE;
2098 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2100 if (bgtype != BT_IMAGEFILE) return FALSE;
2102 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2103 &bmpSrc, &rcSrc, &hasAlpha)))
2104 return FALSE;
2106 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2107 &transparentcolor, FALSE);
2108 return (transparent != ALPHABLEND_NONE);