user32: Add current keyboard layout to thread data.
[wine.git] / dlls / uxtheme / draw.c
blob8c3c4ae2218e65b169272604e39dce1112b7528f
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 <stdlib.h>
22 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "wingdi.h"
28 #include "vfwmsgs.h"
29 #include "uxtheme.h"
30 #include "tmschema.h"
32 #include "msstyles.h"
33 #include "uxthemedll.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
39 /***********************************************************************
40 * Defines and global variables
43 extern ATOM atDialogThemeEnabled;
45 /***********************************************************************/
47 /***********************************************************************
48 * EnableThemeDialogTexture (UXTHEME.@)
50 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
52 BOOL res;
54 TRACE("(%p,0x%08x\n", hwnd, dwFlags);
55 res = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
56 UlongToHandle(dwFlags|0x80000000));
57 /* 0x80000000 serves as a "flags set" flag */
58 if (!res)
59 return HRESULT_FROM_WIN32(GetLastError());
60 if (dwFlags & ETDT_USETABTEXTURE)
61 return SetWindowTheme (hwnd, NULL, L"Tab");
62 else
63 return SetWindowTheme (hwnd, NULL, NULL);
66 /***********************************************************************
67 * IsThemeDialogTextureEnabled (UXTHEME.@)
69 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
71 DWORD dwDialogTextureFlags;
72 TRACE("(%p)\n", hwnd);
74 dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
75 if (dwDialogTextureFlags == 0)
76 /* Means EnableThemeDialogTexture wasn't called for this dialog */
77 return TRUE;
79 return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
82 /***********************************************************************
83 * DrawThemeParentBackground (UXTHEME.@)
85 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
87 RECT rt;
88 POINT org;
89 HWND hParent;
90 HRGN clip = NULL;
91 int hasClip = -1;
93 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
94 hParent = GetParent(hwnd);
95 if(!hParent)
96 hParent = hwnd;
97 if(prc) {
98 rt = *prc;
99 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
101 clip = CreateRectRgn(0,0,1,1);
102 hasClip = GetClipRgn(hdc, clip);
103 if(hasClip == -1)
104 TRACE("Failed to get original clipping region\n");
105 else
106 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
108 else {
109 GetClientRect(hwnd, &rt);
110 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
113 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
115 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
116 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
118 SetViewportOrgEx(hdc, org.x, org.y, NULL);
119 if(prc) {
120 if(hasClip == 0)
121 SelectClipRgn(hdc, NULL);
122 else if(hasClip == 1)
123 SelectClipRgn(hdc, clip);
124 DeleteObject(clip);
126 return S_OK;
130 /***********************************************************************
131 * DrawThemeBackground (UXTHEME.@)
133 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
134 int iStateId, const RECT *pRect,
135 const RECT *pClipRect)
137 DTBGOPTS opts;
138 opts.dwSize = sizeof(DTBGOPTS);
139 opts.dwFlags = 0;
140 if(pClipRect) {
141 opts.dwFlags |= DTBG_CLIPRECT;
142 opts.rcClip = *pClipRect;
144 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
147 /***********************************************************************
148 * UXTHEME_SelectImage
150 * Select the image to use
152 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
154 PTHEME_PROPERTY tp;
155 int imageselecttype = IST_NONE;
156 int i;
157 int image;
158 if(glyph)
159 image = TMT_GLYPHIMAGEFILE;
160 else
161 image = TMT_IMAGEFILE;
163 if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
164 return tp;
165 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
167 if(imageselecttype == IST_DPI) {
168 int reqdpi = 0;
169 int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
170 for(i=4; i>=0; i--) {
171 reqdpi = 0;
172 if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
173 if(reqdpi != 0 && screendpi >= reqdpi) {
174 TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
175 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
179 /* If an image couldn't be selected, choose the first one */
180 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
182 else if(imageselecttype == IST_SIZE) {
183 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
184 POINT reqsize;
185 for(i=4; i>=0; i--) {
186 PTHEME_PROPERTY fileProp =
187 MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
188 if (!fileProp) continue;
189 if(FAILED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
190 /* fall back to size of Nth image */
191 WCHAR szPath[MAX_PATH];
192 int imagelayout = IL_HORIZONTAL;
193 int imagecount = 1;
194 BITMAP bmp;
195 HBITMAP hBmp;
196 BOOL hasAlpha;
198 lstrcpynW(szPath, fileProp->lpValue, min(fileProp->dwValueLen+1, ARRAY_SIZE(szPath)));
199 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
200 if(!hBmp) continue;
202 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
203 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
205 GetObjectW(hBmp, sizeof(bmp), &bmp);
206 if(imagelayout == IL_VERTICAL) {
207 reqsize.x = bmp.bmWidth;
208 reqsize.y = bmp.bmHeight/imagecount;
210 else {
211 reqsize.x = bmp.bmWidth/imagecount;
212 reqsize.y = bmp.bmHeight;
215 if(reqsize.x <= size.x && reqsize.y <= size.y) {
216 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
217 return fileProp;
220 /* If an image couldn't be selected, choose the smallest one */
221 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
223 return NULL;
226 /***********************************************************************
227 * UXTHEME_LoadImage
229 * Load image for part/state
231 static HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
232 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
234 int imagelayout = IL_HORIZONTAL;
235 int imagecount = 1;
236 int imagenum;
237 BITMAP bmp;
238 WCHAR szPath[MAX_PATH];
239 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
240 if(!tp) {
241 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
242 return E_PROP_ID_UNSUPPORTED;
244 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, ARRAY_SIZE(szPath)));
245 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
246 if(!*hBmp) {
247 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
248 return HRESULT_FROM_WIN32(GetLastError());
251 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
252 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
254 imagenum = max (min (imagecount, iStateId), 1) - 1;
255 GetObjectW(*hBmp, sizeof(bmp), &bmp);
257 if(imagecount < 1) imagecount = 1;
259 if(imagelayout == IL_VERTICAL) {
260 int height = bmp.bmHeight/imagecount;
261 bmpRect->left = 0;
262 bmpRect->right = bmp.bmWidth;
263 bmpRect->top = imagenum * height;
264 bmpRect->bottom = bmpRect->top + height;
266 else {
267 int width = bmp.bmWidth/imagecount;
268 bmpRect->left = imagenum * width;
269 bmpRect->right = bmpRect->left + width;
270 bmpRect->top = 0;
271 bmpRect->bottom = bmp.bmHeight;
273 return S_OK;
276 /***********************************************************************
277 * UXTHEME_StretchBlt
279 * Pseudo TransparentBlt/StretchBlt
281 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
282 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
283 INT transparent, COLORREF transcolor)
285 static const BLENDFUNCTION blendFunc =
287 AC_SRC_OVER, /* BlendOp */
288 0, /* BlendFlag */
289 255, /* SourceConstantAlpha */
290 AC_SRC_ALPHA /* AlphaFormat */
293 BOOL ret = TRUE;
294 int old_stretch_mode;
295 POINT old_brush_org;
297 old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
298 SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
300 if (transparent == ALPHABLEND_BINARY) {
301 /* Ensure we don't pass any negative values to TransparentBlt */
302 ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
303 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
304 transcolor);
305 } else if ((transparent == ALPHABLEND_NONE) ||
306 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
307 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
308 blendFunc))
310 ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
311 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
312 SRCCOPY);
315 SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
316 SetStretchBltMode(hdcDst, old_stretch_mode);
318 return ret;
321 /***********************************************************************
322 * UXTHEME_Blt
324 * Simplify sending same width/height for both source and dest
326 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
327 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
328 INT transparent, COLORREF transcolor)
330 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
331 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
332 transparent, transcolor);
335 /***********************************************************************
336 * UXTHEME_SizedBlt
338 * Stretches or tiles, depending on sizingtype.
340 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
341 int nWidthDst, int nHeightDst,
342 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
343 int nWidthSrc, int nHeightSrc,
344 int sizingtype,
345 INT transparent, COLORREF transcolor)
347 if (sizingtype == ST_TILE)
349 HDC hdcTemp;
350 BOOL result = FALSE;
352 if (!nWidthSrc || !nHeightSrc) return TRUE;
354 /* For destination width/height less than or equal to source
355 width/height, do not bother with memory bitmap optimization */
356 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
358 int bltWidth = min (nWidthDst, nWidthSrc);
359 int bltHeight = min (nHeightDst, nHeightSrc);
361 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
362 hdcSrc, nXOriginSrc, nYOriginSrc,
363 transparent, transcolor);
366 /* Create a DC with a bitmap consisting of a tiling of the source
367 bitmap, with standard GDI functions. This is faster than an
368 iteration with UXTHEME_Blt(). */
369 hdcTemp = CreateCompatibleDC(hdcSrc);
370 if (hdcTemp != 0)
372 HBITMAP bitmapTemp;
373 HBITMAP bitmapOrig;
374 int nWidthTemp, nHeightTemp;
375 int xOfs, xRemaining;
376 int yOfs, yRemaining;
377 int growSize;
379 /* Calculate temp dimensions of integer multiples of source dimensions */
380 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
381 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
382 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
383 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
385 /* Initial copy of bitmap */
386 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
388 /* Extend bitmap in the X direction. Growth of width is exponential */
389 xOfs = nWidthSrc;
390 xRemaining = nWidthTemp - nWidthSrc;
391 growSize = nWidthSrc;
392 while (xRemaining > 0)
394 growSize = min(growSize, xRemaining);
395 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
396 xOfs += growSize;
397 xRemaining -= growSize;
398 growSize *= 2;
401 /* Extend bitmap in the Y direction. Growth of height is exponential */
402 yOfs = nHeightSrc;
403 yRemaining = nHeightTemp - nHeightSrc;
404 growSize = nHeightSrc;
405 while (yRemaining > 0)
407 growSize = min(growSize, yRemaining);
408 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
409 yOfs += growSize;
410 yRemaining -= growSize;
411 growSize *= 2;
414 /* Use temporary hdc for source */
415 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
416 hdcTemp, 0, 0,
417 transparent, transcolor);
419 SelectObject(hdcTemp, bitmapOrig);
420 DeleteObject(bitmapTemp);
422 DeleteDC(hdcTemp);
423 return result;
425 else
427 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
428 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
429 transparent, transcolor);
433 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
434 * depend on whether the image has full alpha or whether it is
435 * color-transparent or just opaque. */
436 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
437 BOOL hasImageAlpha, INT* transparent,
438 COLORREF* transparentcolor, BOOL glyph)
440 if (hasImageAlpha)
442 *transparent = ALPHABLEND_FULL;
443 *transparentcolor = RGB (255, 0, 255);
445 else
447 BOOL trans = FALSE;
448 GetThemeBool(hTheme, iPartId, iStateId,
449 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
450 if(trans) {
451 *transparent = ALPHABLEND_BINARY;
452 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
453 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
454 transparentcolor))) {
455 /* If image is transparent, but no color was specified, use magenta */
456 *transparentcolor = RGB(255, 0, 255);
459 else
460 *transparent = ALPHABLEND_NONE;
464 /***********************************************************************
465 * UXTHEME_DrawImageGlyph
467 * Draw an imagefile glyph
469 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
470 int iStateId, RECT *pRect,
471 const DTBGOPTS *pOptions)
473 HRESULT hr;
474 HBITMAP bmpSrc = NULL;
475 HDC hdcSrc = NULL;
476 HGDIOBJ oldSrc = NULL;
477 RECT rcSrc;
478 INT transparent = 0;
479 COLORREF transparentcolor;
480 int valign = VA_CENTER;
481 int halign = HA_CENTER;
482 POINT dstSize;
483 POINT srcSize;
484 POINT topleft;
485 BOOL hasAlpha;
487 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
488 &bmpSrc, &rcSrc, &hasAlpha);
489 if(FAILED(hr)) return hr;
490 hdcSrc = CreateCompatibleDC(hdc);
491 if(!hdcSrc) {
492 hr = HRESULT_FROM_WIN32(GetLastError());
493 return hr;
495 oldSrc = SelectObject(hdcSrc, bmpSrc);
497 dstSize.x = pRect->right-pRect->left;
498 dstSize.y = pRect->bottom-pRect->top;
499 srcSize.x = rcSrc.right-rcSrc.left;
500 srcSize.y = rcSrc.bottom-rcSrc.top;
502 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
503 &transparentcolor, TRUE);
504 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
505 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
507 topleft.x = pRect->left;
508 topleft.y = pRect->top;
509 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
510 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
511 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
512 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
514 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
515 hdcSrc, rcSrc.left, rcSrc.top,
516 transparent, transparentcolor)) {
517 hr = HRESULT_FROM_WIN32(GetLastError());
520 SelectObject(hdcSrc, oldSrc);
521 DeleteDC(hdcSrc);
522 return hr;
525 /***********************************************************************
526 * UXTHEME_DrawImageGlyph
528 * Draw glyph on top of background, if appropriate
530 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
531 int iStateId, RECT *pRect,
532 const DTBGOPTS *pOptions)
534 int glyphtype = GT_NONE;
536 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
538 if(glyphtype == GT_IMAGEGLYPH) {
539 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
541 else if(glyphtype == GT_FONTGLYPH) {
542 /* I don't know what a font glyph is, I've never seen it used in any themes */
543 FIXME("Font glyph\n");
545 return S_OK;
548 /***********************************************************************
549 * get_image_part_size
551 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
553 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
554 int iStateId, RECT *prc, THEMESIZE eSize,
555 POINT *psz)
557 HRESULT hr = S_OK;
558 HBITMAP bmpSrc;
559 RECT rcSrc;
560 BOOL hasAlpha;
562 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
563 &bmpSrc, &rcSrc, &hasAlpha);
564 if (FAILED(hr)) return hr;
566 switch (eSize)
568 case TS_DRAW:
569 if (prc != NULL)
571 RECT rcDst;
572 POINT dstSize;
573 POINT srcSize;
574 int sizingtype = ST_STRETCH;
575 BOOL uniformsizing = FALSE;
577 rcDst = *prc;
579 dstSize.x = rcDst.right-rcDst.left;
580 dstSize.y = rcDst.bottom-rcDst.top;
581 srcSize.x = rcSrc.right-rcSrc.left;
582 srcSize.y = rcSrc.bottom-rcSrc.top;
584 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
585 if(uniformsizing) {
586 /* Scale height and width equally */
587 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
589 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
590 rcDst.bottom = rcDst.top + dstSize.y;
592 else
594 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
595 rcDst.right = rcDst.left + dstSize.x;
599 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
600 if(sizingtype == ST_TRUESIZE) {
601 int truesizestretchmark = 100;
603 if(dstSize.x < 0 || dstSize.y < 0) {
604 BOOL mirrorimage = TRUE;
605 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
606 if(mirrorimage) {
607 if(dstSize.x < 0) {
608 rcDst.left += dstSize.x;
609 rcDst.right += dstSize.x;
611 if(dstSize.y < 0) {
612 rcDst.top += dstSize.y;
613 rcDst.bottom += dstSize.y;
617 /* Whatever TrueSizeStretchMark does - it does not seem to
618 * be what's outlined below. It appears as if native
619 * uxtheme always stretches if dest is smaller than source
620 * (ie as if TrueSizeStretchMark==100 with the code below) */
621 #if 0
622 /* Only stretch when target exceeds source by truesizestretchmark percent */
623 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
624 #endif
625 if(dstSize.x < 0 || dstSize.y < 0 ||
626 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
627 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
628 memcpy (psz, &dstSize, sizeof (SIZE));
630 else {
631 memcpy (psz, &srcSize, sizeof (SIZE));
634 else
636 psz->x = abs(dstSize.x);
637 psz->y = abs(dstSize.y);
639 break;
641 /* else fall through */
642 case TS_MIN:
643 /* FIXME: couldn't figure how native uxtheme computes min size */
644 case TS_TRUE:
645 psz->x = rcSrc.right - rcSrc.left;
646 psz->y = rcSrc.bottom - rcSrc.top;
647 break;
649 return hr;
652 /***********************************************************************
653 * UXTHEME_DrawImageBackground
655 * Draw an imagefile background
657 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
658 int iStateId, RECT *pRect,
659 const DTBGOPTS *pOptions)
661 HRESULT hr = S_OK;
662 HBITMAP bmpSrc, bmpSrcResized = NULL;
663 HGDIOBJ oldSrc;
664 HDC hdcSrc, hdcOrigSrc = NULL;
665 RECT rcSrc;
666 RECT rcDst;
667 POINT dstSize;
668 POINT srcSize;
669 POINT drawSize;
670 int sizingtype = ST_STRETCH;
671 INT transparent;
672 COLORREF transparentcolor = 0;
673 BOOL hasAlpha;
675 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
676 &bmpSrc, &rcSrc, &hasAlpha);
677 if(FAILED(hr)) return hr;
678 hdcSrc = CreateCompatibleDC(hdc);
679 if(!hdcSrc) {
680 hr = HRESULT_FROM_WIN32(GetLastError());
681 return hr;
683 oldSrc = SelectObject(hdcSrc, bmpSrc);
685 rcDst = *pRect;
687 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
688 &transparentcolor, FALSE);
690 dstSize.x = rcDst.right-rcDst.left;
691 dstSize.y = rcDst.bottom-rcDst.top;
692 srcSize.x = rcSrc.right-rcSrc.left;
693 srcSize.y = rcSrc.bottom-rcSrc.top;
695 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
696 if(sizingtype == ST_TRUESIZE) {
697 int valign = VA_CENTER, halign = HA_CENTER;
699 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
700 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
701 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
703 if (halign == HA_CENTER)
704 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
705 else if (halign == HA_RIGHT)
706 rcDst.left = rcDst.right - drawSize.x;
707 if (valign == VA_CENTER)
708 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
709 else if (valign == VA_BOTTOM)
710 rcDst.top = rcDst.bottom - drawSize.y;
711 rcDst.right = rcDst.left + drawSize.x;
712 rcDst.bottom = rcDst.top + drawSize.y;
713 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
714 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
715 transparent, transparentcolor))
716 hr = HRESULT_FROM_WIN32(GetLastError());
718 else {
719 HDC hdcDst = NULL;
720 MARGINS sm;
721 POINT org;
723 dstSize.x = abs(dstSize.x);
724 dstSize.y = abs(dstSize.y);
726 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
728 /* Resize source image if destination smaller than margins */
729 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
730 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
731 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
732 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
733 srcSize.y = dstSize.y;
736 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
737 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
738 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
739 srcSize.x = dstSize.x;
742 hdcOrigSrc = hdcSrc;
743 hdcSrc = CreateCompatibleDC(NULL);
744 bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
745 SelectObject(hdcSrc, bmpSrcResized);
747 UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
748 rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
750 rcSrc.left = 0;
751 rcSrc.top = 0;
752 rcSrc.right = srcSize.x;
753 rcSrc.bottom = srcSize.y;
756 hdcDst = hdc;
757 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
759 /* Upper left corner */
760 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
761 hdcSrc, rcSrc.left, rcSrc.top,
762 transparent, transparentcolor)) {
763 hr = HRESULT_FROM_WIN32(GetLastError());
764 goto draw_error;
766 /* Upper right corner */
767 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
768 sm.cxRightWidth, sm.cyTopHeight,
769 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
770 transparent, transparentcolor)) {
771 hr = HRESULT_FROM_WIN32(GetLastError());
772 goto draw_error;
774 /* Lower left corner */
775 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
776 sm.cxLeftWidth, sm.cyBottomHeight,
777 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
778 transparent, transparentcolor)) {
779 hr = HRESULT_FROM_WIN32(GetLastError());
780 goto draw_error;
782 /* Lower right corner */
783 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
784 sm.cxRightWidth, sm.cyBottomHeight,
785 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
786 transparent, transparentcolor)) {
787 hr = HRESULT_FROM_WIN32(GetLastError());
788 goto draw_error;
791 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
792 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
793 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
794 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
795 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
797 if(destCenterWidth > 0) {
798 /* Center top */
799 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
800 destCenterWidth, sm.cyTopHeight,
801 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
802 srcCenterWidth, sm.cyTopHeight,
803 sizingtype, transparent, transparentcolor)) {
804 hr = HRESULT_FROM_WIN32(GetLastError());
805 goto draw_error;
807 /* Center bottom */
808 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
809 destCenterWidth, sm.cyBottomHeight,
810 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
811 srcCenterWidth, sm.cyBottomHeight,
812 sizingtype, transparent, transparentcolor)) {
813 hr = HRESULT_FROM_WIN32(GetLastError());
814 goto draw_error;
817 if(destCenterHeight > 0) {
818 /* Left center */
819 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
820 sm.cxLeftWidth, destCenterHeight,
821 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
822 sm.cxLeftWidth, srcCenterHeight,
823 sizingtype,
824 transparent, transparentcolor)) {
825 hr = HRESULT_FROM_WIN32(GetLastError());
826 goto draw_error;
828 /* Right center */
829 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
830 sm.cxRightWidth, destCenterHeight,
831 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
832 sm.cxRightWidth, srcCenterHeight,
833 sizingtype, transparent, transparentcolor)) {
834 hr = HRESULT_FROM_WIN32(GetLastError());
835 goto draw_error;
838 if(destCenterHeight > 0 && destCenterWidth > 0) {
839 BOOL borderonly = FALSE;
840 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
841 if(!borderonly) {
842 /* Center */
843 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
844 destCenterWidth, destCenterHeight,
845 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
846 srcCenterWidth, srcCenterHeight,
847 sizingtype, transparent, transparentcolor)) {
848 hr = HRESULT_FROM_WIN32(GetLastError());
849 goto draw_error;
855 draw_error:
856 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
858 SelectObject(hdcSrc, oldSrc);
859 DeleteDC(hdcSrc);
860 if (bmpSrcResized) DeleteObject(bmpSrcResized);
861 if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
862 *pRect = rcDst;
863 return hr;
866 /***********************************************************************
867 * UXTHEME_DrawBorderRectangle
869 * Draw the bounding rectangle for a borderfill background
871 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
872 int iStateId, RECT *pRect,
873 const DTBGOPTS *pOptions)
875 HRESULT hr = S_OK;
876 HPEN hPen;
877 HGDIOBJ oldPen;
878 COLORREF bordercolor = RGB(0,0,0);
879 int bordersize = 1;
881 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
882 if(bordersize > 0) {
883 POINT ptCorners[5];
884 ptCorners[0].x = pRect->left;
885 ptCorners[0].y = pRect->top;
886 ptCorners[1].x = pRect->right-1;
887 ptCorners[1].y = pRect->top;
888 ptCorners[2].x = pRect->right-1;
889 ptCorners[2].y = pRect->bottom-1;
890 ptCorners[3].x = pRect->left;
891 ptCorners[3].y = pRect->bottom-1;
892 ptCorners[4].x = pRect->left;
893 ptCorners[4].y = pRect->top;
895 InflateRect(pRect, -bordersize, -bordersize);
896 if(pOptions->dwFlags & DTBG_OMITBORDER)
897 return S_OK;
898 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
899 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
900 if(!hPen)
901 return HRESULT_FROM_WIN32(GetLastError());
902 oldPen = SelectObject(hdc, hPen);
904 if(!Polyline(hdc, ptCorners, 5))
905 hr = HRESULT_FROM_WIN32(GetLastError());
907 SelectObject(hdc, oldPen);
908 DeleteObject(hPen);
910 return hr;
913 /***********************************************************************
914 * UXTHEME_DrawBackgroundFill
916 * Fill a borderfill background rectangle
918 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
919 int iStateId, RECT *pRect,
920 const DTBGOPTS *pOptions)
922 HRESULT hr = S_OK;
923 int filltype = FT_SOLID;
925 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
927 if(pOptions->dwFlags & DTBG_OMITCONTENT)
928 return S_OK;
930 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
932 if(filltype == FT_SOLID) {
933 HBRUSH hBrush;
934 COLORREF fillcolor = RGB(255,255,255);
936 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
937 hBrush = CreateSolidBrush(fillcolor);
938 if(!FillRect(hdc, pRect, hBrush))
939 hr = HRESULT_FROM_WIN32(GetLastError());
940 DeleteObject(hBrush);
942 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
943 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
944 the gradient ratios (no idea how those work)
945 Few themes use this, and the ones I've seen only use 2 colors with
946 a gradient ratio of 0 and 255 respectively
949 COLORREF gradient1 = RGB(0,0,0);
950 COLORREF gradient2 = RGB(255,255,255);
951 TRIVERTEX vert[2];
952 GRADIENT_RECT gRect;
954 FIXME("Gradient implementation not complete\n");
956 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
957 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
959 vert[0].x = pRect->left;
960 vert[0].y = pRect->top;
961 vert[0].Red = GetRValue(gradient1) << 8;
962 vert[0].Green = GetGValue(gradient1) << 8;
963 vert[0].Blue = GetBValue(gradient1) << 8;
964 vert[0].Alpha = 0xff00;
966 vert[1].x = pRect->right;
967 vert[1].y = pRect->bottom;
968 vert[1].Red = GetRValue(gradient2) << 8;
969 vert[1].Green = GetGValue(gradient2) << 8;
970 vert[1].Blue = GetBValue(gradient2) << 8;
971 vert[1].Alpha = 0xff00;
973 gRect.UpperLeft = 0;
974 gRect.LowerRight = 1;
975 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
977 else if(filltype == FT_RADIALGRADIENT) {
978 /* I've never seen this used in a theme */
979 FIXME("Radial gradient\n");
981 else if(filltype == FT_TILEIMAGE) {
982 /* I've never seen this used in a theme */
983 FIXME("Tile image\n");
985 return hr;
988 /***********************************************************************
989 * UXTHEME_DrawBorderBackground
991 * Draw an imagefile background
993 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
994 int iStateId, const RECT *pRect,
995 const DTBGOPTS *pOptions)
997 HRESULT hr;
998 RECT rt;
1000 rt = *pRect;
1002 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1003 if(FAILED(hr))
1004 return hr;
1005 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
1008 /***********************************************************************
1009 * DrawThemeBackgroundEx (UXTHEME.@)
1011 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1012 int iStateId, const RECT *pRect,
1013 const DTBGOPTS *pOptions)
1015 HRESULT hr;
1016 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1017 const DTBGOPTS *opts;
1018 HRGN clip = NULL;
1019 int hasClip = -1;
1020 int bgtype = BT_BORDERFILL;
1021 RECT rt;
1023 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1024 if(!hTheme)
1025 return E_HANDLE;
1027 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1028 if (bgtype == BT_NONE) return S_OK;
1030 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1031 opts = pOptions;
1032 if(!opts) opts = &defaultOpts;
1034 if(opts->dwFlags & DTBG_CLIPRECT) {
1035 clip = CreateRectRgn(0,0,1,1);
1036 hasClip = GetClipRgn(hdc, clip);
1037 if(hasClip == -1)
1038 TRACE("Failed to get original clipping region\n");
1039 else
1040 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1042 rt = *pRect;
1044 if(bgtype == BT_IMAGEFILE)
1045 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1046 else if(bgtype == BT_BORDERFILL)
1047 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1048 else {
1049 FIXME("Unknown background type\n");
1050 /* This should never happen, and hence I don't know what to return */
1051 hr = E_FAIL;
1053 if(SUCCEEDED(hr))
1054 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1055 if(opts->dwFlags & DTBG_CLIPRECT) {
1056 if(hasClip == 0)
1057 SelectClipRgn(hdc, NULL);
1058 else if(hasClip == 1)
1059 SelectClipRgn(hdc, clip);
1060 DeleteObject(clip);
1062 return hr;
1066 * DrawThemeEdge() implementation
1068 * Since it basically is DrawEdge() with different colors, I copied its code
1069 * from user32's uitools.c.
1072 enum
1074 EDGE_LIGHT,
1075 EDGE_HIGHLIGHT,
1076 EDGE_SHADOW,
1077 EDGE_DARKSHADOW,
1078 EDGE_FILL,
1080 EDGE_WINDOW,
1081 EDGE_WINDOWFRAME,
1083 EDGE_NUMCOLORS
1086 static const struct
1088 int themeProp;
1089 int sysColor;
1090 } EdgeColorMap[EDGE_NUMCOLORS] = {
1091 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1092 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1093 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1094 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1095 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1096 {-1, COLOR_WINDOW},
1097 {-1, COLOR_WINDOWFRAME}
1100 static const signed char LTInnerNormal[] = {
1101 -1, -1, -1, -1,
1102 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1103 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1104 -1, -1, -1, -1
1107 static const signed char LTOuterNormal[] = {
1108 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1109 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1110 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1111 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1114 static const signed char RBInnerNormal[] = {
1115 -1, -1, -1, -1,
1116 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1117 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1118 -1, -1, -1, -1
1121 static const signed char RBOuterNormal[] = {
1122 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1123 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1124 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1125 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1128 static const signed char LTInnerSoft[] = {
1129 -1, -1, -1, -1,
1130 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1131 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1132 -1, -1, -1, -1
1135 static const signed char LTOuterSoft[] = {
1136 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1137 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1138 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1139 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1142 #define RBInnerSoft RBInnerNormal /* These are the same */
1143 #define RBOuterSoft RBOuterNormal
1145 static const signed char LTRBOuterMono[] = {
1146 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1147 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1148 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1149 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1152 static const signed char LTRBInnerMono[] = {
1153 -1, -1, -1, -1,
1154 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1155 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1156 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1159 static const signed char LTRBOuterFlat[] = {
1160 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1161 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1162 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1163 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1166 static const signed char LTRBInnerFlat[] = {
1167 -1, -1, -1, -1,
1168 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1169 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1170 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1173 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1175 COLORREF col;
1176 if ((EdgeColorMap[edgeType].themeProp == -1)
1177 || FAILED (GetThemeColor (theme, part, state,
1178 EdgeColorMap[edgeType].themeProp, &col)))
1179 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1180 return col;
1183 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1185 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1188 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1190 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1193 /***********************************************************************
1194 * draw_diag_edge
1196 * Same as DrawEdge invoked with BF_DIAGONAL
1198 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1199 const RECT* rc, UINT uType,
1200 UINT uFlags, LPRECT contentsRect)
1202 POINT Points[4];
1203 signed char InnerI, OuterI;
1204 HPEN InnerPen, OuterPen;
1205 POINT SavePoint;
1206 HPEN SavePen;
1207 int spx, spy;
1208 int epx, epy;
1209 int Width = rc->right - rc->left;
1210 int Height= rc->bottom - rc->top;
1211 int SmallDiam = Width > Height ? Height : Width;
1212 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1213 || (uType & BDR_OUTER) == BDR_OUTER)
1214 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1215 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1216 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1218 /* Init some vars */
1219 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1220 SavePen = SelectObject(hdc, InnerPen);
1221 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1223 /* Determine the colors of the edges */
1224 if(uFlags & BF_MONO)
1226 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1227 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1229 else if(uFlags & BF_FLAT)
1231 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1232 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1234 else if(uFlags & BF_SOFT)
1236 if(uFlags & BF_BOTTOM)
1238 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1239 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1241 else
1243 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1244 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1247 else
1249 if(uFlags & BF_BOTTOM)
1251 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1252 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1254 else
1256 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1257 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1261 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1262 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1264 MoveToEx(hdc, 0, 0, &SavePoint);
1266 /* Don't ask me why, but this is what is visible... */
1267 /* This must be possible to do much simpler, but I fail to */
1268 /* see the logic in the MS implementation (sigh...). */
1269 /* So, this might look a bit brute force here (and it is), but */
1270 /* it gets the job done;) */
1272 switch(uFlags & BF_RECT)
1274 case 0:
1275 case BF_LEFT:
1276 case BF_BOTTOM:
1277 case BF_BOTTOMLEFT:
1278 /* Left bottom endpoint */
1279 epx = rc->left-1;
1280 spx = epx + SmallDiam;
1281 epy = rc->bottom;
1282 spy = epy - SmallDiam;
1283 break;
1285 case BF_TOPLEFT:
1286 case BF_BOTTOMRIGHT:
1287 /* Left top endpoint */
1288 epx = rc->left-1;
1289 spx = epx + SmallDiam;
1290 epy = rc->top-1;
1291 spy = epy + SmallDiam;
1292 break;
1294 case BF_TOP:
1295 case BF_RIGHT:
1296 case BF_TOPRIGHT:
1297 case BF_RIGHT|BF_LEFT:
1298 case BF_RIGHT|BF_LEFT|BF_TOP:
1299 case BF_BOTTOM|BF_TOP:
1300 case BF_BOTTOM|BF_TOP|BF_LEFT:
1301 case BF_BOTTOMRIGHT|BF_LEFT:
1302 case BF_BOTTOMRIGHT|BF_TOP:
1303 case BF_RECT:
1304 /* Right top endpoint */
1305 spx = rc->left;
1306 epx = spx + SmallDiam;
1307 spy = rc->bottom-1;
1308 epy = spy - SmallDiam;
1309 break;
1312 MoveToEx(hdc, spx, spy, NULL);
1313 SelectObject(hdc, OuterPen);
1314 LineTo(hdc, epx, epy);
1316 SelectObject(hdc, InnerPen);
1318 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1320 case BF_DIAGONAL_ENDBOTTOMLEFT:
1321 case (BF_DIAGONAL|BF_BOTTOM):
1322 case BF_DIAGONAL:
1323 case (BF_DIAGONAL|BF_LEFT):
1324 MoveToEx(hdc, spx-1, spy, NULL);
1325 LineTo(hdc, epx, epy-1);
1326 Points[0].x = spx-add;
1327 Points[0].y = spy;
1328 Points[1].x = rc->left;
1329 Points[1].y = rc->top;
1330 Points[2].x = epx+1;
1331 Points[2].y = epy-1-add;
1332 Points[3] = Points[2];
1333 break;
1335 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1336 MoveToEx(hdc, spx-1, spy, NULL);
1337 LineTo(hdc, epx, epy+1);
1338 Points[0].x = spx-add;
1339 Points[0].y = spy;
1340 Points[1].x = rc->left;
1341 Points[1].y = rc->bottom-1;
1342 Points[2].x = epx+1;
1343 Points[2].y = epy+1+add;
1344 Points[3] = Points[2];
1345 break;
1347 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1348 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1349 case BF_DIAGONAL_ENDTOPRIGHT:
1350 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1351 MoveToEx(hdc, spx+1, spy, NULL);
1352 LineTo(hdc, epx, epy+1);
1353 Points[0].x = epx-1;
1354 Points[0].y = epy+1+add;
1355 Points[1].x = rc->right-1;
1356 Points[1].y = rc->top+add;
1357 Points[2].x = rc->right-1;
1358 Points[2].y = rc->bottom-1;
1359 Points[3].x = spx+add;
1360 Points[3].y = spy;
1361 break;
1363 case BF_DIAGONAL_ENDTOPLEFT:
1364 MoveToEx(hdc, spx, spy-1, NULL);
1365 LineTo(hdc, epx+1, epy);
1366 Points[0].x = epx+1+add;
1367 Points[0].y = epy+1;
1368 Points[1].x = rc->right-1;
1369 Points[1].y = rc->top;
1370 Points[2].x = rc->right-1;
1371 Points[2].y = rc->bottom-1-add;
1372 Points[3].x = spx;
1373 Points[3].y = spy-add;
1374 break;
1376 case (BF_DIAGONAL|BF_TOP):
1377 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1378 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1379 MoveToEx(hdc, spx+1, spy-1, NULL);
1380 LineTo(hdc, epx, epy);
1381 Points[0].x = epx-1;
1382 Points[0].y = epy+1;
1383 Points[1].x = rc->right-1;
1384 Points[1].y = rc->top;
1385 Points[2].x = rc->right-1;
1386 Points[2].y = rc->bottom-1-add;
1387 Points[3].x = spx+add;
1388 Points[3].y = spy-add;
1389 break;
1391 case (BF_DIAGONAL|BF_RIGHT):
1392 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1393 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1394 MoveToEx(hdc, spx, spy, NULL);
1395 LineTo(hdc, epx-1, epy+1);
1396 Points[0].x = spx;
1397 Points[0].y = spy;
1398 Points[1].x = rc->left;
1399 Points[1].y = rc->top+add;
1400 Points[2].x = epx-1-add;
1401 Points[2].y = epy+1+add;
1402 Points[3] = Points[2];
1403 break;
1406 /* Fill the interior if asked */
1407 if((uFlags & BF_MIDDLE) && retval)
1409 HBRUSH hbsave;
1410 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1411 theme, part, state);
1412 HPEN hpsave;
1413 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1414 theme, part, state);
1415 hbsave = SelectObject(hdc, hb);
1416 hpsave = SelectObject(hdc, hp);
1417 Polygon(hdc, Points, 4);
1418 SelectObject(hdc, hbsave);
1419 SelectObject(hdc, hpsave);
1420 DeleteObject (hp);
1421 DeleteObject (hb);
1424 /* Adjust rectangle if asked */
1425 if(uFlags & BF_ADJUST)
1427 *contentsRect = *rc;
1428 if(uFlags & BF_LEFT) contentsRect->left += add;
1429 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1430 if(uFlags & BF_TOP) contentsRect->top += add;
1431 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1434 /* Cleanup */
1435 SelectObject(hdc, SavePen);
1436 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1437 if(InnerI != -1) DeleteObject (InnerPen);
1438 if(OuterI != -1) DeleteObject (OuterPen);
1440 return retval;
1443 /***********************************************************************
1444 * draw_rect_edge
1446 * Same as DrawEdge invoked without BF_DIAGONAL
1448 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1449 const RECT* rc, UINT uType,
1450 UINT uFlags, LPRECT contentsRect)
1452 signed char LTInnerI, LTOuterI;
1453 signed char RBInnerI, RBOuterI;
1454 HPEN LTInnerPen, LTOuterPen;
1455 HPEN RBInnerPen, RBOuterPen;
1456 RECT InnerRect = *rc;
1457 POINT SavePoint;
1458 HPEN SavePen;
1459 int LBpenplus = 0;
1460 int LTpenplus = 0;
1461 int RTpenplus = 0;
1462 int RBpenplus = 0;
1463 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1464 || (uType & BDR_OUTER) == BDR_OUTER)
1465 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1467 /* Init some vars */
1468 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1469 SavePen = SelectObject(hdc, LTInnerPen);
1471 /* Determine the colors of the edges */
1472 if(uFlags & BF_MONO)
1474 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1475 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1477 else if(uFlags & BF_FLAT)
1479 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1480 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1482 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1484 else if(uFlags & BF_SOFT)
1486 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1487 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1488 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1489 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1491 else
1493 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1494 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1495 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1496 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1499 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1500 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1501 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1502 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1504 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1505 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1506 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1507 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1509 MoveToEx(hdc, 0, 0, &SavePoint);
1511 /* Draw the outer edge */
1512 SelectObject(hdc, LTOuterPen);
1513 if(uFlags & BF_TOP)
1515 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1516 LineTo(hdc, InnerRect.right, InnerRect.top);
1518 if(uFlags & BF_LEFT)
1520 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1521 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1523 SelectObject(hdc, RBOuterPen);
1524 if(uFlags & BF_BOTTOM)
1526 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1527 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1529 if(uFlags & BF_RIGHT)
1531 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1532 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1535 /* Draw the inner edge */
1536 SelectObject(hdc, LTInnerPen);
1537 if(uFlags & BF_TOP)
1539 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1540 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1542 if(uFlags & BF_LEFT)
1544 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1545 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1547 SelectObject(hdc, RBInnerPen);
1548 if(uFlags & BF_BOTTOM)
1550 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1551 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1553 if(uFlags & BF_RIGHT)
1555 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1556 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1559 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1561 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1562 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1564 if(uFlags & BF_LEFT) InnerRect.left += add;
1565 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1566 if(uFlags & BF_TOP) InnerRect.top += add;
1567 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1569 if((uFlags & BF_MIDDLE) && retval)
1571 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1572 theme, part, state);
1573 FillRect(hdc, &InnerRect, br);
1574 DeleteObject (br);
1577 if(uFlags & BF_ADJUST)
1578 *contentsRect = InnerRect;
1581 /* Cleanup */
1582 SelectObject(hdc, SavePen);
1583 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1584 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1585 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1586 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1587 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1588 return retval;
1592 /***********************************************************************
1593 * DrawThemeEdge (UXTHEME.@)
1595 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1596 * difference is that it does not rely on the system colors alone, but
1597 * also allows color specification in the theme.
1599 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1600 int iStateId, const RECT *pDestRect, UINT uEdge,
1601 UINT uFlags, RECT *pContentRect)
1603 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1604 if(!hTheme)
1605 return E_HANDLE;
1607 if(uFlags & BF_DIAGONAL)
1608 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1609 uEdge, uFlags, pContentRect);
1610 else
1611 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1612 uEdge, uFlags, pContentRect);
1616 /***********************************************************************
1617 * DrawThemeIcon (UXTHEME.@)
1619 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1620 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1622 FIXME("%d %d: stub\n", iPartId, iStateId);
1623 if(!hTheme)
1624 return E_HANDLE;
1625 return E_NOTIMPL;
1628 /***********************************************************************
1629 * DrawThemeText (UXTHEME.@)
1631 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1632 LPCWSTR pszText, int iCharCount, DWORD flags,
1633 DWORD flags2, const RECT *pRect)
1635 DTTOPTS opts = { 0 };
1636 RECT rt;
1638 TRACE("%d %d\n", iPartId, iStateId);
1640 rt = *pRect;
1642 opts.dwSize = sizeof(opts);
1643 if (flags2 & DTT_GRAYED) {
1644 opts.dwFlags = DTT_TEXTCOLOR;
1645 opts.crText = GetSysColor(COLOR_GRAYTEXT);
1647 return DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, iCharCount, flags, &rt, &opts);
1650 /***********************************************************************
1651 * DrawThemeTextEx (UXTHEME.@)
1653 HRESULT WINAPI DrawThemeTextEx(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1654 LPCWSTR pszText, int iCharCount, DWORD flags, RECT *rect, const DTTOPTS *options)
1656 HRESULT hr;
1657 HFONT hFont = NULL;
1658 HGDIOBJ oldFont = NULL;
1659 LOGFONTW logfont;
1660 COLORREF textColor;
1661 COLORREF oldTextColor;
1662 int oldBkMode;
1663 int fontProp;
1665 TRACE("%p %p %d %d %s:%d 0x%08x %p %p\n", hTheme, hdc, iPartId, iStateId,
1666 debugstr_wn(pszText, iCharCount), iCharCount, flags, rect, options);
1668 if(!hTheme)
1669 return E_HANDLE;
1671 if (options->dwFlags & ~(DTT_TEXTCOLOR | DTT_FONTPROP))
1672 FIXME("unsupported flags 0x%08x\n", options->dwFlags);
1674 if (options->dwFlags & DTT_FONTPROP)
1675 fontProp = options->iFontPropId;
1676 else
1677 fontProp = TMT_FONT;
1679 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, fontProp, &logfont);
1680 if(SUCCEEDED(hr)) {
1681 hFont = CreateFontIndirectW(&logfont);
1682 if(!hFont)
1683 TRACE("Failed to create font\n");
1686 if(hFont)
1687 oldFont = SelectObject(hdc, hFont);
1689 if (options->dwFlags & DTT_TEXTCOLOR)
1690 textColor = options->crText;
1691 else {
1692 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1693 textColor = GetTextColor(hdc);
1695 oldTextColor = SetTextColor(hdc, textColor);
1696 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1697 DrawTextW(hdc, pszText, iCharCount, rect, flags);
1698 SetBkMode(hdc, oldBkMode);
1699 SetTextColor(hdc, oldTextColor);
1701 if(hFont) {
1702 SelectObject(hdc, oldFont);
1703 DeleteObject(hFont);
1705 return S_OK;
1708 /***********************************************************************
1709 * GetThemeBackgroundContentRect (UXTHEME.@)
1711 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1712 int iStateId,
1713 const RECT *pBoundingRect,
1714 RECT *pContentRect)
1716 MARGINS margin;
1717 HRESULT hr;
1719 TRACE("(%d,%d)\n", iPartId, iStateId);
1720 if(!hTheme)
1721 return E_HANDLE;
1723 /* try content margins property... */
1724 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1725 if(SUCCEEDED(hr)) {
1726 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1727 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1728 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1729 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1730 } else {
1731 /* otherwise, try to determine content rect from the background type and props */
1732 int bgtype = BT_BORDERFILL;
1733 *pContentRect = *pBoundingRect;
1735 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1736 if(bgtype == BT_BORDERFILL) {
1737 int bordersize = 1;
1739 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1740 InflateRect(pContentRect, -bordersize, -bordersize);
1741 } else if ((bgtype == BT_IMAGEFILE)
1742 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1743 TMT_SIZINGMARGINS, NULL, &margin)))) {
1744 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1745 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1746 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1747 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1749 /* If nothing was found, leave unchanged */
1752 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1754 return S_OK;
1757 /***********************************************************************
1758 * GetThemeBackgroundExtent (UXTHEME.@)
1760 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1761 int iStateId, const RECT *pContentRect,
1762 RECT *pExtentRect)
1764 MARGINS margin;
1765 HRESULT hr;
1767 TRACE("(%d,%d)\n", iPartId, iStateId);
1768 if(!hTheme)
1769 return E_HANDLE;
1771 /* try content margins property... */
1772 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1773 if(SUCCEEDED(hr)) {
1774 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1775 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1776 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1777 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1778 } else {
1779 /* otherwise, try to determine content rect from the background type and props */
1780 int bgtype = BT_BORDERFILL;
1781 *pExtentRect = *pContentRect;
1783 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1784 if(bgtype == BT_BORDERFILL) {
1785 int bordersize = 1;
1787 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1788 InflateRect(pExtentRect, bordersize, bordersize);
1789 } else if ((bgtype == BT_IMAGEFILE)
1790 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1791 TMT_SIZINGMARGINS, NULL, &margin)))) {
1792 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1793 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1794 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1795 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1797 /* If nothing was found, leave unchanged */
1800 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1802 return S_OK;
1805 static inline void flush_rgn_data( HRGN rgn, RGNDATA *data )
1807 HRGN tmp = ExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1809 CombineRgn( rgn, rgn, tmp, RGN_OR );
1810 DeleteObject( tmp );
1811 data->rdh.nCount = 0;
1814 static inline void add_row( HRGN rgn, RGNDATA *data, int x, int y, int len )
1816 RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
1818 if (len <= 0) return;
1819 rect->left = x;
1820 rect->top = y;
1821 rect->right = x + len;
1822 rect->bottom = y + 1;
1823 data->rdh.nCount++;
1824 if (data->rdh.nCount * sizeof(RECT) > data->rdh.nRgnSize - sizeof(RECT))
1825 flush_rgn_data( rgn, data );
1828 static HRESULT create_image_bg_region(HTHEME theme, int part, int state, const RECT *rect, HRGN *rgn)
1830 RECT r;
1831 HDC dc;
1832 HBITMAP bmp;
1833 HRGN hrgn;
1834 BOOL istrans;
1835 COLORREF transcolour;
1836 HBRUSH transbrush;
1837 unsigned int x, y, start;
1838 BITMAPINFO bitmapinfo;
1839 DWORD *bits;
1840 char buffer[4096];
1841 RGNDATA *data = (RGNDATA *)buffer;
1843 if (FAILED(GetThemeBool(theme, part, state, TMT_TRANSPARENT, &istrans)) || !istrans) {
1844 *rgn = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
1845 return S_OK;
1848 r = *rect;
1849 OffsetRect(&r, -r.left, -r.top);
1851 if (FAILED(GetThemeColor(theme, part, state, TMT_TRANSPARENTCOLOR, &transcolour)))
1852 transcolour = RGB(255, 0, 255); /* defaults to magenta */
1854 dc = CreateCompatibleDC(NULL);
1855 if (!dc) {
1856 WARN("CreateCompatibleDC failed\n");
1857 return E_FAIL;
1860 bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1861 bitmapinfo.bmiHeader.biWidth = rect->right - rect->left;
1862 bitmapinfo.bmiHeader.biHeight = -(rect->bottom - rect->top);
1863 bitmapinfo.bmiHeader.biPlanes = 1;
1864 bitmapinfo.bmiHeader.biBitCount = 32;
1865 bitmapinfo.bmiHeader.biCompression = BI_RGB;
1866 bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * 4;
1867 bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
1868 bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
1869 bitmapinfo.bmiHeader.biClrUsed = 0;
1870 bitmapinfo.bmiHeader.biClrImportant = 0;
1872 bmp = CreateDIBSection(dc, &bitmapinfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
1873 if (!bmp) {
1874 WARN("CreateDIBSection failed\n");
1875 DeleteDC(dc);
1876 return E_FAIL;
1879 SelectObject(dc, bmp);
1881 transbrush = CreateSolidBrush(transcolour);
1882 FillRect(dc, &r, transbrush);
1883 DeleteObject(transbrush);
1885 if (FAILED(DrawThemeBackground(theme, dc, part, state, &r, NULL))) {
1886 WARN("DrawThemeBackground failed\n");
1887 DeleteObject(bmp);
1888 DeleteDC(dc);
1889 return E_FAIL;
1892 data->rdh.dwSize = sizeof(data->rdh);
1893 data->rdh.iType = RDH_RECTANGLES;
1894 data->rdh.nCount = 0;
1895 data->rdh.nRgnSize = sizeof(buffer) - sizeof(data->rdh);
1897 hrgn = CreateRectRgn(0, 0, 0, 0);
1899 for (y = 0; y < r.bottom; y++, bits += r.right) {
1900 x = 0;
1901 while (x < r.right) {
1902 while (x < r.right && (bits[x] & 0xffffff) == transcolour) x++;
1903 start = x;
1904 while (x < r.right && !((bits[x] & 0xffffff) == transcolour)) x++;
1905 add_row( hrgn, data, rect->left + start, rect->top + y, x - start );
1909 if (data->rdh.nCount > 0) flush_rgn_data(hrgn, data);
1911 *rgn = hrgn;
1913 DeleteObject(bmp);
1914 DeleteDC(dc);
1916 return S_OK;
1919 /***********************************************************************
1920 * GetThemeBackgroundRegion (UXTHEME.@)
1922 * Calculate the background region, taking into consideration transparent areas
1923 * of the background image.
1925 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1926 int iStateId, const RECT *pRect,
1927 HRGN *pRegion)
1929 HRESULT hr = S_OK;
1930 int bgtype = BT_BORDERFILL;
1932 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1933 if(!hTheme)
1934 return E_HANDLE;
1935 if(!pRect || !pRegion)
1936 return E_POINTER;
1938 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1939 if(bgtype == BT_IMAGEFILE) {
1940 hr = create_image_bg_region(hTheme, iPartId, iStateId, pRect, pRegion);
1942 else if(bgtype == BT_BORDERFILL) {
1943 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1944 if(!*pRegion)
1945 hr = HRESULT_FROM_WIN32(GetLastError());
1947 else {
1948 FIXME("Unknown background type\n");
1949 /* This should never happen, and hence I don't know what to return */
1950 hr = E_FAIL;
1952 return hr;
1955 /* compute part size for "borderfill" backgrounds */
1956 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1957 int iStateId, THEMESIZE eSize, POINT* psz)
1959 HRESULT hr = S_OK;
1960 int bordersize = 1;
1962 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1963 &bordersize)))
1965 psz->x = psz->y = 2*bordersize;
1966 if (eSize != TS_MIN)
1968 psz->x++;
1969 psz->y++;
1972 return hr;
1975 /***********************************************************************
1976 * GetThemePartSize (UXTHEME.@)
1978 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1979 int iStateId, RECT *prc, THEMESIZE eSize,
1980 SIZE *psz)
1982 int bgtype = BT_BORDERFILL;
1983 HRESULT hr = S_OK;
1984 POINT size = {1, 1};
1986 if(!hTheme)
1987 return E_HANDLE;
1989 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1990 if (bgtype == BT_NONE)
1991 /* do nothing */;
1992 else if(bgtype == BT_IMAGEFILE)
1993 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1994 else if(bgtype == BT_BORDERFILL)
1995 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1996 else {
1997 FIXME("Unknown background type\n");
1998 /* This should never happen, and hence I don't know what to return */
1999 hr = E_FAIL;
2001 psz->cx = size.x;
2002 psz->cy = size.y;
2003 return hr;
2007 /***********************************************************************
2008 * GetThemeTextExtent (UXTHEME.@)
2010 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2011 int iStateId, LPCWSTR pszText, int iCharCount,
2012 DWORD dwTextFlags, const RECT *pBoundingRect,
2013 RECT *pExtentRect)
2015 HRESULT hr;
2016 HFONT hFont = NULL;
2017 HGDIOBJ oldFont = NULL;
2018 LOGFONTW logfont;
2019 RECT rt = {0,0,0xFFFF,0xFFFF};
2021 TRACE("%d %d\n", iPartId, iStateId);
2022 if(!hTheme)
2023 return E_HANDLE;
2025 if(pBoundingRect)
2026 rt = *pBoundingRect;
2028 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2029 if(SUCCEEDED(hr)) {
2030 hFont = CreateFontIndirectW(&logfont);
2031 if(!hFont)
2032 TRACE("Failed to create font\n");
2034 if(hFont)
2035 oldFont = SelectObject(hdc, hFont);
2037 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2038 *pExtentRect = rt;
2040 if(hFont) {
2041 SelectObject(hdc, oldFont);
2042 DeleteObject(hFont);
2044 return S_OK;
2047 /***********************************************************************
2048 * GetThemeTextMetrics (UXTHEME.@)
2050 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2051 int iStateId, TEXTMETRICW *ptm)
2053 HRESULT hr;
2054 HFONT hFont = NULL;
2055 HGDIOBJ oldFont = NULL;
2056 LOGFONTW logfont;
2058 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2059 if(!hTheme)
2060 return E_HANDLE;
2062 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2063 if(SUCCEEDED(hr)) {
2064 hFont = CreateFontIndirectW(&logfont);
2065 if(!hFont)
2066 TRACE("Failed to create font\n");
2068 if(hFont)
2069 oldFont = SelectObject(hdc, hFont);
2071 if(!GetTextMetricsW(hdc, ptm))
2072 hr = HRESULT_FROM_WIN32(GetLastError());
2074 if(hFont) {
2075 SelectObject(hdc, oldFont);
2076 DeleteObject(hFont);
2078 return hr;
2081 /***********************************************************************
2082 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2084 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2085 int iStateId)
2087 int bgtype = BT_BORDERFILL;
2088 RECT rect = {0, 0, 0, 0};
2089 HBITMAP bmpSrc;
2090 RECT rcSrc;
2091 BOOL hasAlpha;
2092 INT transparent;
2093 COLORREF transparentcolor;
2095 TRACE("(%d,%d)\n", iPartId, iStateId);
2097 if(!hTheme)
2098 return FALSE;
2100 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2102 if (bgtype != BT_IMAGEFILE) return FALSE;
2104 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2105 &bmpSrc, &rcSrc, &hasAlpha)))
2106 return FALSE;
2108 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2109 &transparentcolor, FALSE);
2110 return (transparent != ALPHABLEND_NONE);