Allow the implementation of the VxDCall entry points to be moved to
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "uxtheme.h"
31 #include "tmschema.h"
32
33 #include "msstyles.h"
34 #include "uxthemedll.h"
35
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(uxtheme);
39
40 /***********************************************************************
41  * Defines and global variables
42  */
43
44 DWORD dwDialogTextureFlags;
45
46 /***********************************************************************/
47
48 /***********************************************************************
49  *      EnableThemeDialogTexture                            (UXTHEME.@)
50  */
51 HRESULT WINAPI EnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
52 {
53     TRACE("(%p,0x%08lx\n", hwnd, dwFlags);
54     dwDialogTextureFlags = dwFlags;
55     return S_OK;
56  }
57
58 /***********************************************************************
59  *      IsThemeDialogTextureEnabled                         (UXTHEME.@)
60  */
61 BOOL WINAPI IsThemeDialogTextureEnabled(void)
62 {
63     TRACE("\n");
64     return (dwDialogTextureFlags & ETDT_ENABLE) && !(dwDialogTextureFlags & ETDT_DISABLE);
65 }
66
67 /***********************************************************************
68  *      DrawThemeParentBackground                           (UXTHEME.@)
69  */
70 HRESULT WINAPI DrawThemeParentBackground(HWND hwnd, HDC hdc, RECT *prc)
71 {
72     RECT rt;
73     POINT org;
74     HWND hParent;
75     TRACE("(%p,%p,%p)\n", hwnd, hdc, prc);
76     hParent = GetParent(hwnd);
77     if(!hParent)
78         hParent = hwnd;
79     if(prc) {
80         CopyRect(&rt, prc);
81         MapWindowPoints(hwnd, NULL, (LPPOINT)&rt, 2);
82     }
83     else {
84         GetClientRect(hParent, &rt);
85         MapWindowPoints(hParent, NULL, (LPPOINT)&rt, 2);
86     }
87
88     SetViewportOrgEx(hdc, rt.left, rt.top, &org);
89
90     SendMessageW(hParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
91     SendMessageW(hParent, WM_PRINTCLIENT, (WPARAM)hdc, PRF_CLIENT);
92
93     SetViewportOrgEx(hdc, org.x, org.y, NULL);
94     return S_OK;
95 }
96
97
98 /***********************************************************************
99  *      DrawThemeBackground                                 (UXTHEME.@)
100  */
101 HRESULT WINAPI DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId,
102                                    int iStateId, const RECT *pRect,
103                                    const RECT *pClipRect)
104 {
105     DTBGOPTS opts;
106     opts.dwSize = sizeof(DTBGOPTS);
107     opts.dwFlags = 0;
108     if(pClipRect) {
109         opts.dwFlags |= DTBG_CLIPRECT;
110         CopyRect(&opts.rcClip, pClipRect);
111     }
112     return DrawThemeBackgroundEx(hTheme, hdc, iPartId, iStateId, pRect, &opts);
113 }
114
115 /***********************************************************************
116  *      UXTHEME_SelectImage
117  *
118  * Select the image to use
119  */
120 PTHEME_PROPERTY UXTHEME_SelectImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph)
121 {
122     PTHEME_PROPERTY tp;
123     int imageselecttype = IST_NONE;
124     int i;
125     int image;
126     if(glyph)
127         image = TMT_GLYPHIMAGEFILE;
128     else
129         image = TMT_IMAGEFILE;
130
131     if((tp=MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, image)))
132         return tp;
133     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_IMAGESELECTTYPE, &imageselecttype);
134
135     if(imageselecttype == IST_DPI) {
136         int reqdpi = 0;
137         int screendpi = GetDeviceCaps(hdc, LOGPIXELSX);
138         for(i=4; i>=0; i--) {
139             reqdpi = 0;
140             if(SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, i + TMT_MINDPI1, &reqdpi))) {
141                 if(reqdpi != 0 && screendpi >= reqdpi) {
142                     TRACE("Using %d DPI, image %d\n", reqdpi, i + TMT_IMAGEFILE1);
143                     return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
144                 }
145             }
146         }
147         /* If an image couldnt be selected, choose the first one */
148         return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
149     }
150     else if(imageselecttype == IST_SIZE) {
151         POINT size = {pRect->right-pRect->left, pRect->bottom-pRect->top};
152         POINT reqsize;
153         for(i=4; i>=0; i--) {
154             if(SUCCEEDED(GetThemePosition(hTheme, iPartId, iStateId, i + TMT_MINSIZE1, &reqsize))) {
155                 if(reqsize.x >= size.x && reqsize.y >= size.y) {
156                     TRACE("Using image size %ldx%ld, image %d\n", reqsize.x, reqsize.y, i + TMT_IMAGEFILE1);
157                     return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, i + TMT_IMAGEFILE1);
158                 }
159             }
160         }
161         /* If an image couldnt be selected, choose the smallest one */
162         return MSSTYLES_FindProperty(hTheme, iPartId, iStateId, TMT_FILENAME, TMT_IMAGEFILE1);
163     }
164     return NULL;
165 }
166
167 /***********************************************************************
168  *      UXTHEME_LoadImage
169  *
170  * Load image for part/state
171  */
172 HRESULT UXTHEME_LoadImage(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, BOOL glyph,
173                           HBITMAP *hBmp, RECT *bmpRect)
174 {
175     int imagelayout = IL_VERTICAL;
176     int imagecount = 0;
177     BITMAP bmp;
178     WCHAR szPath[MAX_PATH];
179     PTHEME_PROPERTY tp = UXTHEME_SelectImage(hTheme, hdc, iPartId, iStateId, pRect, glyph);
180     if(!tp) {
181         FIXME("Couldn't determine image for part/state %d/%d, invalid theme?\n", iPartId, iStateId);
182         return E_PROP_ID_UNSUPPORTED;
183     }
184     lstrcpynW(szPath, tp->lpValue, min(tp->dwValueLen+1, sizeof(szPath)/sizeof(szPath[0])));
185     *hBmp = MSSTYLES_LoadBitmap(hdc, hTheme, szPath);
186     if(!*hBmp) {
187         TRACE("Failed to load bitmap %s\n", debugstr_w(szPath));
188         return HRESULT_FROM_WIN32(GetLastError());
189     }
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         int height = bmp.bmHeight/imagecount;
197         bmpRect->left = 0;
198         bmpRect->right = bmp.bmWidth;
199         bmpRect->top = (min(imagecount, iStateId)-1) * height;
200         bmpRect->bottom = bmpRect->top + height;
201     }
202     else {
203         int width = bmp.bmWidth/imagecount;
204         bmpRect->left = (min(imagecount, iStateId)-1) * width;
205         bmpRect->right = bmpRect->left + width;
206         bmpRect->top = 0;
207         bmpRect->bottom = bmp.bmHeight;
208     }
209     return S_OK;
210 }
211
212 /***********************************************************************
213  *      UXTHEME_StretchBlt
214  *
215  * Psudo TransparentBlt/StretchBlt
216  */
217 static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
218                                       HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
219                                       BOOL transparent, COLORREF transcolor)
220 {
221     if(transparent) {
222         /* Ensure we dont pass any negative values to TransparentBlt */
223         return TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
224                               hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
225                               transcolor);
226     }
227     /* This should be using AlphaBlend */
228     return StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
229                         hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
230                         SRCCOPY);
231 }
232
233 /***********************************************************************
234  *      UXTHEME_Blt
235  *
236  * Simplify sending same width/height for both source and dest
237  */
238 static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
239                                HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
240                                BOOL transparent, COLORREF transcolor)
241 {
242     return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
243                               hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
244                               transparent, transcolor);
245 }
246
247
248 /***********************************************************************
249  *      UXTHEME_DrawImageGlyph
250  *
251  * Draw an imagefile glyph
252  */
253 HRESULT UXTHEME_DrawImageGlyph(HTHEME hTheme, HDC hdc, int iPartId,
254                                int iStateId, RECT *pRect,
255                                const DTBGOPTS *pOptions)
256 {
257     HRESULT hr;
258     HBITMAP bmpSrc = NULL;
259     HDC hdcSrc = NULL;
260     HGDIOBJ oldSrc = NULL;
261     RECT rcSrc;
262     BOOL transparent = FALSE;
263     COLORREF transparentcolor = 0;
264     int valign = VA_CENTER;
265     int halign = HA_CENTER;
266     POINT dstSize;
267     POINT srcSize;
268     POINT topleft;
269
270     hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, TRUE, &bmpSrc, &rcSrc);
271     if(FAILED(hr)) return hr;
272     hdcSrc = CreateCompatibleDC(hdc);
273     if(!hdcSrc) {
274         hr = HRESULT_FROM_WIN32(GetLastError());
275         DeleteObject(bmpSrc);
276         return hr;
277     }
278     oldSrc = SelectObject(hdcSrc, bmpSrc);
279
280     dstSize.x = pRect->right-pRect->left;
281     dstSize.y = pRect->bottom-pRect->top;
282     srcSize.x = rcSrc.right-rcSrc.left;
283     srcSize.y = rcSrc.bottom-rcSrc.top;
284
285     GetThemeBool(hTheme, iPartId, iStateId, TMT_GLYPHTRANSPARENT, &transparent);
286     if(transparent) {
287         if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_GLYPHTRANSPARENTCOLOR, &transparentcolor))) {
288             /* If image is transparent, but no color was specified, get the color of the upper left corner */
289             transparentcolor = GetPixel(hdcSrc, 0, 0);
290         }
291     }
292     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_VALIGN, &valign);
293     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_HALIGN, &halign);
294
295     topleft.x = pRect->left;
296     topleft.y = pRect->top;
297     if(halign == HA_CENTER)      topleft.x += (dstSize.x/2)-(srcSize.x/2);
298     else if(halign == HA_RIGHT)  topleft.x += dstSize.x-srcSize.x;
299     if(valign == VA_CENTER)      topleft.y += (dstSize.y/2)-(srcSize.y/2);
300     else if(valign == VA_BOTTOM) topleft.y += dstSize.y-srcSize.y;
301
302     if(!UXTHEME_Blt(hdc, topleft.x, topleft.y, srcSize.x, srcSize.y,
303                     hdcSrc, rcSrc.left, rcSrc.top,
304                     transparent, transparentcolor)) {
305         hr = HRESULT_FROM_WIN32(GetLastError());
306     }
307
308     SelectObject(hdcSrc, oldSrc);
309     DeleteDC(hdcSrc);
310     DeleteObject(bmpSrc);
311     return hr;
312 }
313
314 /***********************************************************************
315  *      UXTHEME_DrawImageGlyph
316  *
317  * Draw glyph on top of background, if appropriate
318  */
319 HRESULT UXTHEME_DrawGlyph(HTHEME hTheme, HDC hdc, int iPartId,
320                                     int iStateId, RECT *pRect,
321                                     const DTBGOPTS *pOptions)
322 {
323     int glyphtype = GT_NONE;
324
325     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_GLYPHTYPE, &glyphtype);
326
327     if(glyphtype == GT_IMAGEGLYPH) {
328         return UXTHEME_DrawImageGlyph(hTheme, hdc, iPartId, iStateId, pRect, pOptions);
329     }
330     else if(glyphtype == GT_FONTGLYPH) {
331         /* I don't know what a font glyph is, I've never seen it used in any themes */
332         FIXME("Font glyph\n");
333     }
334     return S_OK;
335 }
336
337 /***********************************************************************
338  *      UXTHEME_DrawImageBackground
339  *
340  * Draw an imagefile background
341  */
342 HRESULT UXTHEME_DrawImageBackground(HTHEME hTheme, HDC hdc, int iPartId,
343                                     int iStateId, RECT *pRect,
344                                     const DTBGOPTS *pOptions)
345 {
346     HRESULT hr = S_OK;
347     HBITMAP bmpSrc;
348     HGDIOBJ oldSrc;
349     HDC hdcSrc;
350     RECT rcSrc;
351     RECT rcDst;
352     POINT dstSize;
353     POINT srcSize;
354     int sizingtype = ST_TRUESIZE;
355     BOOL uniformsizing = FALSE;
356     BOOL transparent = FALSE;
357     COLORREF transparentcolor = 0;
358
359     hr = UXTHEME_LoadImage(hTheme, hdc, iPartId, iStateId, pRect, FALSE, &bmpSrc, &rcSrc);
360     if(FAILED(hr)) return hr;
361     hdcSrc = CreateCompatibleDC(hdc);
362     if(!hdcSrc) {
363         hr = HRESULT_FROM_WIN32(GetLastError());
364         DeleteObject(bmpSrc);
365         return hr;
366     }
367     oldSrc = SelectObject(hdcSrc, bmpSrc);
368
369     CopyRect(&rcDst, pRect);
370     
371     GetThemeBool(hTheme, iPartId, iStateId, TMT_TRANSPARENT, &transparent);
372     if(transparent) {
373         if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TRANSPARENTCOLOR, &transparentcolor))) {
374             /* If image is transparent, but no color was specified, get the color of the upper left corner */
375             transparentcolor = GetPixel(hdcSrc, 0, 0);
376         }
377     }
378
379     dstSize.x = rcDst.right-rcDst.left;
380     dstSize.y = rcDst.bottom-rcDst.top;
381     srcSize.x = rcSrc.right-rcSrc.left;
382     srcSize.y = rcSrc.bottom-rcSrc.top;
383
384     if(uniformsizing) {
385         /* Scale height and width equally */
386         int widthDiff = abs(srcSize.x-dstSize.x);
387         int heightDiff = abs(srcSize.y-dstSize.x);
388         if(widthDiff > heightDiff) {
389             dstSize.y -= widthDiff-heightDiff;
390             rcDst.bottom = rcDst.top + dstSize.y;
391         }
392         else if(heightDiff > widthDiff) {
393             dstSize.x -= heightDiff-widthDiff;
394             rcDst.right = rcDst.left + dstSize.x;
395         }
396     }
397
398     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_SIZINGTYPE, &sizingtype);
399     if(sizingtype == ST_TRUESIZE) {
400         int truesizestretchmark = 0;
401
402         if(dstSize.x < 0 || dstSize.y < 0) {
403             BOOL mirrorimage = TRUE;
404             GetThemeBool(hTheme, iPartId, iStateId, TMT_MIRRORIMAGE, &mirrorimage);
405             if(mirrorimage) {
406                 if(dstSize.x < 0) {
407                     rcDst.left += dstSize.x;
408                     rcDst.right += dstSize.x;
409                 }
410                 if(dstSize.y < 0) {
411                     rcDst.top += dstSize.y;
412                     rcDst.bottom += dstSize.y;
413                 }
414             }
415         }
416         /* Only stretch when target exceeds source by truesizestretchmark percent */
417         GetThemeInt(hTheme, iPartId, iStateId, TMT_TRUESIZESTRETCHMARK, &truesizestretchmark);
418         if(dstSize.x < 0 || dstSize.y < 0 ||
419            MulDiv(srcSize.x, 100, dstSize.x) > truesizestretchmark ||
420            MulDiv(srcSize.y, 100, dstSize.y) > truesizestretchmark) {
421             if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, dstSize.x, dstSize.y,
422                                    hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
423                                    transparent, transparentcolor))
424                 hr = HRESULT_FROM_WIN32(GetLastError());
425         }
426         else {
427             rcDst.left += (dstSize.x/2)-(srcSize.x/2);
428             rcDst.top  += (dstSize.y/2)-(srcSize.y/2);
429             rcDst.right = rcDst.left + srcSize.x;
430             rcDst.bottom = rcDst.top + srcSize.y;
431             if(!UXTHEME_Blt(hdc, rcDst.left, rcDst.top, srcSize.x, srcSize.y,
432                             hdcSrc, rcSrc.left, rcSrc.top,
433                             transparent, transparentcolor))
434                 hr = HRESULT_FROM_WIN32(GetLastError());
435         }
436     }
437     else {
438         HDC hdcDst = NULL;
439         HBITMAP bmpDst = NULL;
440         HGDIOBJ oldDst = NULL;
441         MARGINS sm;
442
443         dstSize.x = abs(dstSize.x);
444         dstSize.y = abs(dstSize.y);
445
446         GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_SIZINGMARGINS, NULL, &sm);
447
448         hdcDst = CreateCompatibleDC(hdc);
449         if(!hdcDst) {
450             hr = HRESULT_FROM_WIN32(GetLastError());
451             goto draw_error; 
452         }
453         bmpDst = CreateCompatibleBitmap(hdc, dstSize.x, dstSize.y);
454         if(!bmpDst) {
455             hr = HRESULT_FROM_WIN32(GetLastError());
456             goto draw_error; 
457         }
458         oldDst = SelectObject(hdcDst, bmpDst);
459
460         /* Upper left corner */
461         if(!BitBlt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
462                    hdcSrc, rcSrc.left, rcSrc.top, SRCCOPY)) {
463             hr = HRESULT_FROM_WIN32(GetLastError());
464             goto draw_error; 
465         }
466         /* Upper right corner */
467         if(!BitBlt(hdcDst, dstSize.x-sm.cxRightWidth, 0, sm.cxRightWidth, sm.cyTopHeight,
468                    hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top, SRCCOPY)) {
469             hr = HRESULT_FROM_WIN32(GetLastError());
470             goto draw_error; 
471         }
472         /* Lower left corner */
473         if(!BitBlt(hdcDst, 0, dstSize.y-sm.cyBottomHeight, sm.cxLeftWidth, sm.cyBottomHeight,
474                    hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight, SRCCOPY)) {
475             hr = HRESULT_FROM_WIN32(GetLastError());
476             goto draw_error; 
477         }
478         /* Lower right corner */
479         if(!BitBlt(hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight, sm.cxRightWidth, sm.cyBottomHeight,
480                    hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight, SRCCOPY)) {
481             hr = HRESULT_FROM_WIN32(GetLastError());
482             goto draw_error; 
483         }
484
485         if(sizingtype == ST_TILE) {
486             FIXME("Tile\n");
487             sizingtype = ST_STRETCH; /* Just use stretch for now */
488         }
489         if(sizingtype == ST_STRETCH) {
490             int destCenterWidth  = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
491             int srcCenterWidth   = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
492             int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
493             int srcCenterHeight  = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
494
495             if(destCenterWidth > 0) {
496                 /* Center top */
497                 if(!StretchBlt(hdcDst, sm.cxLeftWidth, 0, destCenterWidth, sm.cyTopHeight,
498                                hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top, srcCenterWidth, sm.cyTopHeight, SRCCOPY)) {
499                     hr = HRESULT_FROM_WIN32(GetLastError());
500                     goto draw_error; 
501                 }
502                 /* Center bottom */
503                 if(!StretchBlt(hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight, destCenterWidth, sm.cyBottomHeight,
504                                hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight, srcCenterWidth, sm.cyTopHeight, SRCCOPY)) {
505                     hr = HRESULT_FROM_WIN32(GetLastError());
506                     goto draw_error; 
507                 }
508             }
509             if(destCenterHeight > 0) {
510                 /* Left center */
511                 if(!StretchBlt(hdcDst, 0, sm.cyTopHeight, sm.cxLeftWidth, destCenterHeight,
512                            hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight, sm.cxLeftWidth, srcCenterHeight, SRCCOPY)) {
513                     hr = HRESULT_FROM_WIN32(GetLastError());
514                     goto draw_error; 
515                 }
516                 /* Right center */
517                 if(!StretchBlt(hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight, sm.cxRightWidth, destCenterHeight,
518                                hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight, sm.cxRightWidth, srcCenterHeight, SRCCOPY)) {
519                     hr = HRESULT_FROM_WIN32(GetLastError());
520                     goto draw_error; 
521                 }
522             }
523             if(destCenterHeight > 0 && destCenterWidth > 0) {
524                 BOOL borderonly = FALSE;
525                 GetThemeBool(hTheme, iPartId, iStateId, TMT_BORDERONLY, &borderonly);
526                 if(!borderonly) {
527                     /* Center */
528                     if(!StretchBlt(hdcDst, sm.cxLeftWidth, sm.cyTopHeight, destCenterWidth, destCenterHeight,
529                                    hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight, srcCenterWidth, srcCenterHeight, SRCCOPY)) {
530                         hr = HRESULT_FROM_WIN32(GetLastError());
531                         goto draw_error; 
532                     }
533                 }
534             }
535         }
536
537         if(!UXTHEME_Blt(hdc, rcDst.left, rcDst.top, dstSize.x, dstSize.y,
538                         hdcDst, 0, 0,
539                         transparent, transparentcolor))
540             hr = HRESULT_FROM_WIN32(GetLastError());
541
542 draw_error:
543         if(hdcDst) {
544             SelectObject(hdcDst, oldDst);
545             DeleteDC(hdcDst);
546         }
547         if(bmpDst) DeleteObject(bmpDst);
548     }
549     SelectObject(hdcSrc, oldSrc);
550     DeleteObject(bmpSrc);
551     DeleteDC(hdcSrc);
552     CopyRect(pRect, &rcDst);
553     return hr;
554 }
555
556 /***********************************************************************
557  *      UXTHEME_DrawBorderRectangle
558  *
559  * Draw the bounding rectangle for a borderfill background
560  */
561 HRESULT UXTHEME_DrawBorderRectangle(HTHEME hTheme, HDC hdc, int iPartId,
562                                     int iStateId, RECT *pRect,
563                                     const DTBGOPTS *pOptions)
564 {
565     HRESULT hr = S_OK;
566     HPEN hPen;
567     HGDIOBJ oldPen;
568     COLORREF bordercolor = RGB(0,0,0);
569     int bordersize = 1;
570
571     GetThemeInt(hTheme, iPartId, iStateId, TMT_BORDERSIZE, &bordersize);
572     if(bordersize > 0) {
573         POINT ptCorners[4];
574         ptCorners[0].x = pRect->left;
575         ptCorners[0].y = pRect->top;
576         ptCorners[1].x = pRect->right;
577         ptCorners[1].y = pRect->top;
578         ptCorners[2].x = pRect->right;
579         ptCorners[2].y = pRect->bottom;
580         ptCorners[3].x = pRect->left;
581         ptCorners[3].y = pRect->bottom;
582
583         InflateRect(pRect, -bordersize, -bordersize);
584         if(pOptions->dwFlags & DTBG_OMITBORDER)
585             return S_OK;
586         GetThemeColor(hTheme, iPartId, iStateId, TMT_BORDERCOLOR, &bordercolor);
587         hPen = CreatePen(PS_SOLID, bordersize, bordercolor);
588         if(!hPen)
589             return HRESULT_FROM_WIN32(GetLastError());
590         oldPen = SelectObject(hdc, hPen);
591
592         if(!Polyline(hdc, ptCorners, 4))
593             hr = HRESULT_FROM_WIN32(GetLastError());
594
595         SelectObject(hdc, oldPen);
596         DeleteObject(hPen);
597     }
598     return hr;
599 }
600
601 /***********************************************************************
602  *      UXTHEME_DrawBorderRectangle
603  *
604  * Fill a borderfill background rectangle
605  */
606 HRESULT UXTHEME_DrawBackgroundFill(HTHEME hTheme, HDC hdc, int iPartId,
607                                    int iStateId, RECT *pRect,
608                                    const DTBGOPTS *pOptions)
609 {
610     HRESULT hr = S_OK;
611     int filltype = FT_SOLID;
612
613     TRACE("(%d,%d,%ld)\n", iPartId, iStateId, pOptions->dwFlags);
614
615     if(pOptions->dwFlags & DTBG_OMITCONTENT)
616         return S_OK;
617
618     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_FILLTYPE, &filltype);
619
620     if(filltype == FT_SOLID) {
621         HBRUSH hBrush;
622         COLORREF fillcolor = RGB(255,255,255);
623
624         GetThemeColor(hTheme, iPartId, iStateId, TMT_FILLCOLOR, &fillcolor);
625         hBrush = CreateSolidBrush(fillcolor);
626         if(!FillRect(hdc, pRect, hBrush))
627             hr = HRESULT_FROM_WIN32(GetLastError());
628         DeleteObject(hBrush);
629     }
630     else if(filltype == FT_VERTGRADIENT || filltype == FT_HORZGRADIENT) {
631         /* FIXME: This only accounts for 2 gradient colors (out of 5) and ignores
632             the gradient ratios (no idea how those work)
633             Few themes use this, and the ones I've seen only use 2 colors with
634             a gradient ratio of 0 and 255 respectivly
635         */
636
637         COLORREF gradient1 = RGB(0,0,0);
638         COLORREF gradient2 = RGB(255,255,255);
639         TRIVERTEX vert[2];
640         GRADIENT_RECT gRect;
641
642         FIXME("Gradient implementation not complete\n");
643
644         GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR1, &gradient1);
645         GetThemeColor(hTheme, iPartId, iStateId, TMT_GRADIENTCOLOR2, &gradient2);
646
647         vert[0].x     = pRect->left;
648         vert[0].y     = pRect->top;
649         vert[0].Red   = GetRValue(gradient1) << 8;
650         vert[0].Green = GetGValue(gradient1) << 8;
651         vert[0].Blue  = GetBValue(gradient1) << 8;
652         vert[0].Alpha = 0x0000;
653
654         vert[1].x     = pRect->right;
655         vert[1].y     = pRect->bottom;
656         vert[1].Red   = GetRValue(gradient2) << 8;
657         vert[1].Green = GetGValue(gradient2) << 8;
658         vert[1].Blue  = GetBValue(gradient2) << 8;
659         vert[1].Alpha = 0x0000;
660
661         gRect.UpperLeft  = 0;
662         gRect.LowerRight = 1;
663         GradientFill(hdc,vert,2,&gRect,1,filltype==FT_HORZGRADIENT?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
664     }
665     else if(filltype == FT_RADIALGRADIENT) {
666         /* I've never seen this used in a theme */
667         FIXME("Radial gradient\n");
668     }
669     else if(filltype == FT_TILEIMAGE) {
670         /* I've never seen this used in a theme */
671         FIXME("Tile image\n");
672     }
673     return hr;
674 }
675
676 /***********************************************************************
677  *      UXTHEME_DrawImageBackground
678  *
679  * Draw an imagefile background
680  */
681 HRESULT UXTHEME_DrawBorderBackground(HTHEME hTheme, HDC hdc, int iPartId,
682                                      int iStateId, const RECT *pRect,
683                                      const DTBGOPTS *pOptions)
684 {
685     HRESULT hr;
686     RECT rt;
687
688     CopyRect(&rt, pRect);
689
690     hr = UXTHEME_DrawBorderRectangle(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
691     if(FAILED(hr))
692         return hr;
693     return UXTHEME_DrawBackgroundFill(hTheme, hdc, iPartId, iStateId, &rt, pOptions);
694 }
695
696 /***********************************************************************
697  *      DrawThemeBackgroundEx                               (UXTHEME.@)
698  */
699 HRESULT WINAPI DrawThemeBackgroundEx(HTHEME hTheme, HDC hdc, int iPartId,
700                                      int iStateId, const RECT *pRect,
701                                      const DTBGOPTS *pOptions)
702 {
703     HRESULT hr;
704     const DTBGOPTS defaultOpts = {sizeof(DTBGOPTS), 0, {0,0,0,0}};
705     const DTBGOPTS *opts;
706     HRGN clip = NULL;
707     int hasClip = -1;
708     int bgtype = BT_BORDERFILL;
709     RECT rt;
710
711     TRACE("(%p,%p,%d,%d,%ld,%ld)\n", hTheme, hdc, iPartId, iStateId,pRect->left,pRect->top);
712     if(!hTheme)
713         return E_HANDLE;
714
715     /* Ensure we have a DTBGOPTS structure available, simplifies some of the code */
716     opts = pOptions;
717     if(!opts) opts = &defaultOpts;
718
719     if(opts->dwFlags & DTBG_CLIPRECT) {
720         clip = CreateRectRgn(0,0,1,1);
721         hasClip = GetClipRgn(hdc, clip);
722         if(hasClip == -1)
723             TRACE("Failed to get original clipping region\n");
724         else
725             IntersectClipRect(hdc, opts->rcClip.left, opts->rcClip.top, opts->rcClip.right, opts->rcClip.bottom);
726     }
727     CopyRect(&rt, pRect);
728
729     GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_BGTYPE, &bgtype);
730     if(bgtype == BT_IMAGEFILE)
731         hr = UXTHEME_DrawImageBackground(hTheme, hdc, iPartId, iStateId, &rt, opts);
732     else if(bgtype == BT_BORDERFILL)
733         hr = UXTHEME_DrawBorderBackground(hTheme, hdc, iPartId, iStateId, pRect, opts);
734     else {
735         FIXME("Unknown background type\n");
736         /* This should never happen, and hence I don't know what to return */
737         hr = E_FAIL;
738     }
739     if(SUCCEEDED(hr))
740         hr = UXTHEME_DrawGlyph(hTheme, hdc, iPartId, iStateId, &rt, opts);
741     if(opts->dwFlags & DTBG_CLIPRECT) {
742         if(hasClip == 0)
743             SelectClipRgn(hdc, NULL);
744         else if(hasClip == 1)
745             SelectClipRgn(hdc, clip);
746         DeleteObject(clip);
747     }
748     return hr;
749 }
750
751 /***********************************************************************
752  *      DrawThemeEdge                                       (UXTHEME.@)
753  */
754 HRESULT WINAPI DrawThemeEdge(HTHEME hTheme, HDC hdc, int iPartId,
755                              int iStateId, const RECT *pDestRect, UINT uEdge,
756                              UINT uFlags, RECT *pContentRect)
757 {
758     FIXME("%d %d 0x%08x 0x%08x: stub\n", iPartId, iStateId, uEdge, uFlags);
759     if(!hTheme)
760         return E_HANDLE;
761     return ERROR_CALL_NOT_IMPLEMENTED;
762 }
763
764 /***********************************************************************
765  *      DrawThemeIcon                                       (UXTHEME.@)
766  */
767 HRESULT WINAPI DrawThemeIcon(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
768                              const RECT *pRect, HIMAGELIST himl, int iImageIndex)
769 {
770     FIXME("%d %d: stub\n", iPartId, iStateId);
771     if(!hTheme)
772         return E_HANDLE;
773     return ERROR_CALL_NOT_IMPLEMENTED;
774 }
775
776 /***********************************************************************
777  *      DrawThemeText                                       (UXTHEME.@)
778  */
779 HRESULT WINAPI DrawThemeText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
780                              LPCWSTR pszText, int iCharCount, DWORD dwTextFlags,
781                              DWORD dwTextFlags2, const RECT *pRect)
782 {
783     HRESULT hr;
784     HFONT hFont = NULL;
785     HGDIOBJ oldFont = NULL;
786     LOGFONTW logfont;
787     COLORREF textColor;
788     COLORREF oldTextColor;
789     int oldBkMode;
790     RECT rt;
791     
792     TRACE("%d %d: stub\n", iPartId, iStateId);
793     if(!hTheme)
794         return E_HANDLE;
795     
796     hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
797     if(SUCCEEDED(hr)) {
798         hFont = CreateFontIndirectW(&logfont);
799         if(!hFont)
800             TRACE("Failed to create font\n");
801     }
802     CopyRect(&rt, pRect);
803     if(hFont)
804         oldFont = SelectObject(hdc, hFont);
805         
806     if(dwTextFlags2 & DTT_GRAYED)
807         textColor = GetSysColor(COLOR_GRAYTEXT);
808     else {
809         if(FAILED(GetThemeColor(hTheme, iPartId, iStateId, TMT_TEXTCOLOR, &textColor)))
810             textColor = GetTextColor(hdc);
811     }
812     oldTextColor = SetTextColor(hdc, textColor);
813     oldBkMode = SetBkMode(hdc, TRANSPARENT);
814     DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags);
815     SetBkMode(hdc, oldBkMode);
816     SetTextColor(hdc, oldTextColor);
817
818     if(hFont) {
819         SelectObject(hdc, oldFont);
820         DeleteObject(hFont);
821     }
822     return S_OK;
823 }
824
825 /***********************************************************************
826  *      GetThemeBackgroundContentRect                       (UXTHEME.@)
827  */
828 HRESULT WINAPI GetThemeBackgroundContentRect(HTHEME hTheme, HDC hdc, int iPartId,
829                                              int iStateId,
830                                              const RECT *pBoundingRect,
831                                              RECT *pContentRect)
832 {
833     MARGINS margin;
834     HRESULT hr;
835
836     TRACE("(%d,%d)\n", iPartId, iStateId);
837     if(!hTheme)
838         return E_HANDLE;
839
840     hr = GetThemeMargins(hTheme, hdc, iPartId, iStateId, TMT_CONTENTMARGINS, NULL, &margin);
841     if(FAILED(hr)) {
842         TRACE("Margins not found\n");
843         return hr;
844     }
845     pContentRect->left = pBoundingRect->left + margin.cxLeftWidth;
846     pContentRect->top  = pBoundingRect->top + margin.cyTopHeight;
847     pContentRect->right = pBoundingRect->right - margin.cxRightWidth;
848     pContentRect->bottom = pBoundingRect->bottom - margin.cyBottomHeight;
849
850     TRACE("left:%ld,top:%ld,right:%ld,bottom:%ld\n", pContentRect->left, pContentRect->top, pContentRect->right, pContentRect->bottom);
851
852     return S_OK;
853 }
854
855 /***********************************************************************
856  *      GetThemeBackgroundExtent                            (UXTHEME.@)
857  */
858 HRESULT WINAPI GetThemeBackgroundExtent(HTHEME hTheme, HDC hdc, int iPartId,
859                                         int iStateId, const RECT *pContentRect,
860                                         RECT *pExtentRect)
861 {
862     FIXME("%d %d: stub\n", iPartId, iStateId);
863     if(!hTheme)
864         return E_HANDLE;
865     return ERROR_CALL_NOT_IMPLEMENTED;
866 }
867
868 /***********************************************************************
869  *      GetThemeBackgroundRegion                            (UXTHEME.@)
870  */
871 HRESULT WINAPI GetThemeBackgroundRegion(HTHEME hTheme, HDC hdc, int iPartId,
872                                         int iStateId, const RECT *pRect,
873                                         HRGN *pRegion)
874 {
875     FIXME("%d %d: stub\n", iPartId, iStateId);
876     if(!hTheme)
877         return E_HANDLE;
878     return ERROR_CALL_NOT_IMPLEMENTED;
879 }
880
881 /***********************************************************************
882  *      GetThemePartSize                                    (UXTHEME.@)
883  */
884 HRESULT WINAPI GetThemePartSize(HTHEME hTheme, HDC hdc, int iPartId,
885                                 int iStateId, RECT *prc, THEMESIZE eSize,
886                                 SIZE *psz)
887 {
888     FIXME("%d %d %d: stub\n", iPartId, iStateId, eSize);
889     if(!hTheme)
890         return E_HANDLE;
891     return ERROR_CALL_NOT_IMPLEMENTED;
892 }
893
894
895 /***********************************************************************
896  *      GetThemeTextExtent                                  (UXTHEME.@)
897  */
898 HRESULT WINAPI GetThemeTextExtent(HTHEME hTheme, HDC hdc, int iPartId,
899                                   int iStateId, LPCWSTR pszText, int iCharCount,
900                                   DWORD dwTextFlags, const RECT *pBoundingRect,
901                                   RECT *pExtentRect)
902 {
903     HRESULT hr;
904     HFONT hFont = NULL;
905     HGDIOBJ oldFont = NULL;
906     LOGFONTW logfont;
907     RECT rt = {0,0,0xFFFF,0xFFFF};
908     
909     TRACE("%d %d: stub\n", iPartId, iStateId);
910     if(!hTheme)
911         return E_HANDLE;
912
913     if(pBoundingRect)
914         CopyRect(&rt, pBoundingRect);
915             
916     hr = GetThemeFont(hTheme, hdc, iPartId, iStateId, TMT_FONT, &logfont);
917     if(SUCCEEDED(hr)) {
918         hFont = CreateFontIndirectW(&logfont);
919         if(!hFont)
920             TRACE("Failed to create font\n");
921     }
922     if(hFont)
923         oldFont = SelectObject(hdc, hFont);
924         
925     DrawTextW(hdc, pszText, iCharCount, &rt, dwTextFlags|DT_CALCRECT);
926     CopyRect(pExtentRect, &rt);
927
928     if(hFont) {
929         SelectObject(hdc, oldFont);
930         DeleteObject(hFont);
931     }
932     return S_OK;
933
934     
935     FIXME("%d %d: stub\n", iPartId, iStateId);
936     if(!hTheme)
937         return E_HANDLE;
938     return ERROR_CALL_NOT_IMPLEMENTED;
939 }
940
941 /***********************************************************************
942  *      GetThemeTextMetrics                                 (UXTHEME.@)
943  */
944 HRESULT WINAPI GetThemeTextMetrics(HTHEME hTheme, HDC hdc, int iPartId,
945                                    int iStateId, TEXTMETRICW *ptm)
946 {
947     FIXME("%d %d: stub\n", iPartId, iStateId);
948     if(!hTheme)
949         return E_HANDLE;
950     return ERROR_CALL_NOT_IMPLEMENTED;
951 }
952
953 /***********************************************************************
954  *      IsThemeBackgroundPartiallyTransparent               (UXTHEME.@)
955  */
956 BOOL WINAPI IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId,
957                                                   int iStateId)
958 {
959     BOOL transparent = FALSE;
960     TRACE("(%d,%d)\n", iPartId, iStateId);
961     GetThemeBool(hTheme, iPartId, iStateId, TMT_TRANSPARENT, &transparent);
962     return transparent;
963 }