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