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 94SOFT_LINK_LIBRARY(uxtheme) 95SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) 96SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) 97SOFT_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)) 98SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) 99SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) 100 101static bool haveTheme; 102 103static const unsigned vistaMenuListButtonOutset = 1; 104 105using namespace std; 106 107namespace WebCore { 108 109// This is the fixed width IE and Firefox use for buttons on dropdown menus 110static const int dropDownButtonWidth = 17; 111 112static const int shell32MagnifierIconIndex = 22; 113 114// Default font size to match Firefox. 115static const float defaultControlFontPixelSize = 13; 116 117static const float defaultCancelButtonSize = 9; 118static const float minCancelButtonSize = 5; 119static const float maxCancelButtonSize = 21; 120static const float defaultSearchFieldResultsDecorationSize = 13; 121static const float minSearchFieldResultsDecorationSize = 9; 122static const float maxSearchFieldResultsDecorationSize = 30; 123static const float defaultSearchFieldResultsButtonWidth = 18; 124 125static bool gWebKitIsBeingUnloaded; 126 127static bool documentIsInApplicationChromeMode(const Document* document) 128{ 129 Settings* settings = document->settings(); 130 return settings && settings->inApplicationChromeMode(); 131} 132 133void RenderThemeWin::setWebKitIsBeingUnloaded() 134{ 135 gWebKitIsBeingUnloaded = true; 136} 137 138PassRefPtr<RenderTheme> RenderThemeWin::create() 139{ 140 return adoptRef(new RenderThemeWin); 141} 142 143#if !USE(SAFARI_THEME) 144PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) 145{ 146 static RenderTheme* winTheme = RenderThemeWin::create().releaseRef(); 147 return winTheme; 148} 149#endif 150 151RenderThemeWin::RenderThemeWin() 152 : m_buttonTheme(0) 153 , m_textFieldTheme(0) 154 , m_menuListTheme(0) 155 , m_sliderTheme(0) 156{ 157 haveTheme = uxthemeLibrary() && IsThemeActive(); 158} 159 160RenderThemeWin::~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 168HANDLE RenderThemeWin::buttonTheme() const 169{ 170 if (haveTheme && !m_buttonTheme) 171 m_buttonTheme = OpenThemeData(0, L"Button"); 172 return m_buttonTheme; 173} 174 175HANDLE RenderThemeWin::textFieldTheme() const 176{ 177 if (haveTheme && !m_textFieldTheme) 178 m_textFieldTheme = OpenThemeData(0, L"Edit"); 179 return m_textFieldTheme; 180} 181 182HANDLE RenderThemeWin::menuListTheme() const 183{ 184 if (haveTheme && !m_menuListTheme) 185 m_menuListTheme = OpenThemeData(0, L"ComboBox"); 186 return m_menuListTheme; 187} 188 189HANDLE RenderThemeWin::sliderTheme() const 190{ 191 if (haveTheme && !m_sliderTheme) 192 m_sliderTheme = OpenThemeData(0, L"TrackBar"); 193 return m_sliderTheme; 194} 195 196void 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 212void RenderThemeWin::themeChanged() 213{ 214 close(); 215} 216 217String RenderThemeWin::extraDefaultStyleSheet() 218{ 219 return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); 220} 221 222String RenderThemeWin::extraQuirksStyleSheet() 223{ 224 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); 225} 226 227bool RenderThemeWin::supportsHover(const RenderStyle*) const 228{ 229 // The Classic/2k look has no hover effects. 230 return haveTheme; 231} 232 233Color RenderThemeWin::platformActiveSelectionBackgroundColor() const 234{ 235 COLORREF color = GetSysColor(COLOR_HIGHLIGHT); 236 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 237} 238 239Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const 240{ 241 // This color matches Firefox. 242 return Color(176, 176, 176); 243} 244 245Color RenderThemeWin::platformActiveSelectionForegroundColor() const 246{ 247 COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); 248 return Color(GetRValue(color), GetGValue(color), GetBValue(color)); 249} 250 251Color RenderThemeWin::platformInactiveSelectionForegroundColor() const 252{ 253 return platformActiveSelectionForegroundColor(); 254} 255 256static 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 266static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont) 267{ 268 fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); 269} 270 271void 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 353bool 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 365bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const 366{ 367 return supportsFocus(style->appearance()); 368} 369 370unsigned 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 405unsigned 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 424unsigned 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 438unsigned 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 454ThemeData 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 498ThemeData 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 556static 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 614bool 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 620void 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 636bool 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 642bool 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 662void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 663{ 664 style->resetBorder(); 665 adjustMenuListButtonStyle(selector, style, e); 666} 667 668void 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 697bool 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 722const int trackWidth = 4; 723 724bool 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 740bool 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 746const int sliderThumbWidth = 7; 747const int sliderThumbHeight = 15; 748 749void 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 764int RenderThemeWin::buttonInternalPaddingLeft() const 765{ 766 return 3; 767} 768 769int RenderThemeWin::buttonInternalPaddingRight() const 770{ 771 return 3; 772} 773 774int RenderThemeWin::buttonInternalPaddingTop() const 775{ 776 return 1; 777} 778 779int RenderThemeWin::buttonInternalPaddingBottom() const 780{ 781 return 1; 782} 783 784bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 785{ 786 return paintTextField(o, i, r); 787} 788 789void 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 801bool 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 826void 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 835void 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 842void 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 852bool 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 875void 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 886bool 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 912static 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 947Color 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 959bool 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 975bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 976{ 977 return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r); 978} 979 980bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 981{ 982 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r); 983} 984 985bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 986{ 987 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r); 988} 989 990bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 991{ 992 return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r); 993} 994 995bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 996{ 997 return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r); 998} 999 1000bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1001{ 1002 return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r); 1003} 1004 1005bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1006{ 1007 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r); 1008} 1009 1010bool 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