4db6a9cb835c67748907babab855288e3d7770c3
[reactos.git] / reactos / dll / win32 / uxtheme / draw.c
1 /*
2 * Win32 5.1 Theme drawing
3 *
4 * Copyright (C) 2003 Kevin Koltzau
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "uxthemep.h"
22
23 #include <stdlib.h>
24
25 /***********************************************************************
26 * Defines and global variables
27 */
28
29 extern ATOM atDialogThemeEnabled;
30
31 /***********************************************************************/
32
33 /***********************************************************************
34 * EnableThemeDialogTexture (UXTHEME.@)
35 */
36 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
37 {
38 static const WCHAR szTab[] = { 'T','a','b',0 };
39 BOOL res;
40
41 TRACE("(%p,0x%08x\n", hwnd, dwFlags);
42 res = SetPropW (hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled),
43 UlongToHandle(dwFlags|0x80000000));
44 /* 0x80000000 serves as a "flags set" flag */
45 if (!res)
46 return HRESULT_FROM_WIN32(GetLastError());
47 if (dwFlags & ETDT_USETABTEXTURE)
48 return SetWindowTheme (hwnd, NULL, szTab);
49 else
50 return SetWindowTheme (hwnd, NULL, NULL);
51 }
52
53 /***********************************************************************
54 * IsThemeDialogTextureEnabled (UXTHEME.@)
55 */
56 BOOL WINAPI IsThemeDialogTextureEnabled(HWND hwnd)
57 {
58 DWORD dwDialogTextureFlags;
59 TRACE("(%p)\n", hwnd);
60
61 dwDialogTextureFlags = HandleToUlong( GetPropW( hwnd, (LPCWSTR)MAKEINTATOM(atDialogThemeEnabled) ));
62 if (dwDialogTextureFlags == 0)
63 /* Means EnableThemeDialogTexture wasn't called for this dialog */
64 return FALSE;
65
66 return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
67 }
68
69 /***********************************************************************
70 * DrawThemeParentBackground (UXTHEME.@)
71 */
72 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
73 {
74 RECT rt;
75 POINT org;
76 HWND hParent;
77 HRGN clip = NULL;
78 int hasClip = -1;
79
80 TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
81
82 if (!IsWindow(hwnd) || !hdc)
83 return E_HANDLE;
84
85 if (prc && IsBadReadPtr (prc, sizeof(RECT)))
86 return E_POINTER;
87
88 hParent = GetParent(hwnd);
89 if(!hParent)
90 return S_OK;
91
92 if(prc) {
93 rt = *prc;
94 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
95
96 clip = CreateRectRgn(0,0,1,1);
97 hasClip = GetClipRgn(hdc, clip);
98 if(hasClip == -1)
99 TRACE("Failed to get original clipping region\n");
100 else
101 IntersectClipRect(hdc, prc->left, prc->top, prc->right, prc->bottom);
102 }
103 else {
104 GetClientRect(hwnd, &rt);
105 MapWindowPoints(hwnd, hParent, (LPPOINT)&rt, 2);
106 }
107
108 OffsetViewportOrgEx(hdc, -rt.left, -rt.top, &org);
109
110 SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
111 SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
112
113 SetViewportOrgEx(hdc, org.x, org.y, NULL);
114 if(prc) {
115 if(hasClip == 0)
116 SelectClipRgn(hdc, NULL);
117 else if(hasClip == 1)
118 SelectClipRgn(hdc, clip);
119 DeleteObject(clip);
120 }
121 return S_OK;
122 }
123
124
125 /***********************************************************************
126 * DrawThemeBackground (UXTHEME.@)
127 */
128 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
129 int iStateId, const RECT *pRect,
130 const RECT *pClipRect)
131 {
132 DTBGOPTS opts;
133 opts.dwSize = sizeof(DTBGOPTS);
134 opts.dwFlags = 0;
135 if(pClipRect) {
136 opts.dwFlags |= DTBG_CLIPRECT;
137 opts.rcClip = *pClipRect;
138 }
139 return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
140 }
141
142 /***********************************************************************
143 * UXTHEME_SelectImage
144 *
145 * Select the image to use
146 */
147 static PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
148 {
149 PTHEME_PROPERTY tp;
150 int imageselecttype = IST_NONE;
151 int i;
152 int image;
153 if(glyph)
154 image = TMT_GLYPHIMAGEFILE;
155 else
156 image = TMT_IMAGEFILE;
157
158 if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
159 return tp;
160 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
161
162 if(imageselecttype == IST_DPI) {
163 int reqdpi = 0;
164 int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
165 for(i=4; i>=0; i--) {
166 reqdpi = 0;
167 if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
168 if(reqdpi != 0 && screendpi >= reqdpi) {
169 TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
170 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
171 }
172 }
173 }
174 /* If an image couldn't be selected, choose the first one */
175 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
176 }
177 else if(imageselecttype == IST_SIZE) {
178 POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
179 POINT reqsize;
180 for(i=4; i>=0; i--) {
181 PTHEME_PROPERTY fileProp =
182 MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
183 if (!fileProp) continue;
184 if(FAILED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
185 /* fall back to size of Nth image */
186 WCHAR szPath[MAX_PATH];
187 int imagelayout = IL_HORIZONTAL;
188 int imagecount = 1;
189 BITMAP bmp;
190 HBITMAP hBmp;
191 BOOL hasAlpha;
192
193 lstrcpynW(szPath, fileProp->lpValue,
194 min(fileProp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
195 hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, &hasAlpha);
196 if(!hBmp) continue;
197
198 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
199 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
200
201 GetObjectW(hBmp, sizeof(bmp), &bmp);
202 if(imagelayout == IL_VERTICAL) {
203 reqsize.x = bmp.bmWidth;
204 reqsize.y = bmp.bmHeight/imagecount;
205 }
206 else {
207 reqsize.x = bmp.bmWidth/imagecount;
208 reqsize.y = bmp.bmHeight;
209 }
210 }
211 if(reqsize.x <= size.x && reqsize.y <= size.y) {
212 TRACE("Using image size %dx%d, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
213 return fileProp;
214 }
215 }
216 /* If an image couldn't be selected, choose the smallest one */
217 return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
218 }
219 return NULL;
220 }
221
222 /***********************************************************************
223 * UXTHEME_LoadImage
224 *
225 * Load image for part/state
226 */
227 HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
228 HBITMAP *hBmp, RECT *bmpRect, BOOL* hasImageAlpha)
229 {
230 int imagelayout = IL_HORIZONTAL;
231 int imagecount = 1;
232 int imagenum;
233 BITMAP bmp;
234 WCHAR szPath[MAX_PATH];
235 PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
236 if(!tp) {
237 FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
238 return E_PROP_ID_UNSUPPORTED;
239 }
240 lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
241 *hBmp = MSSTYLES_LoadBitmap(hTheme, szPath, hasImageAlpha);
242 if(!*hBmp) {
243 TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
244 return HRESULT_FROM_WIN32(GetLastError());
245 }
246
247 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGELAYOUT, &imagelayout);
248 GetThemeInt(hTheme, iPartId, iStateId, TMT_IMAGECOUNT, &imagecount);
249
250 imagenum = max (min (imagecount, iStateId), 1) - 1;
251 GetObjectW(*hBmp, sizeof(bmp), &bmp);
252
253 if(imagecount < 1) imagecount = 1;
254
255 if(imagelayout == IL_VERTICAL) {
256 int height = bmp.bmHeight/imagecount;
257 bmpRect->left = 0;
258 bmpRect->right = bmp.bmWidth;
259 bmpRect->top = imagenum * height;
260 bmpRect->bottom = bmpRect->top + height;
261 }
262 else {
263 int width = bmp.bmWidth/imagecount;
264 bmpRect->left = imagenum * width;
265 bmpRect->right = bmpRect->left + width;
266 bmpRect->top = 0;
267 bmpRect->bottom = bmp.bmHeight;
268 }
269 return S_OK;
270 }
271
272 /***********************************************************************
273 * UXTHEME_StretchBlt
274 *
275 * Pseudo TransparentBlt/StretchBlt
276 */
277 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
278 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
279 INT transparent, COLORREF transcolor)
280 {
281 static const BLENDFUNCTION blendFunc =
282 {
283 AC_SRC_OVER, /* BlendOp */
284 0, /* BlendFlag */
285 255, /* SourceConstantAlpha */
286 AC_SRC_ALPHA /* AlphaFormat */
287 };
288
289 BOOL ret = TRUE;
290 int old_stretch_mode;
291 POINT old_brush_org;
292
293 old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
294 SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
295
296 if (transparent == ALPHABLEND_BINARY) {
297 /* Ensure we don't pass any negative values to TransparentBlt */
298 ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
299 hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
300 transcolor);
301 } else if ((transparent == ALPHABLEND_NONE) ||
302 !AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
303 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
304 blendFunc))
305 {
306 ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
307 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
308 SRCCOPY);
309 }
310
311 SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
312 SetStretchBltMode(hdcDst, old_stretch_mode);
313
314 return ret;
315 }
316
317 /***********************************************************************
318 * UXTHEME_Blt
319 *
320 * Simplify sending same width/height for both source and dest
321 */
322 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
323 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
324 INT transparent, COLORREF transcolor)
325 {
326 return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
327 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
328 transparent, transcolor);
329 }
330
331 /***********************************************************************
332 * UXTHEME_SizedBlt
333 *
334 * Stretches or tiles, depending on sizingtype.
335 */
336 static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
337 int nWidthDst, int nHeightDst,
338 HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
339 int nWidthSrc, int nHeightSrc,
340 int sizingtype,
341 INT transparent, COLORREF transcolor)
342 {
343 if (sizingtype == ST_TILE)
344 {
345 HDC hdcTemp;
346 BOOL result = FALSE;
347
348 if (!nWidthSrc || !nHeightSrc) return TRUE;
349
350 /* For destination width/height less than or equal to source
351 width/height, do not bother with memory bitmap optimization */
352 if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
353 {
354 int bltWidth = min (nWidthDst, nWidthSrc);
355 int bltHeight = min (nHeightDst, nHeightSrc);
356
357 return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
358 hdcSrc, nXOriginSrc, nYOriginSrc,
359 transparent, transcolor);
360 }
361
362 /* Create a DC with a bitmap consisting of a tiling of the source
363 bitmap, with standard GDI functions. This is faster than an
364 iteration with UXTHEME_Blt(). */
365 hdcTemp = CreateCompatibleDC(hdcSrc);
366 if (hdcTemp != 0)
367 {
368 HBITMAP bitmapTemp;
369 HBITMAP bitmapOrig;
370 int nWidthTemp, nHeightTemp;
371 int xOfs, xRemaining;
372 int yOfs, yRemaining;
373 int growSize;
374
375 /* Calculate temp dimensions of integer multiples of source dimensions */
376 nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
377 nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
378 bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
379 bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
380
381 /* Initial copy of bitmap */
382 BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
383
384 /* Extend bitmap in the X direction. Growth of width is exponential */
385 xOfs = nWidthSrc;
386 xRemaining = nWidthTemp - nWidthSrc;
387 growSize = nWidthSrc;
388 while (xRemaining > 0)
389 {
390 growSize = min(growSize, xRemaining);
391 BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
392 xOfs += growSize;
393 xRemaining -= growSize;
394 growSize *= 2;
395 }
396
397 /* Extend bitmap in the Y direction. Growth of height is exponential */
398 yOfs = nHeightSrc;
399 yRemaining = nHeightTemp - nHeightSrc;
400 growSize = nHeightSrc;
401 while (yRemaining > 0)
402 {
403 growSize = min(growSize, yRemaining);
404 BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
405 yOfs += growSize;
406 yRemaining -= growSize;
407 growSize *= 2;
408 }
409
410 /* Use temporary hdc for source */
411 result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
412 hdcTemp, 0, 0,
413 transparent, transcolor);
414
415 SelectObject(hdcTemp, bitmapOrig);
416 DeleteObject(bitmapTemp);
417 }
418 DeleteDC(hdcTemp);
419 return result;
420 }
421 else
422 {
423 return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
424 hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
425 transparent, transcolor);
426 }
427 }
428
429 /* Get transparency parameters passed to UXTHEME_StretchBlt() - the parameters
430 * depend on whether the image has full alpha or whether it is
431 * color-transparent or just opaque. */
432 static inline void get_transparency (HTHEME hTheme, int iPartId, int iStateId,
433 BOOL hasImageAlpha, INT* transparent,
434 COLORREF* transparentcolor, BOOL glyph)
435 {
436 if (hasImageAlpha)
437 {
438 *transparent = ALPHABLEND_FULL;
439 *transparentcolor = RGB (255, 0, 255);
440 }
441 else
442 {
443 BOOL trans = FALSE;
444 GetThemeBool(hTheme, iPartId, iStateId,
445 glyph ? TMT_GLYPHTRANSPARENT : TMT_TRANSPARENT, &trans);
446 if(trans) {
447 *transparent = ALPHABLEND_BINARY;
448 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId,
449 glyph ? TMT_GLYPHTRANSPARENTCOLOR : TMT_TRANSPARENTCOLOR,
450 transparentcolor))) {
451 /* If image is transparent, but no color was specified, use magenta */
452 *transparentcolor = RGB(255, 0, 255);
453 }
454 }
455 else
456 *transparent = ALPHABLEND_NONE;
457 }
458 }
459
460 /***********************************************************************
461 * UXTHEME_DrawImageGlyph
462 *
463 * Draw an imagefile glyph
464 */
465 static HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
466 int iStateId, RECT *pRect,
467 const DTBGOPTS *pOptions)
468 {
469 HRESULT hr;
470 HBITMAP bmpSrc = NULL;
471 HDC hdcSrc = NULL;
472 HGDIOBJ oldSrc = NULL;
473 RECT rcSrc;
474 INT transparent = 0;
475 COLORREF transparentcolor;
476 int valign = VA_CENTER;
477 int halign = HA_CENTER;
478 POINT dstSize;
479 POINT srcSize;
480 POINT topleft;
481 BOOL hasAlpha;
482
483 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE,
484 &bmpSrc, &rcSrc, &hasAlpha);
485 if(FAILED(hr)) return hr;
486 hdcSrc = CreateCompatibleDC(hdc);
487 if(!hdcSrc) {
488 hr = HRESULT_FROM_WIN32(GetLastError());
489 return hr;
490 }
491 oldSrc = SelectObject(hdcSrc, bmpSrc);
492
493 dstSize.x = pRect->right-pRect->left;
494 dstSize.y = pRect->bottom-pRect->top;
495 srcSize.x = rcSrc.right-rcSrc.left;
496 srcSize.y = rcSrc.bottom-rcSrc.top;
497
498 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
499 &transparentcolor, TRUE);
500 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
501 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
502
503 topleft.x = pRect->left;
504 topleft.y = pRect->top;
505 if(halign == HA_CENTER) topleft.x += (dstSize.x/2)-(srcSize.x/2);
506 else if(halign == HA_RIGHT) topleft.x += dstSize.x-srcSize.x;
507 if(valign == VA_CENTER) topleft.y += (dstSize.y/2)-(srcSize.y/2);
508 else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
509
510 if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
511 hdcSrc, rcSrc.left, rcSrc.top,
512 transparent, transparentcolor)) {
513 hr = HRESULT_FROM_WIN32(GetLastError());
514 }
515
516 SelectObject(hdcSrc, oldSrc);
517 DeleteDC(hdcSrc);
518 return hr;
519 }
520
521 /***********************************************************************
522 * UXTHEME_DrawImageGlyph
523 *
524 * Draw glyph on top of background, if appropriate
525 */
526 static HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
527 int iStateId, RECT *pRect,
528 const DTBGOPTS *pOptions)
529 {
530 int glyphtype = GT_NONE;
531
532 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
533
534 if(glyphtype == GT_IMAGEGLYPH) {
535 return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
536 }
537 else if(glyphtype == GT_FONTGLYPH) {
538 /* I don't know what a font glyph is, I've never seen it used in any themes */
539 FIXME("Font glyph\n");
540 }
541 return S_OK;
542 }
543
544 /***********************************************************************
545 * get_image_part_size
546 *
547 * Used by GetThemePartSize and UXTHEME_DrawImageBackground
548 */
549 static HRESULT get_image_part_size (HTHEME hTheme, HDC hdc, int iPartId,
550 int iStateId, RECT *prc, THEMESIZE eSize,
551 POINT *psz)
552 {
553 HRESULT hr = S_OK;
554 HBITMAP bmpSrc;
555 RECT rcSrc;
556 BOOL hasAlpha;
557
558 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, prc, FALSE,
559 &bmpSrc, &rcSrc, &hasAlpha);
560 if (FAILED(hr)) return hr;
561
562 switch (eSize)
563 {
564 case TS_DRAW:
565 if (prc != NULL)
566 {
567 RECT rcDst;
568 POINT dstSize;
569 POINT srcSize;
570 int sizingtype = ST_STRETCH;
571 BOOL uniformsizing = FALSE;
572
573 rcDst = *prc;
574
575 dstSize.x = rcDst.right-rcDst.left;
576 dstSize.y = rcDst.bottom-rcDst.top;
577 srcSize.x = rcSrc.right-rcSrc.left;
578 srcSize.y = rcSrc.bottom-rcSrc.top;
579
580 GetThemeBool(hTheme, iPartId, iStateId, TMT_UNIFORMSIZING, &uniformsizing);
581 if(uniformsizing) {
582 /* Scale height and width equally */
583 if (dstSize.x*srcSize.y < dstSize.y*srcSize.x)
584 {
585 dstSize.y = MulDiv (srcSize.y, dstSize.x, srcSize.x);
586 rcDst.bottom = rcDst.top + dstSize.y;
587 }
588 else
589 {
590 dstSize.x = MulDiv (srcSize.x, dstSize.y, srcSize.y);
591 rcDst.right = rcDst.left + dstSize.x;
592 }
593 }
594
595 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
596 if(sizingtype == ST_TRUESIZE) {
597 int truesizestretchmark = 100;
598
599 if(dstSize.x < 0 || dstSize.y < 0) {
600 BOOL mirrorimage = TRUE;
601 GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
602 if(mirrorimage) {
603 if(dstSize.x < 0) {
604 rcDst.left += dstSize.x;
605 rcDst.right += dstSize.x;
606 }
607 if(dstSize.y < 0) {
608 rcDst.top += dstSize.y;
609 rcDst.bottom += dstSize.y;
610 }
611 }
612 }
613 /* Whatever TrueSizeStretchMark does - it does not seem to
614 * be what's outlined below. It appears as if native
615 * uxtheme always stretches if dest is smaller than source
616 * (ie as if TrueSizeStretchMark==100 with the code below) */
617 #if 0
618 /* Only stretch when target exceeds source by truesizestretchmark percent */
619 GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
620 #endif
621 if(dstSize.x < 0 || dstSize.y < 0 ||
622 (MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark &&
623 MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark)) {
624 memcpy (psz, &dstSize, sizeof (SIZE));
625 }
626 else {
627 memcpy (psz, &srcSize, sizeof (SIZE));
628 }
629 }
630 else
631 {
632 psz->x = abs(dstSize.x);
633 psz->y = abs(dstSize.y);
634 }
635 break;
636 }
637 /* else fall through */
638 case TS_MIN:
639 /* FIXME: couldn't figure how native uxtheme computes min size */
640 case TS_TRUE:
641 psz->x = rcSrc.right - rcSrc.left;
642 psz->y = rcSrc.bottom - rcSrc.top;
643 break;
644 }
645 return hr;
646 }
647
648 /***********************************************************************
649 * UXTHEME_DrawImageBackground
650 *
651 * Draw an imagefile background
652 */
653 static HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
654 int iStateId, RECT *pRect,
655 const DTBGOPTS *pOptions)
656 {
657 HRESULT hr = S_OK;
658 HBITMAP bmpSrc, bmpSrcResized = NULL;
659 HGDIOBJ oldSrc;
660 HDC hdcSrc, hdcOrigSrc = NULL;
661 RECT rcSrc;
662 RECT rcDst;
663 POINT dstSize;
664 POINT srcSize;
665 POINT drawSize;
666 int sizingtype = ST_STRETCH;
667 INT transparent;
668 COLORREF transparentcolor = 0;
669 BOOL hasAlpha;
670
671 hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE,
672 &bmpSrc, &rcSrc, &hasAlpha);
673 if(FAILED(hr)) return hr;
674 hdcSrc = CreateCompatibleDC(hdc);
675 if(!hdcSrc) {
676 hr = HRESULT_FROM_WIN32(GetLastError());
677 return hr;
678 }
679 oldSrc = SelectObject(hdcSrc, bmpSrc);
680
681 rcDst = *pRect;
682
683 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
684 &transparentcolor, FALSE);
685
686 dstSize.x = rcDst.right-rcDst.left;
687 dstSize.y = rcDst.bottom-rcDst.top;
688 srcSize.x = rcSrc.right-rcSrc.left;
689 srcSize.y = rcSrc.bottom-rcSrc.top;
690
691 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
692 if(sizingtype == ST_TRUESIZE) {
693 int valign = VA_CENTER, halign = HA_CENTER;
694
695 get_image_part_size (hTheme, hdc, iPartId, iStateId, pRect, TS_DRAW, &drawSize);
696 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
697 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
698
699 if (halign == HA_CENTER)
700 rcDst.left += (dstSize.x/2)-(drawSize.x/2);
701 else if (halign == HA_RIGHT)
702 rcDst.left = rcDst.right - drawSize.x;
703 if (valign == VA_CENTER)
704 rcDst.top += (dstSize.y/2)-(drawSize.y/2);
705 else if (valign == VA_BOTTOM)
706 rcDst.top = rcDst.bottom - drawSize.y;
707 rcDst.right = rcDst.left + drawSize.x;
708 rcDst.bottom = rcDst.top + drawSize.y;
709 if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, drawSize.x, drawSize.y,
710 hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
711 transparent, transparentcolor))
712 hr = HRESULT_FROM_WIN32(GetLastError());
713 }
714 else {
715 HDC hdcDst = NULL;
716 MARGINS sm;
717 POINT org;
718
719 dstSize.x = abs(dstSize.x);
720 dstSize.y = abs(dstSize.y);
721
722 GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
723
724 /* Resize source image if destination smaller than margins */
725 #ifndef __REACTOS__
726 /* Revert Wine Commit 2b650fa as it breaks themed Explorer Toolbar Separators
727 FIXME: Revisit this when the bug is fixed. CORE-9636 and Wine Bug #38538 */
728 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
729 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
730 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
731 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
732 srcSize.y = dstSize.y;
733 }
734
735 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
736 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
737 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
738 srcSize.x = dstSize.x;
739 }
740
741 hdcOrigSrc = hdcSrc;
742 hdcSrc = CreateCompatibleDC(NULL);
743 bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
744 SelectObject(hdcSrc, bmpSrcResized);
745
746 UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
747 rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
748
749 rcSrc.left = 0;
750 rcSrc.top = 0;
751 rcSrc.right = srcSize.x;
752 rcSrc.bottom = srcSize.y;
753 }
754 #endif /* __REACTOS__ */
755
756 hdcDst = hdc;
757 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
758
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;
765 }
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;
773 }
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;
781 }
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;
789 }
790
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);
796
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;
806 }
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;
815 }
816 }
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;
827 }
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;
836 }
837 }
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;
850 }
851 }
852 }
853 }
854
855 draw_error:
856 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
857 }
858 SelectObject(hdcSrc, oldSrc);
859 DeleteDC(hdcSrc);
860 if (bmpSrcResized) DeleteObject(bmpSrcResized);
861 if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
862 *pRect = rcDst;
863 return hr;
864 }
865
866 /***********************************************************************
867 * UXTHEME_DrawBorderRectangle
868 *
869 * Draw the bounding rectangle for a borderfill background
870 */
871 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
872 int iStateId, RECT *pRect,
873 const DTBGOPTS *pOptions)
874 {
875 HRESULT hr = S_OK;
876 HPEN hPen;
877 HGDIOBJ oldPen;
878 COLORREF bordercolor = RGB(0,0,0);
879 int bordersize = 1;
880
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;
894
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);
903
904 if(!Polyline(hdc, ptCorners, 5))
905 hr = HRESULT_FROM_WIN32(GetLastError());
906
907 SelectObject(hdc, oldPen);
908 DeleteObject(hPen);
909 }
910 return hr;
911 }
912
913 /***********************************************************************
914 * UXTHEME_DrawBackgroundFill
915 *
916 * Fill a borderfill background rectangle
917 */
918 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
919 int iStateId, RECT *pRect,
920 const DTBGOPTS *pOptions)
921 {
922 HRESULT hr = S_OK;
923 int filltype = FT_SOLID;
924
925 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
926
927 if(pOptions->dwFlags & DTBG_OMITCONTENT)
928 return S_OK;
929
930 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
931
932 if(filltype == FT_SOLID) {
933 HBRUSH hBrush;
934 COLORREF fillcolor = RGB(255,255,255);
935
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);
941 }
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
947 */
948
949 COLORREF gradient1 = RGB(0,0,0);
950 COLORREF gradient2 = RGB(255,255,255);
951 TRIVERTEX vert[2];
952 GRADIENT_RECT gRect;
953
954 FIXME("Gradient implementation not complete\n");
955
956 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
957 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
958
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;
965
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;
972
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);
976 }
977 else if(filltype == FT_RADIALGRADIENT) {
978 /* I've never seen this used in a theme */
979 FIXME("Radial gradient\n");
980 }
981 else if(filltype == FT_TILEIMAGE) {
982 /* I've never seen this used in a theme */
983 FIXME("Tile image\n");
984 }
985 return hr;
986 }
987
988 /***********************************************************************
989 * UXTHEME_DrawBorderBackground
990 *
991 * Draw an imagefile background
992 */
993 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
994 int iStateId, const RECT *pRect,
995 const DTBGOPTS *pOptions)
996 {
997 HRESULT hr;
998 RECT rt;
999
1000 rt = *pRect;
1001
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);
1006 }
1007
1008 /***********************************************************************
1009 * DrawThemeBackgroundEx (UXTHEME.@)
1010 */
1011 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1012 int iStateId, const RECT *pRect,
1013 const DTBGOPTS *pOptions)
1014 {
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;
1022
1023 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1024 if(!hTheme)
1025 return E_HANDLE;
1026
1027 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1028 if (bgtype == BT_NONE) return S_OK;
1029
1030 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1031 opts = pOptions;
1032 if(!opts) opts = &defaultOpts;
1033
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);
1041 }
1042 rt = *pRect;
1043
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;
1052 }
1053 if(SUCCEEDED(hr))
1054 {
1055 RECT rcGlyph = *pRect;
1056 MARGINS margin;
1057 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1058 if(SUCCEEDED(hr))
1059 {
1060 rcGlyph.left += margin.cxLeftWidth;
1061 rcGlyph.right -= margin.cxRightWidth;
1062 rcGlyph.top += margin.cyTopHeight;
1063 rcGlyph.bottom -= margin.cyBottomHeight;
1064 }
1065 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rcGlyph, opts);
1066 }
1067 if(opts->dwFlags & DTBG_CLIPRECT) {
1068 if(hasClip == 0)
1069 SelectClipRgn(hdc, NULL);
1070 else if(hasClip == 1)
1071 SelectClipRgn(hdc, clip);
1072 DeleteObject(clip);
1073 }
1074 return hr;
1075 }
1076
1077 /*
1078 * DrawThemeEdge() implementation
1079 *
1080 * Since it basically is DrawEdge() with different colors, I copied its code
1081 * from user32's uitools.c.
1082 */
1083
1084 enum
1085 {
1086 EDGE_LIGHT,
1087 EDGE_HIGHLIGHT,
1088 EDGE_SHADOW,
1089 EDGE_DARKSHADOW,
1090 EDGE_FILL,
1091
1092 EDGE_WINDOW,
1093 EDGE_WINDOWFRAME,
1094
1095 EDGE_NUMCOLORS
1096 };
1097
1098 static const struct
1099 {
1100 int themeProp;
1101 int sysColor;
1102 } EdgeColorMap[EDGE_NUMCOLORS] = {
1103 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1104 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1105 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1106 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1107 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1108 {-1, COLOR_WINDOW},
1109 {-1, COLOR_WINDOWFRAME}
1110 };
1111
1112 static const signed char LTInnerNormal[] = {
1113 -1, -1, -1, -1,
1114 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1115 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1116 -1, -1, -1, -1
1117 };
1118
1119 static const signed char LTOuterNormal[] = {
1120 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1121 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1122 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1123 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1124 };
1125
1126 static const signed char RBInnerNormal[] = {
1127 -1, -1, -1, -1,
1128 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1129 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1130 -1, -1, -1, -1
1131 };
1132
1133 static const signed char RBOuterNormal[] = {
1134 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1135 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1136 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1137 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1138 };
1139
1140 static const signed char LTInnerSoft[] = {
1141 -1, -1, -1, -1,
1142 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1143 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1144 -1, -1, -1, -1
1145 };
1146
1147 static const signed char LTOuterSoft[] = {
1148 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1149 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1150 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1151 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1152 };
1153
1154 #define RBInnerSoft RBInnerNormal /* These are the same */
1155 #define RBOuterSoft RBOuterNormal
1156
1157 static const signed char LTRBOuterMono[] = {
1158 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1159 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1160 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1161 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1162 };
1163
1164 static const signed char LTRBInnerMono[] = {
1165 -1, -1, -1, -1,
1166 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1167 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1168 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1169 };
1170
1171 static const signed char LTRBOuterFlat[] = {
1172 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1173 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1174 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1175 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1176 };
1177
1178 static const signed char LTRBInnerFlat[] = {
1179 -1, -1, -1, -1,
1180 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1181 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1182 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1183 };
1184
1185 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1186 {
1187 COLORREF col;
1188 if ((EdgeColorMap[edgeType].themeProp == -1)
1189 || FAILED (GetThemeColor (theme, part, state,
1190 EdgeColorMap[edgeType].themeProp, &col)))
1191 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1192 return col;
1193 }
1194
1195 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1196 {
1197 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1198 }
1199
1200 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1201 {
1202 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1203 }
1204
1205 /***********************************************************************
1206 * draw_diag_edge
1207 *
1208 * Same as DrawEdge invoked with BF_DIAGONAL
1209 */
1210 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1211 const RECT* rc, UINT uType,
1212 UINT uFlags, LPRECT contentsRect)
1213 {
1214 POINT Points[4];
1215 signed char InnerI, OuterI;
1216 HPEN InnerPen, OuterPen;
1217 POINT SavePoint;
1218 HPEN SavePen;
1219 int spx, spy;
1220 int epx, epy;
1221 int Width = rc->right - rc->left;
1222 int Height= rc->bottom - rc->top;
1223 int SmallDiam = Width > Height ? Height : Width;
1224 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1225 || (uType & BDR_OUTER) == BDR_OUTER)
1226 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1227 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1228 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1229
1230 /* Init some vars */
1231 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1232 SavePen = SelectObject(hdc, InnerPen);
1233 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1234
1235 /* Determine the colors of the edges */
1236 if(uFlags & BF_MONO)
1237 {
1238 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1239 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1240 }
1241 else if(uFlags & BF_FLAT)
1242 {
1243 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1244 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1245 }
1246 else if(uFlags & BF_SOFT)
1247 {
1248 if(uFlags & BF_BOTTOM)
1249 {
1250 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1251 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1252 }
1253 else
1254 {
1255 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1256 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1257 }
1258 }
1259 else
1260 {
1261 if(uFlags & BF_BOTTOM)
1262 {
1263 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1264 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1265 }
1266 else
1267 {
1268 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1269 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1270 }
1271 }
1272
1273 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1274 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1275
1276 MoveToEx(hdc, 0, 0, &SavePoint);
1277
1278 /* Don't ask me why, but this is what is visible... */
1279 /* This must be possible to do much simpler, but I fail to */
1280 /* see the logic in the MS implementation (sigh...). */
1281 /* So, this might look a bit brute force here (and it is), but */
1282 /* it gets the job done;) */
1283
1284 switch(uFlags & BF_RECT)
1285 {
1286 case 0:
1287 case BF_LEFT:
1288 case BF_BOTTOM:
1289 case BF_BOTTOMLEFT:
1290 /* Left bottom endpoint */
1291 epx = rc->left-1;
1292 spx = epx + SmallDiam;
1293 epy = rc->bottom;
1294 spy = epy - SmallDiam;
1295 break;
1296
1297 case BF_TOPLEFT:
1298 case BF_BOTTOMRIGHT:
1299 /* Left top endpoint */
1300 epx = rc->left-1;
1301 spx = epx + SmallDiam;
1302 epy = rc->top-1;
1303 spy = epy + SmallDiam;
1304 break;
1305
1306 case BF_TOP:
1307 case BF_RIGHT:
1308 case BF_TOPRIGHT:
1309 case BF_RIGHT|BF_LEFT:
1310 case BF_RIGHT|BF_LEFT|BF_TOP:
1311 case BF_BOTTOM|BF_TOP:
1312 case BF_BOTTOM|BF_TOP|BF_LEFT:
1313 case BF_BOTTOMRIGHT|BF_LEFT:
1314 case BF_BOTTOMRIGHT|BF_TOP:
1315 case BF_RECT:
1316 /* Right top endpoint */
1317 spx = rc->left;
1318 epx = spx + SmallDiam;
1319 spy = rc->bottom-1;
1320 epy = spy - SmallDiam;
1321 break;
1322 }
1323
1324 MoveToEx(hdc, spx, spy, NULL);
1325 SelectObject(hdc, OuterPen);
1326 LineTo(hdc, epx, epy);
1327
1328 SelectObject(hdc, InnerPen);
1329
1330 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1331 {
1332 case BF_DIAGONAL_ENDBOTTOMLEFT:
1333 case (BF_DIAGONAL|BF_BOTTOM):
1334 case BF_DIAGONAL:
1335 case (BF_DIAGONAL|BF_LEFT):
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->top;
1342 Points[2].x = epx+1;
1343 Points[2].y = epy-1-add;
1344 Points[3] = Points[2];
1345 break;
1346
1347 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1348 MoveToEx(hdc, spx-1, spy, NULL);
1349 LineTo(hdc, epx, epy+1);
1350 Points[0].x = spx-add;
1351 Points[0].y = spy;
1352 Points[1].x = rc->left;
1353 Points[1].y = rc->bottom-1;
1354 Points[2].x = epx+1;
1355 Points[2].y = epy+1+add;
1356 Points[3] = Points[2];
1357 break;
1358
1359 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1360 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1361 case BF_DIAGONAL_ENDTOPRIGHT:
1362 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1363 MoveToEx(hdc, spx+1, spy, NULL);
1364 LineTo(hdc, epx, epy+1);
1365 Points[0].x = epx-1;
1366 Points[0].y = epy+1+add;
1367 Points[1].x = rc->right-1;
1368 Points[1].y = rc->top+add;
1369 Points[2].x = rc->right-1;
1370 Points[2].y = rc->bottom-1;
1371 Points[3].x = spx+add;
1372 Points[3].y = spy;
1373 break;
1374
1375 case BF_DIAGONAL_ENDTOPLEFT:
1376 MoveToEx(hdc, spx, spy-1, NULL);
1377 LineTo(hdc, epx+1, epy);
1378 Points[0].x = epx+1+add;
1379 Points[0].y = epy+1;
1380 Points[1].x = rc->right-1;
1381 Points[1].y = rc->top;
1382 Points[2].x = rc->right-1;
1383 Points[2].y = rc->bottom-1-add;
1384 Points[3].x = spx;
1385 Points[3].y = spy-add;
1386 break;
1387
1388 case (BF_DIAGONAL|BF_TOP):
1389 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1390 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1391 MoveToEx(hdc, spx+1, spy-1, NULL);
1392 LineTo(hdc, epx, epy);
1393 Points[0].x = epx-1;
1394 Points[0].y = epy+1;
1395 Points[1].x = rc->right-1;
1396 Points[1].y = rc->top;
1397 Points[2].x = rc->right-1;
1398 Points[2].y = rc->bottom-1-add;
1399 Points[3].x = spx+add;
1400 Points[3].y = spy-add;
1401 break;
1402
1403 case (BF_DIAGONAL|BF_RIGHT):
1404 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1405 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1406 MoveToEx(hdc, spx, spy, NULL);
1407 LineTo(hdc, epx-1, epy+1);
1408 Points[0].x = spx;
1409 Points[0].y = spy;
1410 Points[1].x = rc->left;
1411 Points[1].y = rc->top+add;
1412 Points[2].x = epx-1-add;
1413 Points[2].y = epy+1+add;
1414 Points[3] = Points[2];
1415 break;
1416 }
1417
1418 /* Fill the interior if asked */
1419 if((uFlags & BF_MIDDLE) && retval)
1420 {
1421 HBRUSH hbsave;
1422 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1423 theme, part, state);
1424 HPEN hpsave;
1425 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1426 theme, part, state);
1427 hbsave = SelectObject(hdc, hb);
1428 hpsave = SelectObject(hdc, hp);
1429 Polygon(hdc, Points, 4);
1430 SelectObject(hdc, hbsave);
1431 SelectObject(hdc, hpsave);
1432 DeleteObject (hp);
1433 DeleteObject (hb);
1434 }
1435
1436 /* Adjust rectangle if asked */
1437 if(uFlags & BF_ADJUST)
1438 {
1439 *contentsRect = *rc;
1440 if(uFlags & BF_LEFT) contentsRect->left += add;
1441 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1442 if(uFlags & BF_TOP) contentsRect->top += add;
1443 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1444 }
1445
1446 /* Cleanup */
1447 SelectObject(hdc, SavePen);
1448 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1449 if(InnerI != -1) DeleteObject (InnerPen);
1450 if(OuterI != -1) DeleteObject (OuterPen);
1451
1452 return retval;
1453 }
1454
1455 /***********************************************************************
1456 * draw_rect_edge
1457 *
1458 * Same as DrawEdge invoked without BF_DIAGONAL
1459 */
1460 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1461 const RECT* rc, UINT uType,
1462 UINT uFlags, LPRECT contentsRect)
1463 {
1464 signed char LTInnerI, LTOuterI;
1465 signed char RBInnerI, RBOuterI;
1466 HPEN LTInnerPen, LTOuterPen;
1467 HPEN RBInnerPen, RBOuterPen;
1468 RECT InnerRect = *rc;
1469 POINT SavePoint;
1470 HPEN SavePen;
1471 int LBpenplus = 0;
1472 int LTpenplus = 0;
1473 int RTpenplus = 0;
1474 int RBpenplus = 0;
1475 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1476 || (uType & BDR_OUTER) == BDR_OUTER)
1477 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1478
1479 /* Init some vars */
1480 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1481 SavePen = SelectObject(hdc, LTInnerPen);
1482
1483 /* Determine the colors of the edges */
1484 if(uFlags & BF_MONO)
1485 {
1486 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1487 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1488 }
1489 else if(uFlags & BF_FLAT)
1490 {
1491 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1492 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1493
1494 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1495 }
1496 else if(uFlags & BF_SOFT)
1497 {
1498 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1499 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1500 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1501 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1502 }
1503 else
1504 {
1505 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1506 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1507 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1508 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1509 }
1510
1511 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1512 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1513 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1514 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1515
1516 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1517 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1518 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1519 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1520
1521 MoveToEx(hdc, 0, 0, &SavePoint);
1522
1523 /* Draw the outer edge */
1524 SelectObject(hdc, LTOuterPen);
1525 if(uFlags & BF_TOP)
1526 {
1527 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1528 LineTo(hdc, InnerRect.right, InnerRect.top);
1529 }
1530 if(uFlags & BF_LEFT)
1531 {
1532 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1533 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1534 }
1535 SelectObject(hdc, RBOuterPen);
1536 if(uFlags & BF_BOTTOM)
1537 {
1538 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1539 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1540 }
1541 if(uFlags & BF_RIGHT)
1542 {
1543 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1544 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1545 }
1546
1547 /* Draw the inner edge */
1548 SelectObject(hdc, LTInnerPen);
1549 if(uFlags & BF_TOP)
1550 {
1551 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1552 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1553 }
1554 if(uFlags & BF_LEFT)
1555 {
1556 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1557 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1558 }
1559 SelectObject(hdc, RBInnerPen);
1560 if(uFlags & BF_BOTTOM)
1561 {
1562 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1563 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1564 }
1565 if(uFlags & BF_RIGHT)
1566 {
1567 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1568 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1569 }
1570
1571 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1572 {
1573 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1574 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1575
1576 if(uFlags & BF_LEFT) InnerRect.left += add;
1577 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1578 if(uFlags & BF_TOP) InnerRect.top += add;
1579 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1580
1581 if((uFlags & BF_MIDDLE) && retval)
1582 {
1583 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1584 theme, part, state);
1585 FillRect(hdc, &InnerRect, br);
1586 DeleteObject (br);
1587 }
1588
1589 if(uFlags & BF_ADJUST)
1590 *contentsRect = InnerRect;
1591 }
1592
1593 /* Cleanup */
1594 SelectObject(hdc, SavePen);
1595 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1596 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1597 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1598 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1599 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1600 return retval;
1601 }
1602
1603
1604 /***********************************************************************
1605 * DrawThemeEdge (UXTHEME.@)
1606 *
1607 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1608 * difference is that it does not rely on the system colors alone, but
1609 * also allows color specification in the theme.
1610 */
1611 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1612 int iStateId, const RECT *pDestRect, UINT uEdge,
1613 UINT uFlags, RECT *pContentRect)
1614 {
1615 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1616 if(!hTheme)
1617 return E_HANDLE;
1618
1619 if(uFlags & BF_DIAGONAL)
1620 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1621 uEdge, uFlags, pContentRect);
1622 else
1623 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1624 uEdge, uFlags, pContentRect);
1625 }
1626
1627
1628 /***********************************************************************
1629 * DrawThemeIcon (UXTHEME.@)
1630 */
1631 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1632 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1633 {
1634 FIXME("%d %d: stub\n", iPartId, iStateId);
1635 if(!hTheme)
1636 return E_HANDLE;
1637 return E_NOTIMPL;
1638 }
1639
1640 typedef int (WINAPI * DRAWSHADOWTEXT)(HDC hdc, LPCWSTR pszText, UINT cch, RECT *prc, DWORD dwFlags,
1641 COLORREF crText, COLORREF crShadow, int ixOffset, int iyOffset);
1642
1643 /***********************************************************************
1644 * DrawThemeText (UXTHEME.@)
1645 */
1646 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1647 LPCWSTR pszText, int iCharCount, DWORD dwTextFlags,
1648 DWORD dwTextFlags2, const RECT *pRect)
1649 {
1650 HRESULT hr;
1651 HFONT hFont = NULL;
1652 HGDIOBJ oldFont = NULL;
1653 LOGFONTW logfont;
1654 COLORREF textColor;
1655 COLORREF oldTextColor;
1656 COLORREF shadowColor;
1657 POINT ptShadowOffset;
1658 int oldBkMode;
1659 RECT rt;
1660 int iShadowType;
1661
1662 TRACE("%d %d: stub\n", iPartId, iStateId);
1663 if(!hTheme)
1664 return E_HANDLE;
1665
1666 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1667 if(SUCCEEDED(hr))
1668 {
1669 hFont = CreateFontIndirectW(&logfont);
1670 if(!hFont)
1671 {
1672 ERR("Failed to create font\n");
1673 }
1674 }
1675
1676 CopyRect(&rt, pRect);
1677 if(hFont)
1678 oldFont = SelectObject(hdc, hFont);
1679
1680 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1681
1682 if(dwTextFlags2 & DTT_GRAYED)
1683 textColor = GetSysColor(COLOR_GRAYTEXT);
1684 else {
1685 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1686 textColor = GetTextColor(hdc);
1687 }
1688
1689 hr = GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_TEXTSHADOWTYPE, &iShadowType);
1690 if (SUCCEEDED(hr))
1691 {
1692 ERR("Got shadow type %d\n", iShadowType);
1693
1694 hr = GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTSHADOWCOLOR, &shadowColor);
1695 if (FAILED(hr))
1696 {
1697 ERR("GetThemeColor failed\n");
1698 }
1699
1700 hr = GetThemePosition(hTheme, iPartId, iStateId, TMT_TEXTSHADOWOFFSET, &ptShadowOffset);
1701 if (FAILED(hr))
1702 {
1703 ERR("GetThemePosition failed\n");
1704 }
1705
1706 if (iShadowType == TST_SINGLE)
1707 {
1708 oldTextColor = SetTextColor(hdc, shadowColor);
1709 OffsetRect(&rt, ptShadowOffset.x, ptShadowOffset.y);
1710 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1711 OffsetRect(&rt, -ptShadowOffset.x, -ptShadowOffset.y);
1712 SetTextColor(hdc, oldTextColor);
1713 }
1714 else if (iShadowType == TST_CONTINUOUS)
1715 {
1716 HANDLE hcomctl32 = GetModuleHandleW(L"comctl32.dll");
1717 DRAWSHADOWTEXT pDrawShadowText;
1718 if (!hcomctl32)
1719 {
1720 hcomctl32 = LoadLibraryW(L"comctl32.dll");
1721 if (!hcomctl32)
1722 ERR("Failed to load comctl32\n");
1723 }
1724
1725 pDrawShadowText = (DRAWSHADOWTEXT)GetProcAddress(hcomctl32, "DrawShadowText");
1726 if (pDrawShadowText)
1727 {
1728 pDrawShadowText(hdc, pszText, iCharCount, &rt, dwTextFlags, textColor, shadowColor, ptShadowOffset.x, ptShadowOffset.y);
1729 goto cleanup;
1730 }
1731 }
1732 }
1733
1734 oldTextColor = SetTextColor(hdc, textColor);
1735 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1736 SetTextColor(hdc, oldTextColor);
1737 cleanup:
1738 SetBkMode(hdc, oldBkMode);
1739
1740 if(hFont) {
1741 SelectObject(hdc, oldFont);
1742 DeleteObject(hFont);
1743 }
1744 return S_OK;
1745 }
1746
1747 /***********************************************************************
1748 * GetThemeBackgroundContentRect (UXTHEME.@)
1749 */
1750 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1751 int iStateId,
1752 const RECT *pBoundingRect,
1753 RECT *pContentRect)
1754 {
1755 MARGINS margin;
1756 HRESULT hr;
1757
1758 TRACE("(%d,%d)\n", iPartId, iStateId);
1759 if(!hTheme)
1760 return E_HANDLE;
1761
1762 /* try content margins property... */
1763 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1764 if(SUCCEEDED(hr)) {
1765 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1766 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1767 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1768 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1769 } else {
1770 /* otherwise, try to determine content rect from the background type and props */
1771 int bgtype = BT_BORDERFILL;
1772 *pContentRect = *pBoundingRect;
1773
1774 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1775 if(bgtype == BT_BORDERFILL) {
1776 int bordersize = 1;
1777
1778 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1779 InflateRect(pContentRect, -bordersize, -bordersize);
1780 } else if ((bgtype == BT_IMAGEFILE)
1781 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1782 TMT_SIZINGMARGINS, NULL, &margin)))) {
1783 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1784 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1785 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1786 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1787 }
1788 /* If nothing was found, leave unchanged */
1789 }
1790
1791 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1792
1793 return S_OK;
1794 }
1795
1796 /***********************************************************************
1797 * GetThemeBackgroundExtent (UXTHEME.@)
1798 */
1799 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1800 int iStateId, const RECT *pContentRect,
1801 RECT *pExtentRect)
1802 {
1803 MARGINS margin;
1804 HRESULT hr;
1805
1806 TRACE("(%d,%d)\n", iPartId, iStateId);
1807 if(!hTheme)
1808 return E_HANDLE;
1809
1810 /* try content margins property... */
1811 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1812 if(SUCCEEDED(hr)) {
1813 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1814 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1815 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1816 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1817 } else {
1818 /* otherwise, try to determine content rect from the background type and props */
1819 int bgtype = BT_BORDERFILL;
1820 *pExtentRect = *pContentRect;
1821
1822 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1823 if(bgtype == BT_BORDERFILL) {
1824 int bordersize = 1;
1825
1826 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1827 InflateRect(pExtentRect, bordersize, bordersize);
1828 } else if ((bgtype == BT_IMAGEFILE)
1829 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1830 TMT_SIZINGMARGINS, NULL, &margin)))) {
1831 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1832 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1833 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1834 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1835 }
1836 /* If nothing was found, leave unchanged */
1837 }
1838
1839 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1840
1841 return S_OK;
1842 }
1843
1844
1845 static HBITMAP UXTHEME_DrawThemePartToDib(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect)
1846 {
1847 HDC hdcMem;
1848 BITMAPINFO bmi;
1849 HBITMAP hbmp, hbmpOld;
1850 HBRUSH hbrBack;
1851
1852 hdcMem = CreateCompatibleDC(0);
1853
1854 memset(&bmi, 0, sizeof(bmi));
1855 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
1856 bmi.bmiHeader.biWidth = pRect->right;
1857 bmi.bmiHeader.biHeight = -pRect->bottom;
1858 bmi.bmiHeader.biPlanes = 1;
1859 bmi.bmiHeader.biBitCount = 32;
1860 hbmp = CreateDIBSection(hdcMem, &bmi, DIB_RGB_COLORS , NULL, 0, 0);
1861
1862 hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp);
1863
1864 /* FIXME: use an internal function that doesn't do transparent blt */
1865 hbrBack = CreateSolidBrush(RGB(255,0,255));
1866
1867 FillRect(hdcMem, pRect, hbrBack);
1868
1869 DrawThemeBackground(hTheme, hdcMem, iPartId, iStateId, pRect, NULL);
1870
1871 DeleteObject(hbrBack);
1872 SelectObject(hdcMem, hbmpOld);
1873 DeleteObject(hdcMem);
1874
1875 return hbmp;
1876 }
1877
1878 #define PT_IN_RECT(lprc,x,y) ( x >= lprc->left && x < lprc->right && \
1879 y >= lprc->top && y < lprc->bottom)
1880
1881 static HRGN UXTHEME_RegionFromDibBits(RGBQUAD* pBuffer, RGBQUAD* pclrTransparent, LPCRECT pRect)
1882 {
1883 int x, y, xstart;
1884 int cMaxRgnRects, cRgnDataSize, cRgnRects;
1885 RECT* prcCurrent;
1886 PRGNDATA prgnData;
1887 ULONG clrTransparent, *pclrCurrent;
1888 HRGN hrgnRet;
1889
1890 pclrCurrent = (PULONG)pBuffer;
1891 clrTransparent = *(PULONG)pclrTransparent;
1892
1893 /* Create a region and pre-allocate memory enough for 3 spaces in one row*/
1894 cRgnRects = 0;
1895 cMaxRgnRects = 4* (pRect->bottom-pRect->top);
1896 cRgnDataSize = sizeof(RGNDATA) + cMaxRgnRects * sizeof(RECT);
1897
1898 /* Allocate the region data */
1899 prgnData = (PRGNDATA)HeapAlloc(GetProcessHeap(), 0, cRgnDataSize);
1900
1901 prcCurrent = (PRECT)prgnData->Buffer;
1902
1903 /* Calculate the region rects */
1904 y=0;
1905 /* Scan each line of the bitmap */
1906 while(y<pRect->bottom)
1907 {
1908 x=0;
1909 /* Scan each pixel */
1910 while (x<pRect->right)
1911 {
1912 /* Check if the pixel is not transparent and it is in the requested rect */
1913 if(*pclrCurrent != clrTransparent && PT_IN_RECT(pRect,x,y))
1914 {
1915 xstart = x;
1916 /* Find the end of the opaque row of pixels */
1917 while (x<pRect->right)
1918 {
1919 if(*pclrCurrent == clrTransparent || !PT_IN_RECT(pRect,x,y))
1920 break;
1921 x++;
1922 pclrCurrent++;
1923 }
1924
1925 /* Add the scaned line to the region */
1926 SetRect(prcCurrent, xstart, y,x,y+1);
1927 prcCurrent++;
1928 cRgnRects++;
1929
1930 /* Increase the size of the buffer if it is full */
1931 if(cRgnRects == cMaxRgnRects)
1932 {
1933 cMaxRgnRects *=2;
1934 cRgnDataSize = sizeof(RGNDATA) + cMaxRgnRects * sizeof(RECT);
1935 prgnData = (PRGNDATA)HeapReAlloc(GetProcessHeap(),
1936 0,
1937 prgnData,
1938 cRgnDataSize);
1939 prcCurrent = (RECT*)prgnData->Buffer + cRgnRects;
1940 }
1941 }
1942 else
1943 {
1944 x++;
1945 pclrCurrent++;
1946 }
1947 }
1948 y++;
1949 }
1950
1951 /* Fill the region data header */
1952 prgnData->rdh.dwSize = sizeof(prgnData->rdh);
1953 prgnData->rdh.iType = RDH_RECTANGLES;
1954 prgnData->rdh.nCount = cRgnRects;
1955 prgnData->rdh.nRgnSize = cRgnDataSize;
1956 prgnData->rdh.rcBound = *pRect;
1957
1958 /* Create the region*/
1959 hrgnRet = ExtCreateRegion (NULL, cRgnDataSize, prgnData);
1960
1961 /* Free the region data*/
1962 HeapFree(GetProcessHeap(),0,prgnData);
1963
1964 /* return the region*/
1965 return hrgnRet;
1966 }
1967
1968 HRESULT UXTHEME_GetImageBackBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect, HRGN *pRegion)
1969 {
1970 HBITMAP hbmp;
1971 DIBSECTION dib;
1972 RGBQUAD clrTransparent = {0xFF,0x0, 0xFF,0x0};
1973
1974 /* Draw the theme part to a dib */
1975 hbmp = UXTHEME_DrawThemePartToDib(hTheme, hdc, iPartId, iStateId, pRect);
1976
1977 /* Retrieve the info of the dib section */
1978 GetObjectW(hbmp, sizeof (DIBSECTION), &dib);
1979
1980 /* Convert the bits of the dib section to a region */
1981 *pRegion = UXTHEME_RegionFromDibBits((RGBQUAD*)dib.dsBm.bmBits, &clrTransparent, pRect);
1982
1983 /* Free the temp bitmap */
1984 DeleteObject(hbmp);
1985
1986 return S_OK;
1987 }
1988
1989 /***********************************************************************
1990 * GetThemeBackgroundRegion (UXTHEME.@)
1991 *
1992 * Calculate the background region, taking into consideration transparent areas
1993 * of the background image.
1994 */
1995 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1996 int iStateId, const RECT *pRect,
1997 HRGN *pRegion)
1998 {
1999 HRESULT hr = S_OK;
2000 int bgtype = BT_BORDERFILL;
2001
2002 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
2003 if(!hTheme)
2004 return E_HANDLE;
2005 if(!pRect || !pRegion)
2006 return E_POINTER;
2007
2008 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2009 if(bgtype == BT_IMAGEFILE) {
2010 hr = UXTHEME_GetImageBackBackgroundRegion(hTheme, hdc, iPartId, iStateId, pRect, pRegion);
2011 }
2012 else if(bgtype == BT_BORDERFILL) {
2013 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
2014 if(!*pRegion)
2015 hr = HRESULT_FROM_WIN32(GetLastError());
2016 }
2017 else {
2018 FIXME("Unknown background type\n");
2019 /* This should never happen, and hence I don't know what to return */
2020 hr = E_FAIL;
2021 }
2022 return hr;
2023 }
2024
2025 /* compute part size for "borderfill" backgrounds */
2026 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
2027 int iStateId, THEMESIZE eSize, POINT* psz)
2028 {
2029 HRESULT hr = S_OK;
2030 int bordersize = 1;
2031
2032 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
2033 &bordersize)))
2034 {
2035 psz->x = psz->y = 2*bordersize;
2036 if (eSize != TS_MIN)
2037 {
2038 psz->x++;
2039 psz->y++;
2040 }
2041 }
2042 return hr;
2043 }
2044
2045 /***********************************************************************
2046 * GetThemePartSize (UXTHEME.@)
2047 */
2048 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
2049 int iStateId, RECT *prc, THEMESIZE eSize,
2050 SIZE *psz)
2051 {
2052 int bgtype = BT_BORDERFILL;
2053 HRESULT hr = S_OK;
2054 POINT size = {1, 1};
2055
2056 if(!hTheme)
2057 return E_HANDLE;
2058
2059 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2060 if (bgtype == BT_NONE)
2061 /* do nothing */;
2062 else if(bgtype == BT_IMAGEFILE)
2063 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
2064 else if(bgtype == BT_BORDERFILL)
2065 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
2066 else {
2067 FIXME("Unknown background type\n");
2068 /* This should never happen, and hence I don't know what to return */
2069 hr = E_FAIL;
2070 }
2071 psz->cx = size.x;
2072 psz->cy = size.y;
2073 return hr;
2074 }
2075
2076
2077 /***********************************************************************
2078 * GetThemeTextExtent (UXTHEME.@)
2079 */
2080 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2081 int iStateId, LPCWSTR pszText, int iCharCount,
2082 DWORD dwTextFlags, const RECT *pBoundingRect,
2083 RECT *pExtentRect)
2084 {
2085 HRESULT hr;
2086 HFONT hFont = NULL;
2087 HGDIOBJ oldFont = NULL;
2088 LOGFONTW logfont;
2089 RECT rt = {0,0,0xFFFF,0xFFFF};
2090
2091 TRACE("%d %d: stub\n", iPartId, iStateId);
2092 if(!hTheme)
2093 return E_HANDLE;
2094
2095 if(pBoundingRect)
2096 rt = *pBoundingRect;
2097
2098 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2099 if(SUCCEEDED(hr)) {
2100 hFont = CreateFontIndirectW(&logfont);
2101 if(!hFont)
2102 TRACE("Failed to create font\n");
2103 }
2104 if(hFont)
2105 oldFont = SelectObject(hdc, hFont);
2106
2107 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2108 *pExtentRect = rt;
2109
2110 if(hFont) {
2111 SelectObject(hdc, oldFont);
2112 DeleteObject(hFont);
2113 }
2114 return S_OK;
2115 }
2116
2117 /***********************************************************************
2118 * GetThemeTextMetrics (UXTHEME.@)
2119 */
2120 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2121 int iStateId, TEXTMETRICW *ptm)
2122 {
2123 HRESULT hr;
2124 HFONT hFont = NULL;
2125 HGDIOBJ oldFont = NULL;
2126 LOGFONTW logfont;
2127
2128 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2129 if(!hTheme)
2130 return E_HANDLE;
2131
2132 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2133 if(SUCCEEDED(hr)) {
2134 hFont = CreateFontIndirectW(&logfont);
2135 if(!hFont)
2136 TRACE("Failed to create font\n");
2137 }
2138 if(hFont)
2139 oldFont = SelectObject(hdc, hFont);
2140
2141 if(!GetTextMetricsW(hdc, ptm))
2142 hr = HRESULT_FROM_WIN32(GetLastError());
2143
2144 if(hFont) {
2145 SelectObject(hdc, oldFont);
2146 DeleteObject(hFont);
2147 }
2148 return hr;
2149 }
2150
2151 /***********************************************************************
2152 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2153 */
2154 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2155 int iStateId)
2156 {
2157 int bgtype = BT_BORDERFILL;
2158 RECT rect = {0, 0, 0, 0};
2159 HBITMAP bmpSrc;
2160 RECT rcSrc;
2161 BOOL hasAlpha;
2162 INT transparent;
2163 COLORREF transparentcolor;
2164
2165 TRACE("(%d,%d)\n", iPartId, iStateId);
2166
2167 if(!hTheme)
2168 return FALSE;
2169
2170 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2171
2172 #ifdef __REACTOS__
2173 if (bgtype == BT_NONE) return TRUE;
2174 #endif
2175 if (bgtype != BT_IMAGEFILE) return FALSE;
2176
2177 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2178 &bmpSrc, &rcSrc, &hasAlpha)))
2179 return FALSE;
2180
2181 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2182 &transparentcolor, FALSE);
2183 return (transparent != ALPHABLEND_NONE);
2184 }