Reviewed by Adele.
[WebKit-https.git] / WebCore / rendering / RenderThemeWin.cpp
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2006 Apple Computer, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "RenderThemeWin.h"
25
26 #include <cairo-win32.h>
27 #include "Document.h"
28 #include "GraphicsContext.h"
29
30 /* 
31  * The following constants are used to determine how a widget is drawn using
32  * Windows' Theme API. For more information on theme parts and states see
33  * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
34  */
35 #define THEME_COLOR 204
36 #define THEME_FONT  210
37
38 // Generic state constants
39 #define TS_NORMAL    1
40 #define TS_HOVER     2
41 #define TS_ACTIVE    3
42 #define TS_DISABLED  4
43 #define TS_FOCUSED   5
44
45 // Button constants
46 #define BP_BUTTON    1
47 #define BP_RADIO     2
48 #define BP_CHECKBOX  3
49
50 // Textfield constants
51 #define TFP_TEXTFIELD 1
52 #define TFS_READONLY  6
53
54 typedef HANDLE (WINAPI*openThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
55 typedef HRESULT (WINAPI*closeThemeDataPtr)(HANDLE hTheme);
56 typedef HRESULT (WINAPI*drawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId, 
57                                           int iStateId, const RECT *pRect,
58                                           const RECT* pClipRect);
59 typedef HRESULT (WINAPI*drawThemeEdgePtr)(HANDLE hTheme, HDC hdc, int iPartId, 
60                                           int iStateId, const RECT *pRect,
61                                           unsigned uEdge, unsigned uFlags,
62                                           const RECT* pClipRect);
63 typedef HRESULT (WINAPI*getThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
64                                           int iStateId, const RECT* pRect,
65                                           RECT* pContentRect);
66 typedef HRESULT (WINAPI*getThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
67                                        int iStateId, RECT* prc, int ts,
68                                        SIZE* psz);
69 typedef HRESULT (WINAPI*getThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont);
70 typedef HRESULT (WINAPI*getThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId,
71                                    int iStateId, int iPropId, OUT COLORREF* pFont);
72
73 static openThemeDataPtr openTheme = 0;
74 static closeThemeDataPtr closeTheme = 0;
75 static drawThemeBackgroundPtr drawThemeBG = 0;
76 static drawThemeEdgePtr drawThemeEdge = 0;
77 static getThemeContentRectPtr getThemeContentRect = 0;
78 static getThemePartSizePtr getThemePartSize = 0;
79 static getThemeSysFontPtr getThemeSysFont = 0;
80 static getThemeColorPtr getThemeColor = 0;
81
82 namespace WebCore {
83
84 RenderTheme* theme()
85 {
86     static RenderThemeWin winTheme;
87     return &winTheme;
88 }
89
90 RenderThemeWin::RenderThemeWin()
91 :m_themeDLL(0), m_buttonTheme(0), m_textFieldTheme(0)
92 {
93     m_themeDLL = ::LoadLibrary(L"uxtheme.dll");
94     if (m_themeDLL) {
95         openTheme = (openThemeDataPtr)GetProcAddress(m_themeDLL, "OpenThemeData");
96         closeTheme = (closeThemeDataPtr)GetProcAddress(m_themeDLL, "CloseThemeData");
97         drawThemeBG = (drawThemeBackgroundPtr)GetProcAddress(m_themeDLL, "DrawThemeBackground");
98         drawThemeEdge = (drawThemeEdgePtr)GetProcAddress(m_themeDLL, "DrawThemeEdge");
99         getThemeContentRect = (getThemeContentRectPtr)GetProcAddress(m_themeDLL, "GetThemeBackgroundContentRect");
100         getThemePartSize = (getThemePartSizePtr)GetProcAddress(m_themeDLL, "GetThemePartSize");
101         getThemeSysFont = (getThemeSysFontPtr)GetProcAddress(m_themeDLL, "GetThemeSysFont");
102         getThemeColor = (getThemeColorPtr)GetProcAddress(m_themeDLL, "GetThemeColor");
103     }
104 }
105
106 RenderThemeWin::~RenderThemeWin()
107 {
108     if (!m_themeDLL)
109         return;
110
111     close();
112
113     ::FreeLibrary(m_themeDLL);
114 }
115
116 void RenderThemeWin::close()
117 {
118     // This method will need to be called when the OS theme changes to flush our cached themes.
119     if (m_buttonTheme)
120         closeTheme(m_buttonTheme);
121     if (m_textFieldTheme)
122         closeTheme(m_textFieldTheme);
123     m_buttonTheme = m_textFieldTheme = 0;
124 }
125
126 Color RenderThemeWin::platformActiveSelectionBackgroundColor() const
127 {
128     COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
129     return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255);
130 }
131
132 Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const
133 {
134     COLORREF color = GetSysColor(COLOR_GRAYTEXT);
135     return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255);
136 }
137
138 Color RenderThemeWin::platformActiveSelectionForegroundColor() const
139 {
140     COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
141     return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255);
142 }
143
144 Color RenderThemeWin::platformInactiveSelectionForegroundColor() const
145 {
146     return Color::white;
147 }
148
149 bool RenderThemeWin::supportsFocus(EAppearance appearance)
150 {
151     switch (appearance) {
152         case PushButtonAppearance:
153         case ButtonAppearance:
154         case TextFieldAppearance:
155         case TextAreaAppearance:
156             return true;
157         default:
158             return false;
159     }
160
161     return false;
162 }
163
164 unsigned RenderThemeWin::determineState(RenderObject* o)
165 {
166     unsigned result = TS_NORMAL;
167     if (!isEnabled(o))
168         result = TS_DISABLED;
169     else if (isReadOnlyControl(o))
170         result = TFS_READONLY; // Readonly is supported on textfields.
171     else if (supportsFocus(o->style()->appearance()) && isFocused(o))
172         result = TS_FOCUSED;
173     else if (isHovered(o))
174         result = TS_HOVER;
175     else if (isPressed(o))
176         result = TS_ACTIVE;
177     if (isChecked(o))
178         result += 4; // 4 unchecked states, 4 checked states.
179     return result;
180 }
181
182 ThemeData RenderThemeWin::getThemeData(RenderObject* o)
183 {
184     ThemeData result;
185     switch (o->style()->appearance()) {
186         case PushButtonAppearance:
187         case ButtonAppearance:
188             result.m_part = BP_BUTTON;
189             result.m_state = determineState(o);
190             break;
191         case CheckboxAppearance:
192             result.m_part = BP_CHECKBOX;
193             result.m_state = determineState(o);
194             break;
195         case RadioAppearance:
196             result.m_part = BP_RADIO;
197             result.m_state = determineState(o);
198             break;
199         case TextFieldAppearance:
200         case TextAreaAppearance:
201             result.m_part = TFP_TEXTFIELD;
202             result.m_state = determineState(o);
203             break;
204     }
205
206     return result;
207 }
208
209 void RenderThemeWin::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
210 {
211 }
212
213 // May need to add stuff to these later, so keep the graphics context retrieval/release in some helpers.
214 static HDC prepareForDrawing(GraphicsContext* g)
215 {
216     return g->getWindowsContext();
217 }
218  
219 static void doneDrawing(GraphicsContext* g)
220 {
221     g->releaseWindowsContext();
222 }
223
224 bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
225 {
226     // FIXME: Need to fall back to painting a Win2k "Classic" look.  We will hit this situation if
227     // a Windows XP user has turned on the Win2k "Classic" appearance or is running Win2k.
228     if (!m_themeDLL)
229         return true;
230
231     if (!m_buttonTheme)
232         m_buttonTheme = openTheme(0, L"Button");
233
234     if (!m_buttonTheme || !drawThemeBG)
235         return true;
236
237     // Get the correct theme data for a button
238     ThemeData themeData = getThemeData(o);
239     
240     // Now paint the button.
241     HDC hdc = prepareForDrawing(i.p);  
242     RECT widgetRect = r;
243     drawThemeBG(m_buttonTheme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL);
244     doneDrawing(i.p);
245
246     return false;
247 }
248
249 void RenderThemeWin::setCheckboxSize(RenderStyle* style) const
250 {
251     // If the width and height are both specified, then we have nothing to do.
252     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
253         return;
254
255     // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
256     // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
257     // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
258     // metrics.
259     if (style->width().isIntrinsicOrAuto())
260         style->setWidth(Length(13, Fixed));
261     if (style->height().isAuto())
262         style->setHeight(Length(13, Fixed));
263 }
264
265 void RenderThemeWin::setRadioSize(RenderStyle* style) const
266 {
267     // This is the same as checkboxes.
268     setCheckboxSize(style);
269 }
270
271 void RenderThemeWin::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
272 {
273 }
274
275 bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
276 {
277     // FIXME: Need to fall back to painting a Win2k "Classic" look.  We will hit this situation if
278     // a Windows XP user has turned on the Win2k "Classic" appearance or is running Win2k.
279     if (!m_themeDLL)
280         return true;
281
282     if (!m_textFieldTheme)
283         m_textFieldTheme = openTheme(0, L"Edit");
284
285     if (!m_textFieldTheme || !drawThemeBG)
286         return true;
287
288     // Get the correct theme data for a button
289     ThemeData themeData = getThemeData(o);
290     
291     // Now paint the text field.
292     HDC hdc = prepareForDrawing(i.p);
293     RECT widgetRect = r;
294     drawThemeBG(m_textFieldTheme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL);
295     doneDrawing(i.p);
296
297     return false;
298 }
299
300 void RenderThemeWin::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
301 {
302 }
303
304 bool RenderThemeWin::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
305 {
306     return paintTextField(o, i, r);
307 }
308
309 }