[UXTHME] Implement drawing themed text with shadows.
[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 typedef int (WINAPI * DRAWSHADOWTEXT)(HDC hdc, LPCWSTR pszText, UINT cch, RECT *prc, DWORD dwFlags,
1621 COLORREF crText, COLORREF crShadow, int ixOffset, int iyOffset);
1622
1623 /***********************************************************************
1624 * DrawThemeText (UXTHEME.@)
1625 */
1626 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1627 LPCWSTR pszText, int iCharCount, DWORD dwTextFlags,
1628 DWORD dwTextFlags2, const RECT *pRect)
1629 {
1630 HRESULT hr;
1631 HFONT hFont = NULL;
1632 HGDIOBJ oldFont = NULL;
1633 LOGFONTW logfont;
1634 COLORREF textColor;
1635 COLORREF oldTextColor;
1636 COLORREF shadowColor;
1637 POINT ptShadowOffset;
1638 int oldBkMode;
1639 RECT rt;
1640 int iShadowType;
1641
1642 TRACE("%d %d: stub\n", iPartId, iStateId);
1643 if(!hTheme)
1644 return E_HANDLE;
1645
1646 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
1647 if(SUCCEEDED(hr))
1648 {
1649 hFont = CreateFontIndirectW(&logfont);
1650 if(!hFont)
1651 {
1652 ERR("Failed to create font\n");
1653 }
1654 }
1655
1656 CopyRect(&rt, pRect);
1657 if(hFont)
1658 oldFont = SelectObject(hdc, hFont);
1659
1660 oldBkMode = SetBkMode(hdc, TRANSPARENT);
1661
1662 if(dwTextFlags2 & DTT_GRAYED)
1663 textColor = GetSysColor(COLOR_GRAYTEXT);
1664 else {
1665 if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
1666 textColor = GetTextColor(hdc);
1667 }
1668
1669 hr = GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_TEXTSHADOWTYPE, &iShadowType);
1670 if (SUCCEEDED(hr))
1671 {
1672 ERR("Got shadow type %d\n", iShadowType);
1673
1674 hr = GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTSHADOWCOLOR, &shadowColor);
1675 if (FAILED(hr))
1676 {
1677 ERR("GetThemeColor failed\n");
1678 }
1679
1680 hr = GetThemePosition(hTheme, iPartId, iStateId, TMT_TEXTSHADOWOFFSET, &ptShadowOffset);
1681 if (FAILED(hr))
1682 {
1683 ERR("GetThemePosition failed\n");
1684 }
1685
1686 if (iShadowType == TST_SINGLE)
1687 {
1688 oldTextColor = SetTextColor(hdc, shadowColor);
1689 OffsetRect(&rt, ptShadowOffset.x, ptShadowOffset.y);
1690 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1691 OffsetRect(&rt, -ptShadowOffset.x, -ptShadowOffset.y);
1692 SetTextColor(hdc, oldTextColor);
1693 }
1694 else if (iShadowType == TST_CONTINUOUS)
1695 {
1696 HANDLE hcomctl32 = GetModuleHandleW(L"comctl32.dll");
1697 DRAWSHADOWTEXT pDrawShadowText;
1698 if (!hcomctl32)
1699 {
1700 hcomctl32 = LoadLibraryW(L"comctl32.dll");
1701 if (!hcomctl32)
1702 ERR("Failed to load comctl32\n");
1703 }
1704
1705 pDrawShadowText = (DRAWSHADOWTEXT)GetProcAddress(hcomctl32, "DrawShadowText");
1706 if (pDrawShadowText)
1707 {
1708 pDrawShadowText(hdc, pszText, iCharCount, &rt, dwTextFlags, textColor, shadowColor, ptShadowOffset.x, ptShadowOffset.y);
1709 goto cleanup;
1710 }
1711 }
1712 }
1713
1714 oldTextColor = SetTextColor(hdc, textColor);
1715 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
1716 SetTextColor(hdc, oldTextColor);
1717 cleanup:
1718 SetBkMode(hdc, oldBkMode);
1719
1720 if(hFont) {
1721 SelectObject(hdc, oldFont);
1722 DeleteObject(hFont);
1723 }
1724 return S_OK;
1725 }
1726
1727 /***********************************************************************
1728 * GetThemeBackgroundContentRect (UXTHEME.@)
1729 */
1730 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
1731 int iStateId,
1732 const RECT *pBoundingRect,
1733 RECT *pContentRect)
1734 {
1735 MARGINS margin;
1736 HRESULT hr;
1737
1738 TRACE("(%d,%d)\n", iPartId, iStateId);
1739 if(!hTheme)
1740 return E_HANDLE;
1741
1742 /* try content margins property... */
1743 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1744 if(SUCCEEDED(hr)) {
1745 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1746 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1747 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1748 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1749 } else {
1750 /* otherwise, try to determine content rect from the background type and props */
1751 int bgtype = BT_BORDERFILL;
1752 *pContentRect = *pBoundingRect;
1753
1754 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1755 if(bgtype == BT_BORDERFILL) {
1756 int bordersize = 1;
1757
1758 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1759 InflateRect(pContentRect, -bordersize, -bordersize);
1760 } else if ((bgtype == BT_IMAGEFILE)
1761 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1762 TMT_SIZINGMARGINS, NULL, &margin)))) {
1763 pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
1764 pContentRect->top = pBoundingRect->top + margin.cyTopHeight;
1765 pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
1766 pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
1767 }
1768 /* If nothing was found, leave unchanged */
1769 }
1770
1771 TRACE("%s\n", wine_dbgstr_rect(pContentRect));
1772
1773 return S_OK;
1774 }
1775
1776 /***********************************************************************
1777 * GetThemeBackgroundExtent (UXTHEME.@)
1778 */
1779 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
1780 int iStateId, const RECT *pContentRect,
1781 RECT *pExtentRect)
1782 {
1783 MARGINS margin;
1784 HRESULT hr;
1785
1786 TRACE("(%d,%d)\n", iPartId, iStateId);
1787 if(!hTheme)
1788 return E_HANDLE;
1789
1790 /* try content margins property... */
1791 hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
1792 if(SUCCEEDED(hr)) {
1793 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1794 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1795 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1796 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1797 } else {
1798 /* otherwise, try to determine content rect from the background type and props */
1799 int bgtype = BT_BORDERFILL;
1800 *pExtentRect = *pContentRect;
1801
1802 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1803 if(bgtype == BT_BORDERFILL) {
1804 int bordersize = 1;
1805
1806 GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
1807 InflateRect(pExtentRect, bordersize, bordersize);
1808 } else if ((bgtype == BT_IMAGEFILE)
1809 && (SUCCEEDED(hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId,
1810 TMT_SIZINGMARGINS, NULL, &margin)))) {
1811 pExtentRect->left = pContentRect->left - margin.cxLeftWidth;
1812 pExtentRect->top = pContentRect->top - margin.cyTopHeight;
1813 pExtentRect->right = pContentRect->right + margin.cxRightWidth;
1814 pExtentRect->bottom = pContentRect->bottom + margin.cyBottomHeight;
1815 }
1816 /* If nothing was found, leave unchanged */
1817 }
1818
1819 TRACE("%s\n", wine_dbgstr_rect(pExtentRect));
1820
1821 return S_OK;
1822 }
1823
1824
1825 static HBITMAP UXTHEME_DrawThemePartToDib(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect)
1826 {
1827 HDC hdcMem;
1828 BITMAPINFO bmi;
1829 HBITMAP hbmp, hbmpOld;
1830 HBRUSH hbrBack;
1831
1832 hdcMem = CreateCompatibleDC(0);
1833
1834 memset(&bmi, 0, sizeof(bmi));
1835 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
1836 bmi.bmiHeader.biWidth = pRect->right;
1837 bmi.bmiHeader.biHeight = -pRect->bottom;
1838 bmi.bmiHeader.biPlanes = 1;
1839 bmi.bmiHeader.biBitCount = 32;
1840 hbmp = CreateDIBSection(hdcMem, &bmi, DIB_RGB_COLORS , NULL, 0, 0);
1841
1842 hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp);
1843
1844 /* FIXME: use an internal function that doesn't do transparent blt */
1845 hbrBack = CreateSolidBrush(RGB(255,0,255));
1846
1847 FillRect(hdcMem, pRect, hbrBack);
1848
1849 DrawThemeBackground(hTheme, hdcMem, iPartId, iStateId, pRect, NULL);
1850
1851 DeleteObject(hbrBack);
1852 SelectObject(hdcMem, hbmpOld);
1853 DeleteObject(hdcMem);
1854
1855 return hbmp;
1856 }
1857
1858 #define PT_IN_RECT(lprc,x,y) ( x >= lprc->left && x < lprc->right && \
1859 y >= lprc->top && y < lprc->bottom)
1860
1861 static HRGN UXTHEME_RegionFromDibBits(RGBQUAD* pBuffer, RGBQUAD* pclrTransparent, LPCRECT pRect)
1862 {
1863 int x, y, xstart;
1864 int cMaxRgnRects, cRgnDataSize, cRgnRects;
1865 RECT* prcCurrent;
1866 PRGNDATA prgnData;
1867 ULONG clrTransparent, *pclrCurrent;
1868 HRGN hrgnRet;
1869
1870 pclrCurrent = (PULONG)pBuffer;
1871 clrTransparent = *(PULONG)pclrTransparent;
1872
1873 /* Create a region and pre-allocate memory enough for 3 spaces in one row*/
1874 cRgnRects = 0;
1875 cMaxRgnRects = 4* (pRect->bottom-pRect->top);
1876 cRgnDataSize = sizeof(RGNDATA) + cMaxRgnRects * sizeof(RECT);
1877
1878 /* Allocate the region data */
1879 prgnData = (PRGNDATA)HeapAlloc(GetProcessHeap(), 0, cRgnDataSize);
1880
1881 prcCurrent = (PRECT)prgnData->Buffer;
1882
1883 /* Calculate the region rects */
1884 y=0;
1885 /* Scan each line of the bitmap */
1886 while(y<pRect->bottom)
1887 {
1888 x=0;
1889 /* Scan each pixel */
1890 while (x<pRect->right)
1891 {
1892 /* Check if the pixel is not transparent and it is in the requested rect */
1893 if(*pclrCurrent != clrTransparent && PT_IN_RECT(pRect,x,y))
1894 {
1895 xstart = x;
1896 /* Find the end of the opaque row of pixels */
1897 while (x<pRect->right)
1898 {
1899 if(*pclrCurrent == clrTransparent || !PT_IN_RECT(pRect,x,y))
1900 break;
1901 x++;
1902 pclrCurrent++;
1903 }
1904
1905 /* Add the scaned line to the region */
1906 SetRect(prcCurrent, xstart, y,x,y+1);
1907 prcCurrent++;
1908 cRgnRects++;
1909
1910 /* Increase the size of the buffer if it is full */
1911 if(cRgnRects == cMaxRgnRects)
1912 {
1913 cMaxRgnRects *=2;
1914 cRgnDataSize = sizeof(RGNDATA) + cMaxRgnRects * sizeof(RECT);
1915 prgnData = (PRGNDATA)HeapReAlloc(GetProcessHeap(),
1916 0,
1917 prgnData,
1918 cRgnDataSize);
1919 prcCurrent = (RECT*)prgnData->Buffer + cRgnRects;
1920 }
1921 }
1922 else
1923 {
1924 x++;
1925 pclrCurrent++;
1926 }
1927 }
1928 y++;
1929 }
1930
1931 /* Fill the region data header */
1932 prgnData->rdh.dwSize = sizeof(prgnData->rdh);
1933 prgnData->rdh.iType = RDH_RECTANGLES;
1934 prgnData->rdh.nCount = cRgnRects;
1935 prgnData->rdh.nRgnSize = cRgnDataSize;
1936 prgnData->rdh.rcBound = *pRect;
1937
1938 /* Create the region*/
1939 hrgnRet = ExtCreateRegion (NULL, cRgnDataSize, prgnData);
1940
1941 /* Free the region data*/
1942 HeapFree(GetProcessHeap(),0,prgnData);
1943
1944 /* return the region*/
1945 return hrgnRet;
1946 }
1947
1948 HRESULT UXTHEME_GetImageBackBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCRECT pRect, HRGN *pRegion)
1949 {
1950 HBITMAP hbmp;
1951 DIBSECTION dib;
1952 RGBQUAD clrTransparent = {0xFF,0x0, 0xFF,0x0};
1953
1954 /* Draw the theme part to a dib */
1955 hbmp = UXTHEME_DrawThemePartToDib(hTheme, hdc, iPartId, iStateId, pRect);
1956
1957 /* Retrieve the info of the dib section */
1958 GetObjectW(hbmp, sizeof (DIBSECTION), &dib);
1959
1960 /* Convert the bits of the dib section to a region */
1961 *pRegion = UXTHEME_RegionFromDibBits((RGBQUAD*)dib.dsBm.bmBits, &clrTransparent, pRect);
1962
1963 /* Free the temp bitmap */
1964 DeleteObject(hbmp);
1965
1966 return S_OK;
1967 }
1968
1969 /***********************************************************************
1970 * GetThemeBackgroundRegion (UXTHEME.@)
1971 *
1972 * Calculate the background region, taking into consideration transparent areas
1973 * of the background image.
1974 */
1975 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
1976 int iStateId, const RECT *pRect,
1977 HRGN *pRegion)
1978 {
1979 HRESULT hr = S_OK;
1980 int bgtype = BT_BORDERFILL;
1981
1982 TRACE("(%p,%p,%d,%d)\n", hTheme, hdc, iPartId, iStateId);
1983 if(!hTheme)
1984 return E_HANDLE;
1985 if(!pRect || !pRegion)
1986 return E_POINTER;
1987
1988 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
1989 if(bgtype == BT_IMAGEFILE) {
1990 hr = UXTHEME_GetImageBackBackgroundRegion(hTheme, hdc, iPartId, iStateId, pRect, pRegion);
1991 }
1992 else if(bgtype == BT_BORDERFILL) {
1993 *pRegion = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
1994 if(!*pRegion)
1995 hr = HRESULT_FROM_WIN32(GetLastError());
1996 }
1997 else {
1998 FIXME("Unknown background type\n");
1999 /* This should never happen, and hence I don't know what to return */
2000 hr = E_FAIL;
2001 }
2002 return hr;
2003 }
2004
2005 /* compute part size for "borderfill" backgrounds */
2006 static HRESULT get_border_background_size (HTHEME hTheme, int iPartId,
2007 int iStateId, THEMESIZE eSize, POINT* psz)
2008 {
2009 HRESULT hr = S_OK;
2010 int bordersize = 1;
2011
2012 if (SUCCEEDED (hr = GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE,
2013 &bordersize)))
2014 {
2015 psz->x = psz->y = 2*bordersize;
2016 if (eSize != TS_MIN)
2017 {
2018 psz->x++;
2019 psz->y++;
2020 }
2021 }
2022 return hr;
2023 }
2024
2025 /***********************************************************************
2026 * GetThemePartSize (UXTHEME.@)
2027 */
2028 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
2029 int iStateId, RECT *prc, THEMESIZE eSize,
2030 SIZE *psz)
2031 {
2032 int bgtype = BT_BORDERFILL;
2033 HRESULT hr = S_OK;
2034 POINT size = {1, 1};
2035
2036 if(!hTheme)
2037 return E_HANDLE;
2038
2039 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2040 if (bgtype == BT_NONE)
2041 /* do nothing */;
2042 else if(bgtype == BT_IMAGEFILE)
2043 hr = get_image_part_size (hTheme, hdc, iPartId, iStateId, prc, eSize, &size);
2044 else if(bgtype == BT_BORDERFILL)
2045 hr = get_border_background_size (hTheme, iPartId, iStateId, eSize, &size);
2046 else {
2047 FIXME("Unknown background type\n");
2048 /* This should never happen, and hence I don't know what to return */
2049 hr = E_FAIL;
2050 }
2051 psz->cx = size.x;
2052 psz->cy = size.y;
2053 return hr;
2054 }
2055
2056
2057 /***********************************************************************
2058 * GetThemeTextExtent (UXTHEME.@)
2059 */
2060 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
2061 int iStateId, LPCWSTR pszText, int iCharCount,
2062 DWORD dwTextFlags, const RECT *pBoundingRect,
2063 RECT *pExtentRect)
2064 {
2065 HRESULT hr;
2066 HFONT hFont = NULL;
2067 HGDIOBJ oldFont = NULL;
2068 LOGFONTW logfont;
2069 RECT rt = {0,0,0xFFFF,0xFFFF};
2070
2071 TRACE("%d %d: stub\n", iPartId, iStateId);
2072 if(!hTheme)
2073 return E_HANDLE;
2074
2075 if(pBoundingRect)
2076 rt = *pBoundingRect;
2077
2078 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2079 if(SUCCEEDED(hr)) {
2080 hFont = CreateFontIndirectW(&logfont);
2081 if(!hFont)
2082 TRACE("Failed to create font\n");
2083 }
2084 if(hFont)
2085 oldFont = SelectObject(hdc, hFont);
2086
2087 DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
2088 *pExtentRect = rt;
2089
2090 if(hFont) {
2091 SelectObject(hdc, oldFont);
2092 DeleteObject(hFont);
2093 }
2094 return S_OK;
2095 }
2096
2097 /***********************************************************************
2098 * GetThemeTextMetrics (UXTHEME.@)
2099 */
2100 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
2101 int iStateId, TEXTMETRICW *ptm)
2102 {
2103 HRESULT hr;
2104 HFONT hFont = NULL;
2105 HGDIOBJ oldFont = NULL;
2106 LOGFONTW logfont;
2107
2108 TRACE("(%p, %p, %d, %d)\n", hTheme, hdc, iPartId, iStateId);
2109 if(!hTheme)
2110 return E_HANDLE;
2111
2112 hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
2113 if(SUCCEEDED(hr)) {
2114 hFont = CreateFontIndirectW(&logfont);
2115 if(!hFont)
2116 TRACE("Failed to create font\n");
2117 }
2118 if(hFont)
2119 oldFont = SelectObject(hdc, hFont);
2120
2121 if(!GetTextMetricsW(hdc, ptm))
2122 hr = HRESULT_FROM_WIN32(GetLastError());
2123
2124 if(hFont) {
2125 SelectObject(hdc, oldFont);
2126 DeleteObject(hFont);
2127 }
2128 return hr;
2129 }
2130
2131 /***********************************************************************
2132 * IsThemeBackgroundPartiallyTransparent (UXTHEME.@)
2133 */
2134 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
2135 int iStateId)
2136 {
2137 int bgtype = BT_BORDERFILL;
2138 RECT rect = {0, 0, 0, 0};
2139 HBITMAP bmpSrc;
2140 RECT rcSrc;
2141 BOOL hasAlpha;
2142 INT transparent;
2143 COLORREF transparentcolor;
2144
2145 TRACE("(%d,%d)\n", iPartId, iStateId);
2146
2147 if(!hTheme)
2148 return FALSE;
2149
2150 GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
2151
2152 #ifdef __REACTOS__
2153 if (bgtype == BT_NONE) return TRUE;
2154 #endif
2155 if (bgtype != BT_IMAGEFILE) return FALSE;
2156
2157 if(FAILED (UXTHEME_LoadImage (hTheme, 0, iPartId, iStateId, &rect, FALSE,
2158 &bmpSrc, &rcSrc, &hasAlpha)))
2159 return FALSE;
2160
2161 get_transparency (hTheme, iPartId, iStateId, hasAlpha, &transparent,
2162 &transparentcolor, FALSE);
2163 return (transparent != ALPHABLEND_NONE);
2164 }