|
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., 51 Franklin Street, Fifth Floor, |
|
19 * Boston, MA 02110-1301, 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 // Combobox constants |
|
55 #define CP_DROPDOWNBUTTON 1 |
|
56 |
|
57 typedef HANDLE (WINAPI*openThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList); |
|
58 typedef HRESULT (WINAPI*closeThemeDataPtr)(HANDLE hTheme); |
|
59 typedef HRESULT (WINAPI*drawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId, |
|
60 int iStateId, const RECT *pRect, |
|
61 const RECT* pClipRect); |
|
62 typedef HRESULT (WINAPI*drawThemeEdgePtr)(HANDLE hTheme, HDC hdc, int iPartId, |
|
63 int iStateId, const RECT *pRect, |
|
64 unsigned uEdge, unsigned uFlags, |
|
65 const RECT* pClipRect); |
|
66 typedef HRESULT (WINAPI*getThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId, |
|
67 int iStateId, const RECT* pRect, |
|
68 RECT* pContentRect); |
|
69 typedef HRESULT (WINAPI*getThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId, |
|
70 int iStateId, RECT* prc, int ts, |
|
71 SIZE* psz); |
|
72 typedef HRESULT (WINAPI*getThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont); |
|
73 typedef HRESULT (WINAPI*getThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId, |
|
74 int iStateId, int iPropId, OUT COLORREF* pFont); |
|
75 |
|
76 static openThemeDataPtr openTheme = 0; |
|
77 static closeThemeDataPtr closeTheme = 0; |
|
78 static drawThemeBackgroundPtr drawThemeBG = 0; |
|
79 static drawThemeEdgePtr drawThemeEdge = 0; |
|
80 static getThemeContentRectPtr getThemeContentRect = 0; |
|
81 static getThemePartSizePtr getThemePartSize = 0; |
|
82 static getThemeSysFontPtr getThemeSysFont = 0; |
|
83 static getThemeColorPtr getThemeColor = 0; |
|
84 |
|
85 namespace WebCore { |
|
86 |
|
87 RenderTheme* theme() |
|
88 { |
|
89 static RenderThemeWin winTheme; |
|
90 return &winTheme; |
|
91 } |
|
92 |
|
93 RenderThemeWin::RenderThemeWin() |
|
94 :m_themeDLL(0), m_buttonTheme(0), m_textFieldTheme(0), m_menuListTheme(0) |
|
95 { |
|
96 m_themeDLL = ::LoadLibrary(L"uxtheme.dll"); |
|
97 if (m_themeDLL) { |
|
98 openTheme = (openThemeDataPtr)GetProcAddress(m_themeDLL, "OpenThemeData"); |
|
99 closeTheme = (closeThemeDataPtr)GetProcAddress(m_themeDLL, "CloseThemeData"); |
|
100 drawThemeBG = (drawThemeBackgroundPtr)GetProcAddress(m_themeDLL, "DrawThemeBackground"); |
|
101 drawThemeEdge = (drawThemeEdgePtr)GetProcAddress(m_themeDLL, "DrawThemeEdge"); |
|
102 getThemeContentRect = (getThemeContentRectPtr)GetProcAddress(m_themeDLL, "GetThemeBackgroundContentRect"); |
|
103 getThemePartSize = (getThemePartSizePtr)GetProcAddress(m_themeDLL, "GetThemePartSize"); |
|
104 getThemeSysFont = (getThemeSysFontPtr)GetProcAddress(m_themeDLL, "GetThemeSysFont"); |
|
105 getThemeColor = (getThemeColorPtr)GetProcAddress(m_themeDLL, "GetThemeColor"); |
|
106 } |
|
107 } |
|
108 |
|
109 RenderThemeWin::~RenderThemeWin() |
|
110 { |
|
111 if (!m_themeDLL) |
|
112 return; |
|
113 |
|
114 close(); |
|
115 |
|
116 ::FreeLibrary(m_themeDLL); |
|
117 } |
|
118 |
|
119 void RenderThemeWin::close() |
|
120 { |
|
121 // This method will need to be called when the OS theme changes to flush our cached themes. |
|
122 if (m_buttonTheme) |
|
123 closeTheme(m_buttonTheme); |
|
124 if (m_textFieldTheme) |
|
125 closeTheme(m_textFieldTheme); |
|
126 if (m_menuListTheme) |
|
127 closeTheme(m_menuListTheme); |
|
128 m_buttonTheme = m_textFieldTheme = m_menuListTheme = 0; |
|
129 } |
|
130 |
|
131 Color RenderThemeWin::platformActiveSelectionBackgroundColor() const |
|
132 { |
|
133 COLORREF color = GetSysColor(COLOR_HIGHLIGHT); |
|
134 return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); |
|
135 } |
|
136 |
|
137 Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const |
|
138 { |
|
139 COLORREF color = GetSysColor(COLOR_GRAYTEXT); |
|
140 return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); |
|
141 } |
|
142 |
|
143 Color RenderThemeWin::platformActiveSelectionForegroundColor() const |
|
144 { |
|
145 COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); |
|
146 return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); |
|
147 } |
|
148 |
|
149 Color RenderThemeWin::platformInactiveSelectionForegroundColor() const |
|
150 { |
|
151 return Color::white; |
|
152 } |
|
153 |
|
154 bool RenderThemeWin::supportsFocus(EAppearance appearance) |
|
155 { |
|
156 switch (appearance) { |
|
157 case PushButtonAppearance: |
|
158 case ButtonAppearance: |
|
159 case TextFieldAppearance: |
|
160 case TextAreaAppearance: |
|
161 return true; |
|
162 default: |
|
163 return false; |
|
164 } |
|
165 |
|
166 return false; |
|
167 } |
|
168 |
|
169 unsigned RenderThemeWin::determineState(RenderObject* o) |
|
170 { |
|
171 unsigned result = TS_NORMAL; |
|
172 if (!isEnabled(o)) |
|
173 result = TS_DISABLED; |
|
174 else if (isReadOnlyControl(o)) |
|
175 result = TFS_READONLY; // Readonly is supported on textfields. |
|
176 else if (supportsFocus(o->style()->appearance()) && isFocused(o)) |
|
177 result = TS_FOCUSED; |
|
178 else if (isPressed(o)) // Active overrides hover. |
|
179 result = TS_ACTIVE; |
|
180 else if (isHovered(o)) |
|
181 result = TS_HOVER; |
|
182 if (isChecked(o)) |
|
183 result += 4; // 4 unchecked states, 4 checked states. |
|
184 return result; |
|
185 } |
|
186 |
|
187 unsigned RenderThemeWin::determineClassicState(RenderObject* o) |
|
188 { |
|
189 unsigned result = 0; |
|
190 if (!isEnabled(o) || isReadOnlyControl(o)) |
|
191 result = DFCS_INACTIVE; |
|
192 else if (isPressed(o)) // Active supersedes hover |
|
193 result = DFCS_PUSHED; |
|
194 else if (isHovered(o)) |
|
195 result = DFCS_HOT; |
|
196 if (isChecked(o)) |
|
197 result |= DFCS_CHECKED; |
|
198 return result; |
|
199 } |
|
200 |
|
201 ThemeData RenderThemeWin::getThemeData(RenderObject* o) |
|
202 { |
|
203 ThemeData result; |
|
204 switch (o->style()->appearance()) { |
|
205 case PushButtonAppearance: |
|
206 case ButtonAppearance: |
|
207 result.m_part = BP_BUTTON; |
|
208 result.m_classicState = DFCS_BUTTONPUSH; |
|
209 break; |
|
210 case CheckboxAppearance: |
|
211 result.m_part = BP_CHECKBOX; |
|
212 result.m_classicState = DFCS_BUTTONCHECK; |
|
213 break; |
|
214 case RadioAppearance: |
|
215 result.m_part = BP_RADIO; |
|
216 result.m_classicState = DFCS_BUTTONRADIO; |
|
217 break; |
|
218 case ListboxAppearance: |
|
219 case MenulistAppearance: |
|
220 case TextFieldAppearance: |
|
221 case TextAreaAppearance: |
|
222 result.m_part = TFP_TEXTFIELD; |
|
223 break; |
|
224 } |
|
225 |
|
226 result.m_state = determineState(o); |
|
227 result.m_classicState |= determineClassicState(o); |
|
228 |
|
229 return result; |
|
230 } |
|
231 |
|
232 // May need to add stuff to these later, so keep the graphics context retrieval/release in some helpers. |
|
233 static HDC prepareForDrawing(GraphicsContext* g) |
|
234 { |
|
235 return g->getWindowsContext(); |
|
236 } |
|
237 |
|
238 static void doneDrawing(GraphicsContext* g, HDC hdc) |
|
239 { |
|
240 g->releaseWindowsContext(hdc); |
|
241 } |
|
242 |
|
243 bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) |
|
244 { |
|
245 // Get the correct theme data for a button |
|
246 ThemeData themeData = getThemeData(o); |
|
247 |
|
248 // Now paint the button. |
|
249 HDC hdc = prepareForDrawing(i.context); |
|
250 RECT widgetRect = r; |
|
251 if (m_themeDLL && !m_buttonTheme) |
|
252 m_buttonTheme = openTheme(0, L"Button"); |
|
253 if (m_buttonTheme && drawThemeBG) { |
|
254 drawThemeBG(m_buttonTheme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); |
|
255 } else { |
|
256 if ((themeData.m_part == BP_BUTTON) && isFocused(o)) { |
|
257 // Draw black focus rect around button outer edge |
|
258 HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW); |
|
259 if (brush) { |
|
260 FrameRect(hdc, &widgetRect, brush); |
|
261 InflateRect(&widgetRect, -1, -1); |
|
262 } |
|
263 } |
|
264 DrawFrameControl(hdc, &widgetRect, DFC_BUTTON, themeData.m_classicState); |
|
265 if ((themeData.m_part != BP_BUTTON) && isFocused(o)) { |
|
266 DrawFocusRect(hdc, &widgetRect); |
|
267 } |
|
268 } |
|
269 doneDrawing(i.context, hdc); |
|
270 |
|
271 return false; |
|
272 } |
|
273 |
|
274 void RenderThemeWin::setCheckboxSize(RenderStyle* style) const |
|
275 { |
|
276 // If the width and height are both specified, then we have nothing to do. |
|
277 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
|
278 return; |
|
279 |
|
280 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. |
|
281 // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for |
|
282 // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's |
|
283 // metrics. |
|
284 if (style->width().isIntrinsicOrAuto()) |
|
285 style->setWidth(Length(13, Fixed)); |
|
286 if (style->height().isAuto()) |
|
287 style->setHeight(Length(13, Fixed)); |
|
288 } |
|
289 |
|
290 bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) |
|
291 { |
|
292 // Get the correct theme data for a textfield |
|
293 ThemeData themeData = getThemeData(o); |
|
294 |
|
295 // Now paint the text field. |
|
296 HDC hdc = prepareForDrawing(i.context); |
|
297 RECT widgetRect = r; |
|
298 if (m_themeDLL && !m_textFieldTheme) |
|
299 m_textFieldTheme = openTheme(0, L"Edit"); |
|
300 if (m_textFieldTheme && drawThemeBG) { |
|
301 drawThemeBG(m_textFieldTheme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); |
|
302 } else { |
|
303 DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); |
|
304 FillRect(hdc, &widgetRect, reinterpret_cast<HBRUSH>(((themeData.m_classicState & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW) + 1)); |
|
305 } |
|
306 doneDrawing(i.context, hdc); |
|
307 |
|
308 return false; |
|
309 } |
|
310 |
|
311 void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
312 { |
|
313 // Height is locked to auto. |
|
314 style->setHeight(Length(Auto)); |
|
315 |
|
316 // White-space is locked to pre |
|
317 style->setWhiteSpace(PRE); |
|
318 |
|
319 // Add in the padding that we'd like to use. |
|
320 const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL); |
|
321 style->setPaddingLeft(Length(2, Fixed)); |
|
322 style->setPaddingRight(Length(buttonWidth + 2, Fixed)); |
|
323 style->setPaddingTop(Length(1, Fixed)); |
|
324 style->setPaddingBottom(Length(1, Fixed)); |
|
325 } |
|
326 |
|
327 bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) |
|
328 { |
|
329 // FIXME: All these inflate() calls are bogus, causing painting problems, |
|
330 // as well as sizing wackiness in Classic mode |
|
331 IntRect editRect(r); |
|
332 paintTextField(o, i, editRect); |
|
333 |
|
334 const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL); |
|
335 IntRect buttonRect(r.right() - buttonWidth - 1, r.y(), buttonWidth, r.height()); |
|
336 buttonRect.inflateY(-1); |
|
337 paintMenuListButton(o, i, buttonRect); |
|
338 |
|
339 return false; |
|
340 } |
|
341 |
|
342 bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) |
|
343 { |
|
344 HDC hdc = prepareForDrawing(i.context); |
|
345 RECT widgetRect = r; |
|
346 if (m_themeDLL && !m_menuListTheme) |
|
347 m_menuListTheme = openTheme(0, L"Combobox"); |
|
348 if (m_menuListTheme && drawThemeBG) |
|
349 drawThemeBG(m_menuListTheme, hdc, CP_DROPDOWNBUTTON, determineState(o), &widgetRect, NULL); |
|
350 else |
|
351 DrawFrameControl(hdc, &widgetRect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | determineClassicState(o)); |
|
352 doneDrawing(i.context, hdc); |
|
353 |
|
354 return false; |
|
355 } |
|
356 |
|
357 } |