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