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