[COMCTL32][EXPLORER][UXTHEME] Properly draw the taskbar rebar when themes are enabled...
[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 = 0;
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, bmpSrcResized = NULL;
651 HGDIOBJ oldSrc;
652 HDC hdcSrc, hdcOrigSrc = NULL;
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 /* Resize source image if destination smaller than margins */
717 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
718 if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
719 sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
720 sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
721 srcSize.y = dstSize.y;
722 }
723
724 if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
725 sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
726 sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
727 srcSize.x = dstSize.x;
728 }
729
730 hdcOrigSrc = hdcSrc;
731 hdcSrc = CreateCompatibleDC(NULL);
732 bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
733 SelectObject(hdcSrc, bmpSrcResized);
734
735 UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
736 rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
737
738 rcSrc.left = 0;
739 rcSrc.top = 0;
740 rcSrc.right = srcSize.x;
741 rcSrc.bottom = srcSize.y;
742 }
743
744 hdcDst = hdc;
745 OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
746
747 /* Upper left corner */
748 if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
749 hdcSrc, rcSrc.left, rcSrc.top,
750 transparent, transparentcolor)) {
751 hr = HRESULT_FROM_WIN32(GetLastError());
752 goto draw_error;
753 }
754 /* Upper right corner */
755 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
756 sm.cxRightWidth, sm.cyTopHeight,
757 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
758 transparent, transparentcolor)) {
759 hr = HRESULT_FROM_WIN32(GetLastError());
760 goto draw_error;
761 }
762 /* Lower left corner */
763 if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
764 sm.cxLeftWidth, sm.cyBottomHeight,
765 hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
766 transparent, transparentcolor)) {
767 hr = HRESULT_FROM_WIN32(GetLastError());
768 goto draw_error;
769 }
770 /* Lower right corner */
771 if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
772 sm.cxRightWidth, sm.cyBottomHeight,
773 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
774 transparent, transparentcolor)) {
775 hr = HRESULT_FROM_WIN32(GetLastError());
776 goto draw_error;
777 }
778
779 if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
780 int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
781 int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
782 int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
783 int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
784
785 if(destCenterWidth > 0) {
786 /* Center top */
787 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
788 destCenterWidth, sm.cyTopHeight,
789 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
790 srcCenterWidth, sm.cyTopHeight,
791 sizingtype, transparent, transparentcolor)) {
792 hr = HRESULT_FROM_WIN32(GetLastError());
793 goto draw_error;
794 }
795 /* Center bottom */
796 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
797 destCenterWidth, sm.cyBottomHeight,
798 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
799 srcCenterWidth, sm.cyBottomHeight,
800 sizingtype, transparent, transparentcolor)) {
801 hr = HRESULT_FROM_WIN32(GetLastError());
802 goto draw_error;
803 }
804 }
805 if(destCenterHeight > 0) {
806 /* Left center */
807 if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
808 sm.cxLeftWidth, destCenterHeight,
809 hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
810 sm.cxLeftWidth, srcCenterHeight,
811 sizingtype,
812 transparent, transparentcolor)) {
813 hr = HRESULT_FROM_WIN32(GetLastError());
814 goto draw_error;
815 }
816 /* Right center */
817 if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
818 sm.cxRightWidth, destCenterHeight,
819 hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
820 sm.cxRightWidth, srcCenterHeight,
821 sizingtype, transparent, transparentcolor)) {
822 hr = HRESULT_FROM_WIN32(GetLastError());
823 goto draw_error;
824 }
825 }
826 if(destCenterHeight > 0 && destCenterWidth > 0) {
827 BOOL borderonly = FALSE;
828 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
829 if(!borderonly) {
830 /* Center */
831 if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
832 destCenterWidth, destCenterHeight,
833 hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
834 srcCenterWidth, srcCenterHeight,
835 sizingtype, transparent, transparentcolor)) {
836 hr = HRESULT_FROM_WIN32(GetLastError());
837 goto draw_error;
838 }
839 }
840 }
841 }
842
843 draw_error:
844 SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
845 }
846 SelectObject(hdcSrc, oldSrc);
847 DeleteDC(hdcSrc);
848 if (bmpSrcResized) DeleteObject(bmpSrcResized);
849 if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
850 CopyRect(pRect, &rcDst);
851 return hr;
852 }
853
854 /***********************************************************************
855 * UXTHEME_DrawBorderRectangle
856 *
857 * Draw the bounding rectangle for a borderfill background
858 */
859 static HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
860 int iStateId, RECT *pRect,
861 const DTBGOPTS *pOptions)
862 {
863 HRESULT hr = S_OK;
864 HPEN hPen;
865 HGDIOBJ oldPen;
866 COLORREF bordercolor = RGB(0,0,0);
867 int bordersize = 1;
868
869 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
870 if(bordersize > 0) {
871 POINT ptCorners[5];
872 ptCorners[0].x = pRect->left;
873 ptCorners[0].y = pRect->top;
874 ptCorners[1].x = pRect->right-1;
875 ptCorners[1].y = pRect->top;
876 ptCorners[2].x = pRect->right-1;
877 ptCorners[2].y = pRect->bottom-1;
878 ptCorners[3].x = pRect->left;
879 ptCorners[3].y = pRect->bottom-1;
880 ptCorners[4].x = pRect->left;
881 ptCorners[4].y = pRect->top;
882
883 InflateRect(pRect, -bordersize, -bordersize);
884 if(pOptions->dwFlags & DTBG_OMITBORDER)
885 return S_OK;
886 GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
887 hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
888 if(!hPen)
889 return HRESULT_FROM_WIN32(GetLastError());
890 oldPen = SelectObject(hdc, hPen);
891
892 if(!Polyline(hdc, ptCorners, 5))
893 hr = HRESULT_FROM_WIN32(GetLastError());
894
895 SelectObject(hdc, oldPen);
896 DeleteObject(hPen);
897 }
898 return hr;
899 }
900
901 /***********************************************************************
902 * UXTHEME_DrawBackgroundFill
903 *
904 * Fill a borderfill background rectangle
905 */
906 static HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
907 int iStateId, RECT *pRect,
908 const DTBGOPTS *pOptions)
909 {
910 HRESULT hr = S_OK;
911 int filltype = FT_SOLID;
912
913 TRACE("(%d,%d,%d)\n", iPartId, iStateId, pOptions->dwFlags);
914
915 if(pOptions->dwFlags & DTBG_OMITCONTENT)
916 return S_OK;
917
918 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
919
920 if(filltype == FT_SOLID) {
921 HBRUSH hBrush;
922 COLORREF fillcolor = RGB(255,255,255);
923
924 GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
925 hBrush = CreateSolidBrush(fillcolor);
926 if(!FillRect(hdc, pRect, hBrush))
927 hr = HRESULT_FROM_WIN32(GetLastError());
928 DeleteObject(hBrush);
929 }
930 else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
931 /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
932 the gradient ratios (no idea how those work)
933 Few themes use this, and the ones I've seen only use 2 colors with
934 a gradient ratio of 0 and 255 respectively
935 */
936
937 COLORREF gradient1 = RGB(0,0,0);
938 COLORREF gradient2 = RGB(255,255,255);
939 TRIVERTEX vert[2];
940 GRADIENT_RECT gRect;
941
942 FIXME("Gradient implementation not complete\n");
943
944 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
945 GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
946
947 vert[0].x = pRect->left;
948 vert[0].y = pRect->top;
949 vert[0].Red = GetRValue(gradient1) << 8;
950 vert[0].Green = GetGValue(gradient1) << 8;
951 vert[0].Blue = GetBValue(gradient1) << 8;
952 vert[0].Alpha = 0xff00;
953
954 vert[1].x = pRect->right;
955 vert[1].y = pRect->bottom;
956 vert[1].Red = GetRValue(gradient2) << 8;
957 vert[1].Green = GetGValue(gradient2) << 8;
958 vert[1].Blue = GetBValue(gradient2) << 8;
959 vert[1].Alpha = 0xff00;
960
961 gRect.UpperLeft = 0;
962 gRect.LowerRight = 1;
963 GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
964 }
965 else if(filltype == FT_RADIALGRADIENT) {
966 /* I've never seen this used in a theme */
967 FIXME("Radial gradient\n");
968 }
969 else if(filltype == FT_TILEIMAGE) {
970 /* I've never seen this used in a theme */
971 FIXME("Tile image\n");
972 }
973 return hr;
974 }
975
976 /***********************************************************************
977 * UXTHEME_DrawBorderBackground
978 *
979 * Draw an imagefile background
980 */
981 static HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
982 int iStateId, const RECT *pRect,
983 const DTBGOPTS *pOptions)
984 {
985 HRESULT hr;
986 RECT rt;
987
988 CopyRect(&rt, pRect);
989
990 hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
991 if(FAILED(hr))
992 return hr;
993 return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
994 }
995
996 /***********************************************************************
997 * DrawThemeBackgroundEx (UXTHEME.@)
998 */
999 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
1000 int iStateId, const RECT *pRect,
1001 const DTBGOPTS *pOptions)
1002 {
1003 HRESULT hr;
1004 const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
1005 const DTBGOPTS *opts;
1006 HRGN clip = NULL;
1007 int hasClip = -1;
1008 int bgtype = BT_BORDERFILL;
1009 RECT rt;
1010
1011 TRACE("(%p,%p,%d,%d,%d,%d)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
1012 if(!hTheme)
1013 return E_HANDLE;
1014
1015 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1016 if (bgtype == BT_NONE) return S_OK;
1017
1018 /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
1019 opts = pOptions;
1020 if(!opts) opts = &defaultOpts;
1021
1022 if(opts->dwFlags & DTBG_CLIPRECT) {
1023 clip = CreateRectRgn(0,0,1,1);
1024 hasClip = GetClipRgn(hdc, clip);
1025 if(hasClip == -1)
1026 TRACE("Failed to get original clipping region\n");
1027 else
1028 IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
1029 }
1030 CopyRect(&rt, pRect);
1031
1032 if(bgtype == BT_IMAGEFILE)
1033 hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
1034 else if(bgtype == BT_BORDERFILL)
1035 hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
1036 else {
1037 FIXME("Unknown background type\n");
1038 /* This should never happen, and hence I don't know what to return */
1039 hr = E_FAIL;
1040 }
1041 if(SUCCEEDED(hr))
1042 hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
1043 if(opts->dwFlags & DTBG_CLIPRECT) {
1044 if(hasClip == 0)
1045 SelectClipRgn(hdc, NULL);
1046 else if(hasClip == 1)
1047 SelectClipRgn(hdc, clip);
1048 DeleteObject(clip);
1049 }
1050 return hr;
1051 }
1052
1053 /*
1054 * DrawThemeEdge() implementation
1055 *
1056 * Since it basically is DrawEdge() with different colors, I copied its code
1057 * from user32's uitools.c.
1058 */
1059
1060 enum
1061 {
1062 EDGE_LIGHT,
1063 EDGE_HIGHLIGHT,
1064 EDGE_SHADOW,
1065 EDGE_DARKSHADOW,
1066 EDGE_FILL,
1067
1068 EDGE_WINDOW,
1069 EDGE_WINDOWFRAME,
1070
1071 EDGE_NUMCOLORS
1072 };
1073
1074 static const struct
1075 {
1076 int themeProp;
1077 int sysColor;
1078 } EdgeColorMap[EDGE_NUMCOLORS] = {
1079 {TMT_EDGELIGHTCOLOR, COLOR_3DLIGHT},
1080 {TMT_EDGEHIGHLIGHTCOLOR, COLOR_BTNHIGHLIGHT},
1081 {TMT_EDGESHADOWCOLOR, COLOR_BTNSHADOW},
1082 {TMT_EDGEDKSHADOWCOLOR, COLOR_3DDKSHADOW},
1083 {TMT_EDGEFILLCOLOR, COLOR_BTNFACE},
1084 {-1, COLOR_WINDOW},
1085 {-1, COLOR_WINDOWFRAME}
1086 };
1087
1088 static const signed char LTInnerNormal[] = {
1089 -1, -1, -1, -1,
1090 -1, EDGE_HIGHLIGHT, EDGE_HIGHLIGHT, -1,
1091 -1, EDGE_DARKSHADOW, EDGE_DARKSHADOW, -1,
1092 -1, -1, -1, -1
1093 };
1094
1095 static const signed char LTOuterNormal[] = {
1096 -1, EDGE_LIGHT, EDGE_SHADOW, -1,
1097 EDGE_HIGHLIGHT, EDGE_LIGHT, EDGE_SHADOW, -1,
1098 EDGE_DARKSHADOW, EDGE_LIGHT, EDGE_SHADOW, -1,
1099 -1, EDGE_LIGHT, EDGE_SHADOW, -1
1100 };
1101
1102 static const signed char RBInnerNormal[] = {
1103 -1, -1, -1, -1,
1104 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1105 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1106 -1, -1, -1, -1
1107 };
1108
1109 static const signed char RBOuterNormal[] = {
1110 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1111 EDGE_SHADOW, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1112 EDGE_LIGHT, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1,
1113 -1, EDGE_DARKSHADOW, EDGE_HIGHLIGHT, -1
1114 };
1115
1116 static const signed char LTInnerSoft[] = {
1117 -1, -1, -1, -1,
1118 -1, EDGE_LIGHT, EDGE_LIGHT, -1,
1119 -1, EDGE_SHADOW, EDGE_SHADOW, -1,
1120 -1, -1, -1, -1
1121 };
1122
1123 static const signed char LTOuterSoft[] = {
1124 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1125 EDGE_LIGHT, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1126 EDGE_SHADOW, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1,
1127 -1, EDGE_HIGHLIGHT, EDGE_DARKSHADOW, -1
1128 };
1129
1130 #define RBInnerSoft RBInnerNormal /* These are the same */
1131 #define RBOuterSoft RBOuterNormal
1132
1133 static const signed char LTRBOuterMono[] = {
1134 -1, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1135 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1136 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1137 EDGE_WINDOW, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME, EDGE_WINDOWFRAME,
1138 };
1139
1140 static const signed char LTRBInnerMono[] = {
1141 -1, -1, -1, -1,
1142 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1143 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1144 -1, EDGE_WINDOW, EDGE_WINDOW, EDGE_WINDOW,
1145 };
1146
1147 static const signed char LTRBOuterFlat[] = {
1148 -1, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1149 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1150 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1151 EDGE_FILL, EDGE_SHADOW, EDGE_SHADOW, EDGE_SHADOW,
1152 };
1153
1154 static const signed char LTRBInnerFlat[] = {
1155 -1, -1, -1, -1,
1156 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1157 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1158 -1, EDGE_FILL, EDGE_FILL, EDGE_FILL,
1159 };
1160
1161 static COLORREF get_edge_color (int edgeType, HTHEME theme, int part, int state)
1162 {
1163 COLORREF col;
1164 if ((EdgeColorMap[edgeType].themeProp == -1)
1165 || FAILED (GetThemeColor (theme, part, state,
1166 EdgeColorMap[edgeType].themeProp, &col)))
1167 col = GetSysColor (EdgeColorMap[edgeType].sysColor);
1168 return col;
1169 }
1170
1171 static inline HPEN get_edge_pen (int edgeType, HTHEME theme, int part, int state)
1172 {
1173 return CreatePen (PS_SOLID, 1, get_edge_color (edgeType, theme, part, state));
1174 }
1175
1176 static inline HBRUSH get_edge_brush (int edgeType, HTHEME theme, int part, int state)
1177 {
1178 return CreateSolidBrush (get_edge_color (edgeType, theme, part, state));
1179 }
1180
1181 /***********************************************************************
1182 * draw_diag_edge
1183 *
1184 * Same as DrawEdge invoked with BF_DIAGONAL
1185 */
1186 static HRESULT draw_diag_edge (HDC hdc, HTHEME theme, int part, int state,
1187 const RECT* rc, UINT uType,
1188 UINT uFlags, LPRECT contentsRect)
1189 {
1190 POINT Points[4];
1191 signed char InnerI, OuterI;
1192 HPEN InnerPen, OuterPen;
1193 POINT SavePoint;
1194 HPEN SavePen;
1195 int spx, spy;
1196 int epx, epy;
1197 int Width = rc->right - rc->left;
1198 int Height= rc->bottom - rc->top;
1199 int SmallDiam = Width > Height ? Height : Width;
1200 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1201 || (uType & BDR_OUTER) == BDR_OUTER)
1202 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1203 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1204 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1205
1206 /* Init some vars */
1207 OuterPen = InnerPen = GetStockObject(NULL_PEN);
1208 SavePen = SelectObject(hdc, InnerPen);
1209 spx = spy = epx = epy = 0; /* Satisfy the compiler... */
1210
1211 /* Determine the colors of the edges */
1212 if(uFlags & BF_MONO)
1213 {
1214 InnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1215 OuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1216 }
1217 else if(uFlags & BF_FLAT)
1218 {
1219 InnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1220 OuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1221 }
1222 else if(uFlags & BF_SOFT)
1223 {
1224 if(uFlags & BF_BOTTOM)
1225 {
1226 InnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1227 OuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1228 }
1229 else
1230 {
1231 InnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1232 OuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1233 }
1234 }
1235 else
1236 {
1237 if(uFlags & BF_BOTTOM)
1238 {
1239 InnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1240 OuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1241 }
1242 else
1243 {
1244 InnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1245 OuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1246 }
1247 }
1248
1249 if(InnerI != -1) InnerPen = get_edge_pen (InnerI, theme, part, state);
1250 if(OuterI != -1) OuterPen = get_edge_pen (OuterI, theme, part, state);
1251
1252 MoveToEx(hdc, 0, 0, &SavePoint);
1253
1254 /* Don't ask me why, but this is what is visible... */
1255 /* This must be possible to do much simpler, but I fail to */
1256 /* see the logic in the MS implementation (sigh...). */
1257 /* So, this might look a bit brute force here (and it is), but */
1258 /* it gets the job done;) */
1259
1260 switch(uFlags & BF_RECT)
1261 {
1262 case 0:
1263 case BF_LEFT:
1264 case BF_BOTTOM:
1265 case BF_BOTTOMLEFT:
1266 /* Left bottom endpoint */
1267 epx = rc->left-1;
1268 spx = epx + SmallDiam;
1269 epy = rc->bottom;
1270 spy = epy - SmallDiam;
1271 break;
1272
1273 case BF_TOPLEFT:
1274 case BF_BOTTOMRIGHT:
1275 /* Left top endpoint */
1276 epx = rc->left-1;
1277 spx = epx + SmallDiam;
1278 epy = rc->top-1;
1279 spy = epy + SmallDiam;
1280 break;
1281
1282 case BF_TOP:
1283 case BF_RIGHT:
1284 case BF_TOPRIGHT:
1285 case BF_RIGHT|BF_LEFT:
1286 case BF_RIGHT|BF_LEFT|BF_TOP:
1287 case BF_BOTTOM|BF_TOP:
1288 case BF_BOTTOM|BF_TOP|BF_LEFT:
1289 case BF_BOTTOMRIGHT|BF_LEFT:
1290 case BF_BOTTOMRIGHT|BF_TOP:
1291 case BF_RECT:
1292 /* Right top endpoint */
1293 spx = rc->left;
1294 epx = spx + SmallDiam;
1295 spy = rc->bottom-1;
1296 epy = spy - SmallDiam;
1297 break;
1298 }
1299
1300 MoveToEx(hdc, spx, spy, NULL);
1301 SelectObject(hdc, OuterPen);
1302 LineTo(hdc, epx, epy);
1303
1304 SelectObject(hdc, InnerPen);
1305
1306 switch(uFlags & (BF_RECT|BF_DIAGONAL))
1307 {
1308 case BF_DIAGONAL_ENDBOTTOMLEFT:
1309 case (BF_DIAGONAL|BF_BOTTOM):
1310 case BF_DIAGONAL:
1311 case (BF_DIAGONAL|BF_LEFT):
1312 MoveToEx(hdc, spx-1, spy, NULL);
1313 LineTo(hdc, epx, epy-1);
1314 Points[0].x = spx-add;
1315 Points[0].y = spy;
1316 Points[1].x = rc->left;
1317 Points[1].y = rc->top;
1318 Points[2].x = epx+1;
1319 Points[2].y = epy-1-add;
1320 Points[3] = Points[2];
1321 break;
1322
1323 case BF_DIAGONAL_ENDBOTTOMRIGHT:
1324 MoveToEx(hdc, spx-1, spy, NULL);
1325 LineTo(hdc, epx, epy+1);
1326 Points[0].x = spx-add;
1327 Points[0].y = spy;
1328 Points[1].x = rc->left;
1329 Points[1].y = rc->bottom-1;
1330 Points[2].x = epx+1;
1331 Points[2].y = epy+1+add;
1332 Points[3] = Points[2];
1333 break;
1334
1335 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP):
1336 case (BF_DIAGONAL|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT):
1337 case BF_DIAGONAL_ENDTOPRIGHT:
1338 case (BF_DIAGONAL|BF_RIGHT|BF_TOP|BF_LEFT):
1339 MoveToEx(hdc, spx+1, spy, NULL);
1340 LineTo(hdc, epx, epy+1);
1341 Points[0].x = epx-1;
1342 Points[0].y = epy+1+add;
1343 Points[1].x = rc->right-1;
1344 Points[1].y = rc->top+add;
1345 Points[2].x = rc->right-1;
1346 Points[2].y = rc->bottom-1;
1347 Points[3].x = spx+add;
1348 Points[3].y = spy;
1349 break;
1350
1351 case BF_DIAGONAL_ENDTOPLEFT:
1352 MoveToEx(hdc, spx, spy-1, NULL);
1353 LineTo(hdc, epx+1, epy);
1354 Points[0].x = epx+1+add;
1355 Points[0].y = epy+1;
1356 Points[1].x = rc->right-1;
1357 Points[1].y = rc->top;
1358 Points[2].x = rc->right-1;
1359 Points[2].y = rc->bottom-1-add;
1360 Points[3].x = spx;
1361 Points[3].y = spy-add;
1362 break;
1363
1364 case (BF_DIAGONAL|BF_TOP):
1365 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP):
1366 case (BF_DIAGONAL|BF_BOTTOM|BF_TOP|BF_LEFT):
1367 MoveToEx(hdc, spx+1, spy-1, NULL);
1368 LineTo(hdc, epx, epy);
1369 Points[0].x = epx-1;
1370 Points[0].y = epy+1;
1371 Points[1].x = rc->right-1;
1372 Points[1].y = rc->top;
1373 Points[2].x = rc->right-1;
1374 Points[2].y = rc->bottom-1-add;
1375 Points[3].x = spx+add;
1376 Points[3].y = spy-add;
1377 break;
1378
1379 case (BF_DIAGONAL|BF_RIGHT):
1380 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT):
1381 case (BF_DIAGONAL|BF_RIGHT|BF_LEFT|BF_BOTTOM):
1382 MoveToEx(hdc, spx, spy, NULL);
1383 LineTo(hdc, epx-1, epy+1);
1384 Points[0].x = spx;
1385 Points[0].y = spy;
1386 Points[1].x = rc->left;
1387 Points[1].y = rc->top+add;
1388 Points[2].x = epx-1-add;
1389 Points[2].y = epy+1+add;
1390 Points[3] = Points[2];
1391 break;
1392 }
1393
1394 /* Fill the interior if asked */
1395 if((uFlags & BF_MIDDLE) && retval)
1396 {
1397 HBRUSH hbsave;
1398 HBRUSH hb = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1399 theme, part, state);
1400 HPEN hpsave;
1401 HPEN hp = get_edge_pen ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1402 theme, part, state);
1403 hbsave = SelectObject(hdc, hb);
1404 hpsave = SelectObject(hdc, hp);
1405 Polygon(hdc, Points, 4);
1406 SelectObject(hdc, hbsave);
1407 SelectObject(hdc, hpsave);
1408 DeleteObject (hp);
1409 DeleteObject (hb);
1410 }
1411
1412 /* Adjust rectangle if asked */
1413 if(uFlags & BF_ADJUST)
1414 {
1415 *contentsRect = *rc;
1416 if(uFlags & BF_LEFT) contentsRect->left += add;
1417 if(uFlags & BF_RIGHT) contentsRect->right -= add;
1418 if(uFlags & BF_TOP) contentsRect->top += add;
1419 if(uFlags & BF_BOTTOM) contentsRect->bottom -= add;
1420 }
1421
1422 /* Cleanup */
1423 SelectObject(hdc, SavePen);
1424 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1425 if(InnerI != -1) DeleteObject (InnerPen);
1426 if(OuterI != -1) DeleteObject (OuterPen);
1427
1428 return retval;
1429 }
1430
1431 /***********************************************************************
1432 * draw_rect_edge
1433 *
1434 * Same as DrawEdge invoked without BF_DIAGONAL
1435 */
1436 static HRESULT draw_rect_edge (HDC hdc, HTHEME theme, int part, int state,
1437 const RECT* rc, UINT uType,
1438 UINT uFlags, LPRECT contentsRect)
1439 {
1440 signed char LTInnerI, LTOuterI;
1441 signed char RBInnerI, RBOuterI;
1442 HPEN LTInnerPen, LTOuterPen;
1443 HPEN RBInnerPen, RBOuterPen;
1444 RECT InnerRect = *rc;
1445 POINT SavePoint;
1446 HPEN SavePen;
1447 int LBpenplus = 0;
1448 int LTpenplus = 0;
1449 int RTpenplus = 0;
1450 int RBpenplus = 0;
1451 HRESULT retval = (((uType & BDR_INNER) == BDR_INNER
1452 || (uType & BDR_OUTER) == BDR_OUTER)
1453 && !(uFlags & (BF_FLAT|BF_MONO)) ) ? E_FAIL : S_OK;
1454
1455 /* Init some vars */
1456 LTInnerPen = LTOuterPen = RBInnerPen = RBOuterPen = GetStockObject(NULL_PEN);
1457 SavePen = SelectObject(hdc, LTInnerPen);
1458
1459 /* Determine the colors of the edges */
1460 if(uFlags & BF_MONO)
1461 {
1462 LTInnerI = RBInnerI = LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)];
1463 LTOuterI = RBOuterI = LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)];
1464 }
1465 else if(uFlags & BF_FLAT)
1466 {
1467 LTInnerI = RBInnerI = LTRBInnerFlat[uType & (BDR_INNER|BDR_OUTER)];
1468 LTOuterI = RBOuterI = LTRBOuterFlat[uType & (BDR_INNER|BDR_OUTER)];
1469
1470 if( LTInnerI != -1 ) LTInnerI = RBInnerI = EDGE_FILL;
1471 }
1472 else if(uFlags & BF_SOFT)
1473 {
1474 LTInnerI = LTInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1475 LTOuterI = LTOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1476 RBInnerI = RBInnerSoft[uType & (BDR_INNER|BDR_OUTER)];
1477 RBOuterI = RBOuterSoft[uType & (BDR_INNER|BDR_OUTER)];
1478 }
1479 else
1480 {
1481 LTInnerI = LTInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1482 LTOuterI = LTOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1483 RBInnerI = RBInnerNormal[uType & (BDR_INNER|BDR_OUTER)];
1484 RBOuterI = RBOuterNormal[uType & (BDR_INNER|BDR_OUTER)];
1485 }
1486
1487 if((uFlags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) LBpenplus = 1;
1488 if((uFlags & BF_TOPRIGHT) == BF_TOPRIGHT) RTpenplus = 1;
1489 if((uFlags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) RBpenplus = 1;
1490 if((uFlags & BF_TOPLEFT) == BF_TOPLEFT) LTpenplus = 1;
1491
1492 if(LTInnerI != -1) LTInnerPen = get_edge_pen (LTInnerI, theme, part, state);
1493 if(LTOuterI != -1) LTOuterPen = get_edge_pen (LTOuterI, theme, part, state);
1494 if(RBInnerI != -1) RBInnerPen = get_edge_pen (RBInnerI, theme, part, state);
1495 if(RBOuterI != -1) RBOuterPen = get_edge_pen (RBOuterI, theme, part, state);
1496
1497 MoveToEx(hdc, 0, 0, &SavePoint);
1498
1499 /* Draw the outer edge */
1500 SelectObject(hdc, LTOuterPen);
1501 if(uFlags & BF_TOP)
1502 {
1503 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1504 LineTo(hdc, InnerRect.right, InnerRect.top);
1505 }
1506 if(uFlags & BF_LEFT)
1507 {
1508 MoveToEx(hdc, InnerRect.left, InnerRect.top, NULL);
1509 LineTo(hdc, InnerRect.left, InnerRect.bottom);
1510 }
1511 SelectObject(hdc, RBOuterPen);
1512 if(uFlags & BF_BOTTOM)
1513 {
1514 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1515 LineTo(hdc, InnerRect.left-1, InnerRect.bottom-1);
1516 }
1517 if(uFlags & BF_RIGHT)
1518 {
1519 MoveToEx(hdc, InnerRect.right-1, InnerRect.bottom-1, NULL);
1520 LineTo(hdc, InnerRect.right-1, InnerRect.top-1);
1521 }
1522
1523 /* Draw the inner edge */
1524 SelectObject(hdc, LTInnerPen);
1525 if(uFlags & BF_TOP)
1526 {
1527 MoveToEx(hdc, InnerRect.left+LTpenplus, InnerRect.top+1, NULL);
1528 LineTo(hdc, InnerRect.right-RTpenplus, InnerRect.top+1);
1529 }
1530 if(uFlags & BF_LEFT)
1531 {
1532 MoveToEx(hdc, InnerRect.left+1, InnerRect.top+LTpenplus, NULL);
1533 LineTo(hdc, InnerRect.left+1, InnerRect.bottom-LBpenplus);
1534 }
1535 SelectObject(hdc, RBInnerPen);
1536 if(uFlags & BF_BOTTOM)
1537 {
1538 MoveToEx(hdc, InnerRect.right-1-RBpenplus, InnerRect.bottom-2, NULL);
1539 LineTo(hdc, InnerRect.left-1+LBpenplus, InnerRect.bottom-2);
1540 }
1541 if(uFlags & BF_RIGHT)
1542 {
1543 MoveToEx(hdc, InnerRect.right-2, InnerRect.bottom-1-RBpenplus, NULL);
1544 LineTo(hdc, InnerRect.right-2, InnerRect.top-1+RTpenplus);
1545 }
1546
1547 if( ((uFlags & BF_MIDDLE) && retval) || (uFlags & BF_ADJUST) )
1548 {
1549 int add = (LTRBInnerMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0)
1550 + (LTRBOuterMono[uType & (BDR_INNER|BDR_OUTER)] != -1 ? 1 : 0);
1551
1552 if(uFlags & BF_LEFT) InnerRect.left += add;
1553 if(uFlags & BF_RIGHT) InnerRect.right -= add;
1554 if(uFlags & BF_TOP) InnerRect.top += add;
1555 if(uFlags & BF_BOTTOM) InnerRect.bottom -= add;
1556
1557 if((uFlags & BF_MIDDLE) && retval)
1558 {
1559 HBRUSH br = get_edge_brush ((uFlags & BF_MONO) ? EDGE_WINDOW : EDGE_FILL,
1560 theme, part, state);
1561 FillRect(hdc, &InnerRect, br);
1562 DeleteObject (br);
1563 }
1564
1565 if(uFlags & BF_ADJUST)
1566 *contentsRect = InnerRect;
1567 }
1568
1569 /* Cleanup */
1570 SelectObject(hdc, SavePen);
1571 MoveToEx(hdc, SavePoint.x, SavePoint.y, NULL);
1572 if(LTInnerI != -1) DeleteObject (LTInnerPen);
1573 if(LTOuterI != -1) DeleteObject (LTOuterPen);
1574 if(RBInnerI != -1) DeleteObject (RBInnerPen);
1575 if(RBOuterI != -1) DeleteObject (RBOuterPen);
1576 return retval;
1577 }
1578
1579
1580 /***********************************************************************
1581 * DrawThemeEdge (UXTHEME.@)
1582 *
1583 * DrawThemeEdge() is pretty similar to the vanilla DrawEdge() - the
1584 * difference is that it does not rely on the system colors alone, but
1585 * also allows color specification in the theme.
1586 */
1587 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
1588 int iStateId, const RECT *pDestRect, UINT uEdge,
1589 UINT uFlags, RECT *pContentRect)
1590 {
1591 TRACE("%d %d 0x%08x 0x%08x\n", iPartId, iStateId, uEdge, uFlags);
1592 if(!hTheme)
1593 return E_HANDLE;
1594
1595 if(uFlags & BF_DIAGONAL)
1596 return draw_diag_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1597 uEdge, uFlags, pContentRect);
1598 else
1599 return draw_rect_edge (hdc, hTheme, iPartId, iStateId, pDestRect,
1600 uEdge, uFlags, pContentRect);
1601 }
1602
1603
1604 /***********************************************************************
1605 * DrawThemeIcon (UXTHEME.@)
1606 */
1607 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1608 const RECT *pRect, HIMAGELIST himl, int iImageIndex)
1609 {
1610 FIXME("%d %d: stub\n", iPartId, iStateId);
1611 if(!hTheme)
1612 return E_HANDLE;
1613 return ERROR_CALL_NOT_IMPLEMENTED;
1614 }
1615
1616 /***********************************************************************
1617 * DrawThemeText (UXTHEME.@)
1618 */
1619 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1620 LPCWSTR pszText, int iCharCount, DWORD dwTextFlags,
1621 DWORD dwTextFlags2, const RECT *pRect)
1622 {
1623 HRESULT hr;
1624 HFONT hFont = NULL;
1625 HGDIOBJ oldFont = NULL;
1626 LOGFONTW logfont;
1627 COLORREF textColor;
1628 COLORREF oldTextColor;
1629 int oldBkMode;
1630 RECT rt;
1631
1632 TRACE("%d %d: stub\n", iPartId, iStateId);
1633 if(!hTheme)
1634 return E_HANDLE;
1635
1636 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1637 if(SUCCEEDED(hr)) {
1638 hFont = CreateFontIndirectW(&logfont);
1639 if(!hFont)
1640 TRACE("Failed to create font\n");
1641 }
1642 CopyRect(&rt, pRect);
1643 if(hFont)
1644 oldFont = SelectObject(hdc, hFont);
1645
1646 if(dwTextFlags2 & DTT_GRAYED)
1647 textColor = GetSysColor(COLOR_GRAYTEXT);
1648 else {
1649 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1650 textColor = GetTextColor(hdc);
1651 }
1652 oldTextColor = SetTextColor(hdc, textColor);
1653 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1654 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1655 SetBkMode(hdc, oldBkMode);
1656 SetTextColor(hdc, oldTextColor);
1657
1658 if(hFont) {
1659 SelectObject(hdc, oldFont);
1660 DeleteObject(hFont);
1661 }
1662 return S_OK;
1663 }
1664
1665 /***********************************************************************
1666 * GetThemeBackgroundContentRect (UXTHEME.@)
1667 */
1668 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1669 int iStateId,
1670 const RECT *pBoundingRect,
1671 RECT *pContentRect)
1672 {
1673 MARGINS margin;
1674 HRESULT hr;
1675
1676 TRACE("(%d,%d)\n", iPartId, iStateId);
1677 if(!hTheme)
1678 return E_HANDLE;
1679
1680 /* try content margins property... */
1681 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1682 if(SUCCEEDED(hr)) {
1683 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1684 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1685 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1686 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1687 } else {
1688 /* otherwise, try to determine content rect from the background type and props */
1689 int bgtype = BT_BORDERFILL;
1690 *pContentRect = *pBoundingRect;
1691
1692 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1693 if(bgtype == BT_BORDERFILL) {
1694 int bordersize = 1;
1695
1696 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1697 InflateRect(pContentRect, -bordersize, -bordersize);
1698 } else if ((bgtype == BT_IMAGEFILE)
1699 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1700 TMT_SIZINGMARGINS, NULL, &margin)))) {
1701 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1702 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1703 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1704 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1705 }
1706 /* If nothing was found, leave unchanged */
1707 }
1708
1709 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pContentRect->left, pContentRect->top, pContentRect->right, pContentRect->bottom);
1710
1711 return S_OK;
1712 }
1713
1714 /***********************************************************************
1715 * GetThemeBackgroundExtent (UXTHEME.@)
1716 */
1717 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1718 int iStateId, const RECT *pContentRect,
1719 RECT *pExtentRect)
1720 {
1721 MARGINS margin;
1722 HRESULT hr;
1723
1724 TRACE("(%d,%d)\n", iPartId, iStateId);
1725 if(!hTheme)
1726 return E_HANDLE;
1727
1728 /* try content margins property... */
1729 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1730 if(SUCCEEDED(hr)) {
1731 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1732 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1733 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1734 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1735 } else {
1736 /* otherwise, try to determine content rect from the background type and props */
1737 int bgtype = BT_BORDERFILL;
1738 *pExtentRect = *pContentRect;
1739
1740 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1741 if(bgtype == BT_BORDERFILL) {
1742 int bordersize = 1;
1743
1744 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1745 InflateRect(pExtentRect, bordersize, bordersize);
1746 } else if ((bgtype == BT_IMAGEFILE)
1747 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1748 TMT_SIZINGMARGINS, NULL, &margin)))) {
1749 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1750 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1751 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1752 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1753 }
1754 /* If nothing was found, leave unchanged */
1755 }
1756
1757 TRACE("left:%d,top:%d,right:%d,bottom:%d\n", pExtentRect->left, pExtentRect->top, pExtentRect->right, pExtentRect->bottom);
1758
1759 return S_OK;
1760 }
1761
1762
1763 static HBITMAP UXTHEME_DrawThemePartToDib(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect)
1764 {
1765 HDC hdcMem;
1766 BITMAPINFO bmi;
1767 HBITMAP hbmp, hbmpOld;
1768 HBRUSH hbrBack;
1769
1770 hdcMem = CreateCompatibleDC(0);
1771
1772 memset(&bmi, 0, sizeof(bmi));
1773 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
1774 bmi.bmiHeader.biWidth = pRect->right;
1775 bmi.bmiHeader.biHeight = -pRect->bottom;
1776 bmi.bmiHeader.biPlanes = 1;
1777 bmi.bmiHeader.biBitCount = 32;
1778 hbmp = CreateDIBSection(hdcMem, &bmi, DIB_RGB_COLORS , NULL, 0, 0);
1779
1780 hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp);
1781
1782 /* FIXME: use an internal function that doesn't do transparent blt */
1783 hbrBack = CreateSolidBrush(RGB(255,0,255));
1784
1785 FillRect(hdcMem, pRect, hbrBack);
1786
1787 DrawThemeBackground(hTheme, hdcMem, iPartId, iStateId, pRect, NULL);
1788
1789 DeleteObject(hbrBack);
1790 SelectObject(hdcMem, hbmpOld);
1791 DeleteObject(hdcMem);
1792
1793 return hbmp;
1794 }
1795
1796 #define PT_IN_RECT(lprc,x,y) ( x >= lprc->left && x < lprc->right && \
1797 y >= lprc->top && y < lprc->bottom)
1798
1799 static HRGN UXTHEME_RegionFromDibBits(RGBQUAD* pBuffer, RGBQUAD* pclrTransparent, LPCRECT pRect)
1800 {
1801 int x, y, xstart;
1802 int cMaxRgnRects, cRgnDataSize, cRgnRects;
1803 RECT* prcCurrent;
1804 PRGNDATA prgnData;
1805 ULONG clrTransparent, *pclrCurrent;
1806 HRGN hrgnRet;
1807
1808 pclrCurrent = (PULONG)pBuffer;
1809 clrTransparent = *(PULONG)pclrTransparent;
1810
1811 /* Create a region and pre-allocate memory enough for 3 spaces in one row*/
1812 cRgnRects = 0;
1813 cMaxRgnRects = 4* (pRect->bottom-pRect->top);
1814 cRgnDataSize = sizeof(RGNDATA) + cMaxRgnRects * sizeof(RECT);
1815
1816 /* Allocate the region data */
1817 prgnData = (PRGNDATA)HeapAlloc(GetProcessHeap(), 0, cRgnDataSize);
1818
1819 prcCurrent = (PRECT)prgnData->Buffer;
1820
1821 /* Calculate the region rects */
1822 y=0;
1823 /* Scan each line of the bitmap */
1824 while(y<pRect->bottom)
1825 {
1826 x=0;
1827 /* Scan each pixel */
1828 while (x<pRect->right)
1829 {
1830 /* Check if the pixel is not transparent and it is in the requested rect */
1831 if(*pclrCurrent != clrTransparent && PT_IN_RECT(pRect,x,y))
1832 {
1833 xstart = x;
1834 /* Find the end of the opaque row of pixels */
1835 while (x<pRect->right)
1836 {
1837 if(*pclrCurrent == clrTransparent || !PT_IN_RECT(pRect,x,y))
1838 break;
1839 x++;
1840 pclrCurrent++;
1841 }
1842
1843 /* Add the scaned line to the region */
1844 SetRect(prcCurrent, xstart, y,x,y+1);
1845 prcCurrent++;
1846 cRgnRects++;
1847
1848 /* Increase the size of the buffer if it is full */
1849 if(cRgnRects == cMaxRgnRects)
1850 {
1851 cMaxRgnRects *=2;
1852 cRgnDataSize = sizeof(RGNDATA) + cMaxRgnRects * sizeof(RECT);
1853 prgnData = (PRGNDATA)HeapReAlloc(GetProcessHeap(),
1854 0,
1855 prgnData,
1856 cRgnDataSize);
1857 prcCurrent = (RECT*)prgnData->Buffer + cRgnRects;
1858 }
1859 }
1860 else
1861 {
1862 x++;
1863 pclrCurrent++;
1864 }
1865 }
1866 y++;
1867 }
1868
1869 /* Fill the region data header */
1870 prgnData->rdh.dwSize = sizeof(prgnData->rdh);
1871 prgnData->rdh.iType = RDH_RECTANGLES;
1872 prgnData->rdh.nCount = cRgnRects;
1873 prgnData->rdh.nRgnSize = cRgnDataSize;
1874 prgnData->rdh.rcBound = *pRect;
1875
1876 /* Create the region*/
1877 hrgnRet = ExtCreateRegion (NULL, cRgnDataSize, prgnData);
1878
1879 /* Free the region data*/
1880 HeapFree(GetProcessHeap(),0,prgnData);
1881
1882 /* return the region*/
1883 return hrgnRet;
1884 }
1885
1886 HRESULT UXTHEME_GetImageBackBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect, HRGN *pRegion)
1887 {
1888 HBITMAP hbmp;
1889 DIBSECTION dib;
1890 RGBQUAD clrTransparent = {0xFF,0x0, 0xFF,0x0};
1891
1892 /* Draw the theme part to a dib */
1893 hbmp = UXTHEME_DrawThemePartToDib(hTheme, hdc, iPartId, iStateId, pRect);
1894
1895 /* Retrieve the info of the dib section */
1896 GetObjectW(hbmp, sizeof (DIBSECTION), &dib);
1897
1898 /* Convert the bits of the dib section to a region */
1899 *pRegion = UXTHEME_RegionFromDibBits((RGBQUAD*)dib.dsBm.bmBits, &clrTransparent, pRect);
1900
1901 /* Free the temp bitmap */
1902 DeleteObject(hbmp);
1903
1904 return S_OK;
1905 }
1906
1907 /***********************************************************************
1908 * GetThemeBackgroundRegion (UXTHEME.@)
1909 *
1910 * Calculate the background region, taking into consideration transparent areas
1911 * of the background image.
1912 */
1913 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1914 int iStateId, const RECT *pRect,
1915 HRGN *pRegion)
1916 {
1917 HRESULT hr = S_OK;
1918 int bgtype = BT_BORDERFILL;
1919
1920 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1921 if(!hTheme)
1922 return E_HANDLE;
1923 if(!pRect || !pRegion)
1924 return E_POINTER;
1925
1926 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1927 if(bgtype == BT_IMAGEFILE) {
1928 hr = UXTHEME_GetImageBackBackgroundRegion(hTheme, hdc, iPartId, iStateId, pRect, pRegion);
1929 }
1930 else if(bgtype == BT_BORDERFILL) {
1931 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1932 if(!*pRegion)
1933 hr = HRESULT_FROM_WIN32(GetLastError());
1934 }
1935 else {
1936 FIXME("Unknown background type\n");
1937 /* This should never happen, and hence I don't know what to return */
1938 hr = E_FAIL;
1939 }
1940 return hr;
1941 }
1942
1943 /* compute part size for "borderfill" backgrounds */
1944 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
1945 int iStateId, THEMESIZE eSize, POINT* psz)
1946 {
1947 HRESULT hr = S_OK;
1948 int bordersize = 1;
1949
1950 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
1951 &bordersize)))
1952 {
1953 psz->x = psz->y = 2*bordersize;
1954 if (eSize != TS_MIN)
1955 {
1956 psz->x++;
1957 psz->y++;
1958 }
1959 }
1960 return hr;
1961 }
1962
1963 /***********************************************************************
1964 * GetThemePartSize (UXTHEME.@)
1965 */
1966 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
1967 int iStateId, RECT *prc, THEMESIZE eSize,
1968 SIZE *psz)
1969 {
1970 int bgtype = BT_BORDERFILL;
1971 HRESULT hr = S_OK;
1972 POINT size = {1, 1};
1973
1974 if(!hTheme)
1975 return E_HANDLE;
1976
1977 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1978 if (bgtype == BT_NONE)
1979 /* do nothing */;
1980 else if(bgtype == BT_IMAGEFILE)
1981 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
1982 else if(bgtype == BT_BORDERFILL)
1983 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
1984 else {
1985 FIXME("Unknown background type\n");
1986 /* This should never happen, and hence I don't know what to return */
1987 hr = E_FAIL;
1988 }
1989 psz->cx = size.x;
1990 psz->cy = size.y;
1991 return hr;
1992 }
1993
1994
1995 /***********************************************************************
1996 * GetThemeTextExtent (UXTHEME.@)
1997 */
1998 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
1999 int iStateId, LPCWSTR pszText, int iCharCount,
2000 DWORD dwTextFlags, const RECT *pBoundingRect,
2001 RECT *pExtentRect)
2002 {
2003 HRESULT hr;
2004 HFONT hFont = NULL;
2005 HGDIOBJ oldFont = NULL;
2006 LOGFONTW logfont;
2007 RECT rt = {0,0,0xFFFF,0xFFFF};
2008
2009 TRACE("%d %d: stub\n", iPartId, iStateId);
2010 if(!hTheme)
2011 return E_HANDLE;
2012
2013 if(pBoundingRect)
2014 CopyRect(&rt, pBoundingRect);
2015
2016 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2017 if(SUCCEEDED(hr)) {
2018 hFont = CreateFontIndirectW(&logfont);
2019 if(!hFont)
2020 TRACE("Failed to create font\n");
2021 }
2022 if(hFont)
2023 oldFont = SelectObject(hdc, hFont);
2024
2025 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2026 CopyRect(pExtentRect, &rt);
2027
2028 if(hFont) {
2029 SelectObject(hdc, oldFont);
2030 DeleteObject(hFont);
2031 }
2032 return S_OK;
2033 }
2034
2035 /***********************************************************************
2036 * GetThemeTextMetrics (UXTHEME.@)
2037 */
2038 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2039 int iStateId, TEXTMETRICW *ptm)
2040 {
2041 HRESULT hr;
2042 HFONT hFont = NULL;
2043 HGDIOBJ oldFont = NULL;
2044 LOGFONTW logfont;
2045
2046 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2047 if(!hTheme)
2048 return E_HANDLE;
2049
2050 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2051 if(SUCCEEDED(hr)) {
2052 hFont = CreateFontIndirectW(&logfont);
2053 if(!hFont)
2054 TRACE("Failed to create font\n");
2055 }
2056 if(hFont)
2057 oldFont = SelectObject(hdc, hFont);
2058
2059 if(!GetTextMetricsW(hdc, ptm))
2060 hr = HRESULT_FROM_WIN32(GetLastError());
2061
2062 if(hFont) {
2063 SelectObject(hdc, oldFont);
2064 DeleteObject(hFont);
2065 }
2066 return hr;
2067 }
2068
2069 /***********************************************************************
2070 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2071 */
2072 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2073 int iStateId)
2074 {
2075 int bgtype = BT_BORDERFILL;
2076 RECT rect = {0, 0, 0, 0};
2077 HBITMAP bmpSrc;
2078 RECT rcSrc;
2079 BOOL hasAlpha;
2080 INT transparent;
2081 COLORREF transparentcolor;
2082
2083 TRACE("(%d,%d)\n", iPartId, iStateId);
2084
2085 if(!hTheme)
2086 return FALSE;
2087
2088 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2089
2090 #ifdef __REACTOS__
2091 if (bgtype == BT_NONE) return TRUE;
2092 #endif
2093 if (bgtype != BT_IMAGEFILE) return FALSE;
2094
2095 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2096 &bmpSrc, &rcSrc, &hasAlpha)))
2097 return FALSE;
2098
2099 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2100 &transparentcolor, FALSE);
2101 return (transparent != ALPHABLEND_NONE);
2102 }