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