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