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