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