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