1 /* 2 * Copyright (C) 2006, 2007 Apple Inc. 3 * Copyright (C) 2009 Kenneth Rohde Christiansen 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 * Boston, MA 02111-1307, USA. 19 * 20 */ 21 22 #include "config.h" 23 #include "RenderThemeWin.h" 24 25 #include "CSSValueKeywords.h" 26 #include "Element.h" 27 #include "Frame.h" 28 #include "GraphicsContext.h" 29 #include "LocalWindowsContext.h" 30 #include "PaintInfo.h" 31 #include "RenderSlider.h" 32 #include "Settings.h" 33 #include "SoftLinking.h" 34 #include "SystemInfo.h" 35 #include "UserAgentStyleSheets.h" 36 37 #if ENABLE(VIDEO) 38 #include "RenderMediaControls.h" 39 #endif 40 41 #include <tchar.h> 42 43 /* 44 * The following constants are used to determine how a widget is drawn using 45 * Windows' Theme API. For more information on theme parts and states see 46 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp 47 */ 48 49 // Generic state constants 50 #define TS_NORMAL 1 51 #define TS_HOVER 2 52 #define TS_ACTIVE 3 53 #define TS_DISABLED 4 54 #define TS_FOCUSED 5 55 56 // Button constants 57 #define BP_BUTTON 1 58 #define BP_RADIO 2 59 #define BP_CHECKBOX 3 60 61 // Textfield constants 62 #define TFP_TEXTFIELD 1 63 #define EP_EDITBORDER_NOSCROLL 6 64 #define TFS_READONLY 6 65 66 // ComboBox constants (from vsstyle.h) 67 #define CP_DROPDOWNBUTTON 1 68 #define CP_BORDER 4 69 #define CP_READONLY 5 70 #define CP_DROPDOWNBUTTONRIGHT 6 71 72 // TrackBar (slider) parts 73 #define TKP_TRACK 1 74 #define TKP_TRACKVERT 2 75 76 // TrackBar (slider) thumb parts 77 #define TKP_THUMBBOTTOM 4 78 #define TKP_THUMBTOP 5 79 #define TKP_THUMBLEFT 7 80 #define TKP_THUMBRIGHT 8 81 82 // Trackbar (slider) thumb states 83 #define TUS_NORMAL 1 84 #define TUS_HOT 2 85 #define TUS_PRESSED 3 86 #define TUS_FOCUSED 4 87 #define TUS_DISABLED 5 88 89 // button states 90 #define PBS_NORMAL 1 91 #define PBS_HOT 2 92 #define PBS_PRESSED 3 93 #define PBS_DISABLED 4 94 #define PBS_DEFAULTED 5 95 96 // Spin button parts 97 #define SPNP_UP 1 98 #define SPNP_DOWN 2 99 100 // Spin button states 101 #define DNS_NORMAL 1 102 #define DNS_HOT 2 103 #define DNS_PRESSED 3 104 #define DNS_DISABLED 4 105 #define UPS_NORMAL 1 106 #define UPS_HOT 2 107 #define UPS_PRESSED 3 108 #define UPS_DISABLED 4 109 110 111 SOFT_LINK_LIBRARY(uxtheme) 112 SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) 113 SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) 114 SOFT_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)) 115 SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) 116 SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) 117 118 static bool haveTheme; 119 120 static const unsigned vistaMenuListButtonOutset = 1; 121 122 using namespace std; 123 124 namespace WebCore { 125 126 // This is the fixed width IE and Firefox use for buttons on dropdown menus 127 static const int dropDownButtonWidth = 17; 128 129 static const int shell32MagnifierIconIndex = 22; 130 131 // Default font size to match Firefox. 132 static const float defaultControlFontPixelSize = 13; 133 134 static const float defaultCancelButtonSize = 9; 135 static const float minCancelButtonSize = 5; 136 static const float maxCancelButtonSize = 21; 137 static const float defaultSearchFieldResultsDecorationSize = 13; 138 static const float minSearchFieldResultsDecorationSize = 9; 139 static const float maxSearchFieldResultsDecorationSize = 30; 140 static const float defaultSearchFieldResultsButtonWidth = 18; 141 142 static bool gWebKitIsBeingUnloaded; 143 144 static bool documentIsInApplicationChromeMode(const Document* document) 145 { 146 Settings* settings = document->settings(); 147 return settings && settings->inApplicationChromeMode(); 148 } 149 150 void RenderThemeWin::setWebKitIsBeingUnloaded() 151 { 152 gWebKitIsBeingUnloaded = true; 153 } 154 155 PassRefPtr<RenderTheme> RenderThemeWin::create() 156 { 157 return adoptRef(new RenderThemeWin); 158 } 159 160 #if !USE(SAFARI_THEME) 161 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 162 { 163 static RenderTheme* winTheme = RenderThemeWin::create().releaseRef(); 164 return winTheme; 165 } 166 #endif 167 168 RenderThemeWin::RenderThemeWin() 169 : m_buttonTheme(0) 170 , m_textFieldTheme(0) 171 , m_menuListTheme(0) 172 , m_sliderTheme(0) 173 , m_spinButtonTheme(0) 174 { 175 haveTheme = uxthemeLibrary() && IsThemeActive(); 176 } 177 178 RenderThemeWin::~RenderThemeWin() 179 { 180 // If WebKit is being unloaded, then uxtheme.dll is no longer available. 181 if (gWebKitIsBeingUnloaded || !uxthemeLibrary()) 182 return; 183 close(); 184 } 185 186 HANDLE RenderThemeWin::buttonTheme() const 187 { 188 if (haveTheme && !m_buttonTheme) 189 m_buttonTheme = OpenThemeData(0, L"Button"); 190 return m_buttonTheme; 191 } 192 193 HANDLE RenderThemeWin::textFieldTheme() const 194 { 195 if (haveTheme && !m_textFieldTheme) 196 m_textFieldTheme = OpenThemeData(0, L"Edit"); 197 return m_textFieldTheme; 198 } 199 200 HANDLE RenderThemeWin::menuListTheme() const 201 { 202 if (haveTheme && !m_menuListTheme) 203 m_menuListTheme = OpenThemeData(0, L"ComboBox"); 204 return m_menuListTheme; 205 } 206 207 HANDLE RenderThemeWin::sliderTheme() const 208 { 209 if (haveTheme && !m_sliderTheme) 210 m_sliderTheme = OpenThemeData(0, L"TrackBar"); 211 return m_sliderTheme; 212 } 213 214 HANDLE RenderThemeWin::spinButtonTheme() const 215 { 216 if (haveTheme && !m_spinButtonTheme) 217 m_spinButtonTheme = OpenThemeData(0, L"Spin"); 218 return m_spinButtonTheme; 219 } 220 221 void RenderThemeWin::close() 222 { 223 // This method will need to be called when the OS theme changes to flush our cached themes. 224 if (m_buttonTheme) 225 CloseThemeData(m_buttonTheme); 226 if (m_textFieldTheme) 227 CloseThemeData(m_textFieldTheme); 228 if (m_menuListTheme) 229 CloseThemeData(m_menuListTheme); 230 if (m_sliderTheme) 231 CloseThemeData(m_sliderTheme); 232 if (m_spinButtonTheme) 233 CloseThemeData(m_spinButtonTheme); 234 m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = m_spinButtonTheme = 0; 235 236 haveTheme = uxthemeLibrary() && IsThemeActive(); 237 } 238 239 void RenderThemeWin::themeChanged() 240 { 241 close(); 242 } 243 244 String RenderThemeWin::extraDefaultStyleSheet() 245 { 246 return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); 247 } 248 249 String RenderThemeWin::extraQuirksStyleSheet() 250 { 251 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); 252 } 253 254 bool RenderThemeWin::supportsHover(const RenderStyle*) const 255 { 256 // The Classic/2k look has no hover effects. 257 return haveTheme; 258 } 259 260 Color RenderThemeWin::platformActiveSelectionBackgroundColor() const 261 { 262 COLORREF color = GetSysColor(COLOR_HIGHLIGHT); 263 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 264 } 265 266 Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const 267 { 268 // This color matches Firefox. 269 return Color(176, 176, 176); 270 } 271 272 Color RenderThemeWin::platformActiveSelectionForegroundColor() const 273 { 274 COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); 275 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 276 } 277 278 Color RenderThemeWin::platformInactiveSelectionForegroundColor() const 279 { 280 return platformActiveSelectionForegroundColor(); 281 } 282 283 static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize) 284 { 285 fontDescription.setIsAbsoluteSize(true); 286 fontDescription.setGenericFamily(FontDescription::NoFamily); 287 fontDescription.firstFamily().setFamily(String(logFont.lfFaceName)); 288 fontDescription.setSpecifiedSize(fontSize); 289 fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight. 290 fontDescription.setItalic(logFont.lfItalic); 291 } 292 293 static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont) 294 { 295 fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); 296 } 297 298 void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const 299 { 300 static FontDescription captionFont; 301 static FontDescription controlFont; 302 static FontDescription smallCaptionFont; 303 static FontDescription menuFont; 304 static FontDescription iconFont; 305 static FontDescription messageBoxFont; 306 static FontDescription statusBarFont; 307 static FontDescription systemFont; 308 309 static bool initialized; 310 static NONCLIENTMETRICS ncm; 311 312 if (!initialized) { 313 initialized = true; 314 ncm.cbSize = sizeof(NONCLIENTMETRICS); 315 ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); 316 } 317 318 switch (propId) { 319 case CSSValueIcon: { 320 if (!iconFont.isAbsoluteSize()) { 321 LOGFONT logFont; 322 ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); 323 fillFontDescription(iconFont, logFont); 324 } 325 fontDescription = iconFont; 326 break; 327 } 328 case CSSValueMenu: 329 if (!menuFont.isAbsoluteSize()) 330 fillFontDescription(menuFont, ncm.lfMenuFont); 331 fontDescription = menuFont; 332 break; 333 case CSSValueMessageBox: 334 if (!messageBoxFont.isAbsoluteSize()) 335 fillFontDescription(messageBoxFont, ncm.lfMessageFont); 336 fontDescription = messageBoxFont; 337 break; 338 case CSSValueStatusBar: 339 if (!statusBarFont.isAbsoluteSize()) 340 fillFontDescription(statusBarFont, ncm.lfStatusFont); 341 fontDescription = statusBarFont; 342 break; 343 case CSSValueCaption: 344 if (!captionFont.isAbsoluteSize()) 345 fillFontDescription(captionFont, ncm.lfCaptionFont); 346 fontDescription = captionFont; 347 break; 348 case CSSValueSmallCaption: 349 if (!smallCaptionFont.isAbsoluteSize()) 350 fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); 351 fontDescription = smallCaptionFont; 352 break; 353 case CSSValueWebkitSmallControl: 354 case CSSValueWebkitMiniControl: // Just map to small. 355 case CSSValueWebkitControl: // Just map to small. 356 if (!controlFont.isAbsoluteSize()) { 357 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); 358 if (hGDI) { 359 LOGFONT logFont; 360 if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) 361 fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); 362 } 363 } 364 fontDescription = controlFont; 365 break; 366 default: { // Everything else uses the stock GUI font. 367 if (!systemFont.isAbsoluteSize()) { 368 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); 369 if (hGDI) { 370 LOGFONT logFont; 371 if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) 372 fillFontDescription(systemFont, logFont); 373 } 374 } 375 fontDescription = systemFont; 376 } 377 } 378 } 379 380 bool RenderThemeWin::supportsFocus(ControlPart appearance) const 381 { 382 switch (appearance) { 383 case PushButtonPart: 384 case ButtonPart: 385 case DefaultButtonPart: 386 return true; 387 default: 388 return false; 389 } 390 } 391 392 bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const 393 { 394 return supportsFocus(style->appearance()); 395 } 396 397 unsigned RenderThemeWin::determineClassicState(RenderObject* o, ControlSubPart subPart) 398 { 399 unsigned state = 0; 400 switch (o->style()->appearance()) { 401 case PushButtonPart: 402 case ButtonPart: 403 case DefaultButtonPart: 404 state = DFCS_BUTTONPUSH; 405 if (!isEnabled(o)) 406 state |= DFCS_INACTIVE; 407 else if (isPressed(o)) 408 state |= DFCS_PUSHED; 409 break; 410 case RadioPart: 411 case CheckboxPart: 412 state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK; 413 if (isChecked(o)) 414 state |= DFCS_CHECKED; 415 if (!isEnabled(o)) 416 state |= DFCS_INACTIVE; 417 else if (isPressed(o)) 418 state |= DFCS_PUSHED; 419 break; 420 case MenulistPart: 421 state = DFCS_SCROLLCOMBOBOX; 422 if (!isEnabled(o)) 423 state |= DFCS_INACTIVE; 424 else if (isPressed(o)) 425 state |= DFCS_PUSHED; 426 break; 427 case InnerSpinButtonPart: { 428 bool isUpButton = subPart == SpinButtonUp; 429 state = isUpButton ? DFCS_SCROLLUP : DFCS_SCROLLDOWN; 430 if (!isEnabled(o) || isReadOnlyControl(o)) 431 state |= DFCS_INACTIVE; 432 else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o)) 433 state |= DFCS_PUSHED; 434 else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o)) 435 state |= DFCS_HOT; 436 break; 437 } 438 default: 439 break; 440 } 441 return state; 442 } 443 444 unsigned RenderThemeWin::determineState(RenderObject* o) 445 { 446 unsigned result = TS_NORMAL; 447 ControlPart appearance = o->style()->appearance(); 448 if (!isEnabled(o)) 449 result = TS_DISABLED; 450 else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance)) 451 result = TFS_READONLY; // Readonly is supported on textfields. 452 else if (isPressed(o)) // Active overrides hover and focused. 453 result = TS_ACTIVE; 454 else if (supportsFocus(appearance) && isFocused(o)) 455 result = TS_FOCUSED; 456 else if (isHovered(o)) 457 result = TS_HOVER; 458 if (isChecked(o)) 459 result += 4; // 4 unchecked states, 4 checked states. 460 return result; 461 } 462 463 unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o) 464 { 465 unsigned result = TUS_NORMAL; 466 if (!isEnabled(o->parent())) 467 result = TUS_DISABLED; 468 else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) 469 result = TUS_FOCUSED; 470 else if (toRenderSlider(o->parent())->inDragMode()) 471 result = TUS_PRESSED; 472 else if (isHovered(o)) 473 result = TUS_HOT; 474 return result; 475 } 476 477 unsigned RenderThemeWin::determineButtonState(RenderObject* o) 478 { 479 unsigned result = PBS_NORMAL; 480 if (!isEnabled(o)) 481 result = PBS_DISABLED; 482 else if (isPressed(o)) 483 result = PBS_PRESSED; 484 else if (supportsFocus(o->style()->appearance()) && isFocused(o)) 485 result = PBS_DEFAULTED; 486 else if (isHovered(o)) 487 result = PBS_HOT; 488 else if (isDefault(o)) 489 result = PBS_DEFAULTED; 490 return result; 491 } 492 493 unsigned RenderThemeWin::determineSpinButtonState(RenderObject* o, ControlSubPart subPart) 494 { 495 bool isUpButton = subPart == SpinButtonUp; 496 unsigned result = isUpButton ? UPS_NORMAL : DNS_NORMAL; 497 if (!isEnabled(o) || isReadOnlyControl(o)) 498 result = isUpButton ? UPS_DISABLED : DNS_DISABLED; 499 else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o)) 500 result = isUpButton ? UPS_PRESSED : DNS_PRESSED; 501 else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o)) 502 result = isUpButton ? UPS_HOT : DNS_HOT; 503 return result; 504 } 505 506 ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o, ControlSubPart subPart) 507 { 508 ThemeData result; 509 switch (o->style()->appearance()) { 510 case PushButtonPart: 511 case ButtonPart: 512 case DefaultButtonPart: 513 case CheckboxPart: 514 case RadioPart: 515 result.m_part = DFC_BUTTON; 516 result.m_state = determineClassicState(o); 517 break; 518 case MenulistPart: 519 result.m_part = DFC_SCROLL; 520 result.m_state = determineClassicState(o); 521 break; 522 case SearchFieldPart: 523 case TextFieldPart: 524 case TextAreaPart: 525 result.m_part = TFP_TEXTFIELD; 526 result.m_state = determineState(o); 527 break; 528 case SliderHorizontalPart: 529 result.m_part = TKP_TRACK; 530 result.m_state = TS_NORMAL; 531 break; 532 case SliderVerticalPart: 533 result.m_part = TKP_TRACKVERT; 534 result.m_state = TS_NORMAL; 535 break; 536 case SliderThumbHorizontalPart: 537 result.m_part = TKP_THUMBBOTTOM; 538 result.m_state = determineSliderThumbState(o); 539 break; 540 case SliderThumbVerticalPart: 541 result.m_part = TKP_THUMBRIGHT; 542 result.m_state = determineSliderThumbState(o); 543 break; 544 case InnerSpinButtonPart: 545 result.m_part = DFC_SCROLL; 546 result.m_state = determineClassicState(o, subPart); 547 break; 548 default: 549 break; 550 } 551 return result; 552 } 553 554 ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart) 555 { 556 if (!haveTheme) 557 return getClassicThemeData(o, subPart); 558 559 ThemeData result; 560 switch (o->style()->appearance()) { 561 case PushButtonPart: 562 case ButtonPart: 563 case DefaultButtonPart: 564 result.m_part = BP_BUTTON; 565 result.m_state = determineButtonState(o); 566 break; 567 case CheckboxPart: 568 result.m_part = BP_CHECKBOX; 569 result.m_state = determineState(o); 570 break; 571 case MenulistPart: 572 case MenulistButtonPart: { 573 const bool isVistaOrLater = (windowsVersion() >= WindowsVista); 574 result.m_part = isVistaOrLater ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON; 575 if (isVistaOrLater && documentIsInApplicationChromeMode(o->document())) { 576 // The "readonly" look we use in application chrome mode 577 // only uses a "normal" look for the drop down button. 578 result.m_state = TS_NORMAL; 579 } else 580 result.m_state = determineState(o); 581 break; 582 } 583 case RadioPart: 584 result.m_part = BP_RADIO; 585 result.m_state = determineState(o); 586 break; 587 case SearchFieldPart: 588 case TextFieldPart: 589 case TextAreaPart: 590 result.m_part = (windowsVersion() >= WindowsVista) ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD; 591 result.m_state = determineState(o); 592 break; 593 case SliderHorizontalPart: 594 result.m_part = TKP_TRACK; 595 result.m_state = TS_NORMAL; 596 break; 597 case SliderVerticalPart: 598 result.m_part = TKP_TRACKVERT; 599 result.m_state = TS_NORMAL; 600 break; 601 case SliderThumbHorizontalPart: 602 result.m_part = TKP_THUMBBOTTOM; 603 result.m_state = determineSliderThumbState(o); 604 break; 605 case SliderThumbVerticalPart: 606 result.m_part = TKP_THUMBRIGHT; 607 result.m_state = determineSliderThumbState(o); 608 break; 609 case InnerSpinButtonPart: 610 result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN; 611 result.m_state = determineSpinButtonState(o, subPart); 612 break; 613 } 614 615 return result; 616 } 617 618 static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r) 619 { 620 bool alphaBlend = false; 621 if (theme) 622 alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state); 623 LocalWindowsContext windowsContext(context, r, alphaBlend); 624 RECT widgetRect = r; 625 if (theme) 626 DrawThemeBackground(theme, windowsContext.hdc(), themeData.m_part, themeData.m_state, &widgetRect, 0); 627 else { 628 HDC hdc = windowsContext.hdc(); 629 if (themeData.m_part == TFP_TEXTFIELD) { 630 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 631 if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY) 632 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1)); 633 else 634 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1)); 635 } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) { 636 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); 637 ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH)); 638 } else if ((o->style()->appearance() == SliderThumbHorizontalPart || 639 o->style()->appearance() == SliderThumbVerticalPart) && 640 (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP || 641 themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) { 642 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST); 643 if (themeData.m_state == TUS_DISABLED) { 644 static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}; 645 HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits); 646 if (patternBmp) { 647 HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp); 648 COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE)); 649 COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); 650 POINT p; 651 ::GetViewportOrgEx(hdc, &p); 652 ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL); 653 HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush); 654 ::FillRect(hdc, &widgetRect, brush); 655 ::SetTextColor(hdc, oldForeColor); 656 ::SetBkColor(hdc, oldBackColor); 657 ::SelectObject(hdc, oldBrush); 658 ::DeleteObject(brush); 659 } else 660 ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT); 661 ::DeleteObject(patternBmp); 662 } 663 } else { 664 // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists. 665 if (o->style()->appearance() == DefaultButtonPart) { 666 HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW); 667 ::FrameRect(hdc, &widgetRect, brush); 668 ::InflateRect(&widgetRect, -1, -1); 669 ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE); 670 } 671 ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state); 672 } 673 } 674 } 675 676 bool RenderThemeWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) 677 { 678 drawControl(i.context, o, buttonTheme(), getThemeData(o), r); 679 return false; 680 } 681 682 void RenderThemeWin::adjustInnerSpinButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 683 { 684 int width = ::GetSystemMetrics(SM_CXVSCROLL); 685 if (width <= 0) 686 width = 17; // Vista's default. 687 style->setWidth(Length(width, Fixed)); 688 style->setMinWidth(Length(width, Fixed)); 689 } 690 691 bool RenderThemeWin::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& r) 692 { 693 // We split the specified rectangle into two vertically. We can't draw a 694 // spin button of which height is less than 2px. 695 if (r.height() < 2) 696 return false; 697 IntRect upRect(r); 698 upRect.setHeight(r.height() / 2); 699 IntRect downRect(r); 700 downRect.setY(upRect.maxY()); 701 downRect.setHeight(r.height() - upRect.height()); 702 drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonUp), upRect); 703 drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonDown), downRect); 704 return false; 705 } 706 707 void RenderThemeWin::setCheckboxSize(RenderStyle* style) const 708 { 709 // If the width and height are both specified, then we have nothing to do. 710 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 711 return; 712 713 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. 714 // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for 715 // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's 716 // metrics. 717 if (style->width().isIntrinsicOrAuto()) 718 style->setWidth(Length(13, Fixed)); 719 if (style->height().isAuto()) 720 style->setHeight(Length(13, Fixed)); 721 } 722 723 bool RenderThemeWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) 724 { 725 drawControl(i.context, o, textFieldTheme(), getThemeData(o), r); 726 return false; 727 } 728 729 bool RenderThemeWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) 730 { 731 HANDLE theme; 732 int part; 733 if (haveTheme && (windowsVersion() >= WindowsVista)) { 734 theme = menuListTheme(); 735 if (documentIsInApplicationChromeMode(o->document())) 736 part = CP_READONLY; 737 else 738 part = CP_BORDER; 739 } else { 740 theme = textFieldTheme(); 741 part = TFP_TEXTFIELD; 742 } 743 744 drawControl(i.context, o, theme, ThemeData(part, determineState(o)), r); 745 746 return paintMenuListButton(o, i, r); 747 } 748 749 void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 750 { 751 style->resetBorder(); 752 adjustMenuListButtonStyle(selector, style, e); 753 } 754 755 void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 756 { 757 // These are the paddings needed to place the text correctly in the <select> box 758 const int dropDownBoxPaddingTop = 2; 759 const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4; 760 const int dropDownBoxPaddingBottom = 2; 761 const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth; 762 // The <select> box must be at least 12px high for the button to render nicely on Windows 763 const int dropDownBoxMinHeight = 12; 764 765 // Position the text correctly within the select box and make the box wide enough to fit the dropdown button 766 style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed)); 767 style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed)); 768 style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed)); 769 style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed)); 770 771 // Height is locked to auto 772 style->setHeight(Length(Auto)); 773 774 // Calculate our min-height 775 int minHeight = style->fontMetrics().height(); 776 minHeight = max(minHeight, dropDownBoxMinHeight); 777 778 style->setMinHeight(Length(minHeight, Fixed)); 779 780 // White-space is locked to pre 781 style->setWhiteSpace(PRE); 782 } 783 784 bool RenderThemeWin::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& r) 785 { 786 // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border. 787 int borderThickness = haveTheme ? 1 : 2; 788 789 // Paint the dropdown button on the inner edge of the text field, 790 // leaving space for the text field's 1px border 791 IntRect buttonRect(r); 792 buttonRect.inflate(-borderThickness); 793 if (o->style()->direction() == LTR) 794 buttonRect.setX(buttonRect.maxX() - dropDownButtonWidth); 795 buttonRect.setWidth(dropDownButtonWidth); 796 797 if ((windowsVersion() >= WindowsVista)) { 798 // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border. 799 buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset); 800 buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset); 801 buttonRect.setWidth(buttonRect.width() + vistaMenuListButtonOutset); 802 } 803 804 drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect); 805 806 return false; 807 } 808 809 const int trackWidth = 4; 810 811 bool RenderThemeWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r) 812 { 813 IntRect bounds = r; 814 815 if (o->style()->appearance() == SliderHorizontalPart) { 816 bounds.setHeight(trackWidth); 817 bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); 818 } else if (o->style()->appearance() == SliderVerticalPart) { 819 bounds.setWidth(trackWidth); 820 bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); 821 } 822 823 drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds); 824 return false; 825 } 826 827 bool RenderThemeWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r) 828 { 829 drawControl(i.context, o, sliderTheme(), getThemeData(o), r); 830 return false; 831 } 832 833 const int sliderThumbWidth = 7; 834 const int sliderThumbHeight = 15; 835 836 void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const 837 { 838 ControlPart part = o->style()->appearance(); 839 if (part == SliderThumbVerticalPart) { 840 o->style()->setWidth(Length(sliderThumbHeight, Fixed)); 841 o->style()->setHeight(Length(sliderThumbWidth, Fixed)); 842 } else if (part == SliderThumbHorizontalPart) { 843 o->style()->setWidth(Length(sliderThumbWidth, Fixed)); 844 o->style()->setHeight(Length(sliderThumbHeight, Fixed)); 845 } 846 #if ENABLE(VIDEO) 847 else if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) 848 RenderMediaControls::adjustMediaSliderThumbSize(o); 849 #endif 850 } 851 852 bool RenderThemeWin::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) 853 { 854 return paintTextField(o, i, r); 855 } 856 857 void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 858 { 859 // Override paddingSize to match AppKit text positioning. 860 const int padding = 1; 861 style->setPaddingLeft(Length(padding, Fixed)); 862 style->setPaddingRight(Length(padding, Fixed)); 863 style->setPaddingTop(Length(padding, Fixed)); 864 style->setPaddingBottom(Length(padding, Fixed)); 865 if (e && e->focused() && e->document()->frame()->selection()->isFocusedAndActive()) 866 style->setOutlineOffset(-2); 867 } 868 869 bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 870 { 871 IntRect bounds = r; 872 ASSERT(o->parent()); 873 if (!o->parent() || !o->parent()->isBox()) 874 return false; 875 876 RenderBox* parentRenderBox = toRenderBox(o->parent()); 877 878 IntRect parentBox = parentRenderBox->absoluteContentBox(); 879 880 // Make sure the scaled button stays square and will fit in its parent's box 881 bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); 882 bounds.setWidth(bounds.height()); 883 884 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 885 // be one pixel closer to the bottom of the field. This tends to look better with the text. 886 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 887 888 static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); 889 static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); 890 paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds); 891 return false; 892 } 893 894 void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 895 { 896 // Scale the button size based on the font size 897 float fontScale = style->fontSize() / defaultControlFontPixelSize; 898 int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); 899 style->setWidth(Length(cancelButtonSize, Fixed)); 900 style->setHeight(Length(cancelButtonSize, Fixed)); 901 } 902 903 void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 904 { 905 IntSize emptySize(1, 11); 906 style->setWidth(Length(emptySize.width(), Fixed)); 907 style->setHeight(Length(emptySize.height(), Fixed)); 908 } 909 910 void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 911 { 912 // Scale the decoration size based on the font size 913 float fontScale = style->fontSize() / defaultControlFontPixelSize; 914 int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 915 maxSearchFieldResultsDecorationSize)); 916 style->setWidth(Length(magnifierSize, Fixed)); 917 style->setHeight(Length(magnifierSize, Fixed)); 918 } 919 920 bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 921 { 922 IntRect bounds = r; 923 ASSERT(o->parent()); 924 if (!o->parent() || !o->parent()->isBox()) 925 return false; 926 927 RenderBox* parentRenderBox = toRenderBox(o->parent()); 928 IntRect parentBox = parentRenderBox->absoluteContentBox(); 929 930 // Make sure the scaled decoration stays square and will fit in its parent's box 931 bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); 932 bounds.setWidth(bounds.height()); 933 934 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will 935 // be one pixel closer to the bottom of the field. This tends to look better with the text. 936 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 937 938 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); 939 paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); 940 return false; 941 } 942 943 void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 944 { 945 // Scale the button size based on the font size 946 float fontScale = style->fontSize() / defaultControlFontPixelSize; 947 int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 948 maxSearchFieldResultsDecorationSize)); 949 int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); 950 style->setWidth(Length(magnifierWidth, Fixed)); 951 style->setHeight(Length(magnifierHeight, Fixed)); 952 } 953 954 bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 955 { 956 IntRect bounds = r; 957 ASSERT(o->parent()); 958 if (!o->parent()) 959 return false; 960 if (!o->parent() || !o->parent()->isBox()) 961 return false; 962 963 RenderBox* parentRenderBox = toRenderBox(o->parent()); 964 IntRect parentBox = parentRenderBox->absoluteContentBox(); 965 966 // Make sure the scaled decoration will fit in its parent's box 967 bounds.setHeight(min(parentBox.height(), bounds.height())); 968 bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize))); 969 970 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 971 // be one pixel closer to the bottom of the field. This tends to look better with the text. 972 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 973 974 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); 975 paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); 976 return false; 977 } 978 979 // Map a CSSValue* system color to an index understood by GetSysColor 980 static int cssValueIdToSysColorIndex(int cssValueId) 981 { 982 switch (cssValueId) { 983 case CSSValueActiveborder: return COLOR_ACTIVEBORDER; 984 case CSSValueActivecaption: return COLOR_ACTIVECAPTION; 985 case CSSValueAppworkspace: return COLOR_APPWORKSPACE; 986 case CSSValueBackground: return COLOR_BACKGROUND; 987 case CSSValueButtonface: return COLOR_BTNFACE; 988 case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; 989 case CSSValueButtonshadow: return COLOR_BTNSHADOW; 990 case CSSValueButtontext: return COLOR_BTNTEXT; 991 case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; 992 case CSSValueGraytext: return COLOR_GRAYTEXT; 993 case CSSValueHighlight: return COLOR_HIGHLIGHT; 994 case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; 995 case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; 996 case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; 997 case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; 998 case CSSValueInfobackground: return COLOR_INFOBK; 999 case CSSValueInfotext: return COLOR_INFOTEXT; 1000 case CSSValueMenu: return COLOR_MENU; 1001 case CSSValueMenutext: return COLOR_MENUTEXT; 1002 case CSSValueScrollbar: return COLOR_SCROLLBAR; 1003 case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; 1004 case CSSValueThreedface: return COLOR_3DFACE; 1005 case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; 1006 case CSSValueThreedlightshadow: return COLOR_3DLIGHT; 1007 case CSSValueThreedshadow: return COLOR_3DSHADOW; 1008 case CSSValueWindow: return COLOR_WINDOW; 1009 case CSSValueWindowframe: return COLOR_WINDOWFRAME; 1010 case CSSValueWindowtext: return COLOR_WINDOWTEXT; 1011 default: return -1; // Unsupported CSSValue 1012 } 1013 } 1014 1015 Color RenderThemeWin::systemColor(int cssValueId) const 1016 { 1017 int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); 1018 if (sysColorIndex == -1) 1019 return RenderTheme::systemColor(cssValueId); 1020 1021 COLORREF color = GetSysColor(sysColorIndex); 1022 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 1023 } 1024 1025 #if ENABLE(VIDEO) 1026 1027 String RenderThemeWin::extraMediaControlsStyleSheet() 1028 { 1029 return String(mediaControlsQuickTimeUserAgentStyleSheet, sizeof(mediaControlsQuickTimeUserAgentStyleSheet)); 1030 } 1031 1032 bool RenderThemeWin::supportsClosedCaptioning() const 1033 { 1034 // We rely on QuickTime to render captions so only enable the button for a video element. 1035 #if SAFARI_THEME_VERSION >= 4 1036 return true; 1037 #else 1038 return false; 1039 #endif 1040 } 1041 1042 bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1043 { 1044 return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r); 1045 } 1046 1047 bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1048 { 1049 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r); 1050 } 1051 1052 bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1053 { 1054 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r); 1055 } 1056 1057 bool RenderThemeWin::paintMediaRewindButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1058 { 1059 return RenderMediaControls::paintMediaControlsPart(MediaRewindButton, o, paintInfo, r); 1060 } 1061 1062 bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1063 { 1064 return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r); 1065 } 1066 1067 bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1068 { 1069 return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r); 1070 } 1071 1072 bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1073 { 1074 return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r); 1075 } 1076 1077 bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1078 { 1079 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r); 1080 } 1081 1082 bool RenderThemeWin::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1083 { 1084 return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); 1085 } 1086 1087 bool RenderThemeWin::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1088 { 1089 return RenderMediaControls::paintMediaControlsPart(MediaTimelineContainer, o, paintInfo, r); 1090 } 1091 1092 bool RenderThemeWin::paintMediaVolumeSliderContainer(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1093 { 1094 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderContainer, o, paintInfo, r); 1095 } 1096 1097 bool RenderThemeWin::paintMediaVolumeSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1098 { 1099 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSlider, o, paintInfo, r); 1100 } 1101 1102 bool RenderThemeWin::paintMediaVolumeSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 1103 { 1104 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderThumb, o, paintInfo, r); 1105 } 1106 1107 IntPoint RenderThemeWin::volumeSliderOffsetFromMuteButton(RenderBox* muteButtonBox, const IntSize& size) const 1108 { 1109 return RenderMediaControls::volumeSliderOffsetFromMuteButton(muteButtonBox, size); 1110 } 1111 1112 1113 #endif 1114 1115 } 1116