diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderThemeWin.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderThemeWin.cpp | 1118 |
1 files changed, 1118 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderThemeWin.cpp b/Source/WebCore/rendering/RenderThemeWin.cpp new file mode 100644 index 0000000..f0f8268 --- /dev/null +++ b/Source/WebCore/rendering/RenderThemeWin.cpp @@ -0,0 +1,1118 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. + * Copyright (C) 2009 Kenneth Rohde Christiansen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "RenderThemeWin.h" + +#include "CSSValueKeywords.h" +#include "Element.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "LocalWindowsContext.h" +#include "RenderSlider.h" +#include "Settings.h" +#include "SoftLinking.h" +#include "SystemInfo.h" +#include "UserAgentStyleSheets.h" + +#if ENABLE(VIDEO) +#include "RenderMediaControls.h" +#endif + +#include <tchar.h> + +/* + * The following constants are used to determine how a widget is drawn using + * Windows' Theme API. For more information on theme parts and states see + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp + */ + +// Generic state constants +#define TS_NORMAL 1 +#define TS_HOVER 2 +#define TS_ACTIVE 3 +#define TS_DISABLED 4 +#define TS_FOCUSED 5 + +// Button constants +#define BP_BUTTON 1 +#define BP_RADIO 2 +#define BP_CHECKBOX 3 + +// Textfield constants +#define TFP_TEXTFIELD 1 +#define EP_EDITBORDER_NOSCROLL 6 +#define TFS_READONLY 6 + +// ComboBox constants (from vsstyle.h) +#define CP_DROPDOWNBUTTON 1 +#define CP_BORDER 4 +#define CP_READONLY 5 +#define CP_DROPDOWNBUTTONRIGHT 6 + +// TrackBar (slider) parts +#define TKP_TRACK 1 +#define TKP_TRACKVERT 2 + +// TrackBar (slider) thumb parts +#define TKP_THUMBBOTTOM 4 +#define TKP_THUMBTOP 5 +#define TKP_THUMBLEFT 7 +#define TKP_THUMBRIGHT 8 + +// Trackbar (slider) thumb states +#define TUS_NORMAL 1 +#define TUS_HOT 2 +#define TUS_PRESSED 3 +#define TUS_FOCUSED 4 +#define TUS_DISABLED 5 + +// button states +#define PBS_NORMAL 1 +#define PBS_HOT 2 +#define PBS_PRESSED 3 +#define PBS_DISABLED 4 +#define PBS_DEFAULTED 5 + +// Spin button parts +#define SPNP_UP 1 +#define SPNP_DOWN 2 + +// Spin button states +#define DNS_NORMAL 1 +#define DNS_HOT 2 +#define DNS_PRESSED 3 +#define DNS_DISABLED 4 +#define UPS_NORMAL 1 +#define UPS_HOT 2 +#define UPS_PRESSED 3 +#define UPS_DISABLED 4 + + +SOFT_LINK_LIBRARY(uxtheme) +SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) +SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) +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)) +SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) +SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) + +static bool haveTheme; + +static const unsigned vistaMenuListButtonOutset = 1; + +using namespace std; + +namespace WebCore { + +// This is the fixed width IE and Firefox use for buttons on dropdown menus +static const int dropDownButtonWidth = 17; + +static const int shell32MagnifierIconIndex = 22; + +// Default font size to match Firefox. +static const float defaultControlFontPixelSize = 13; + +static const float defaultCancelButtonSize = 9; +static const float minCancelButtonSize = 5; +static const float maxCancelButtonSize = 21; +static const float defaultSearchFieldResultsDecorationSize = 13; +static const float minSearchFieldResultsDecorationSize = 9; +static const float maxSearchFieldResultsDecorationSize = 30; +static const float defaultSearchFieldResultsButtonWidth = 18; + +static bool gWebKitIsBeingUnloaded; + +static bool documentIsInApplicationChromeMode(const Document* document) +{ + Settings* settings = document->settings(); + return settings && settings->inApplicationChromeMode(); +} + +void RenderThemeWin::setWebKitIsBeingUnloaded() +{ + gWebKitIsBeingUnloaded = true; +} + +PassRefPtr<RenderTheme> RenderThemeWin::create() +{ + return adoptRef(new RenderThemeWin); +} + +#if !USE(SAFARI_THEME) +PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) +{ + static RenderTheme* winTheme = RenderThemeWin::create().releaseRef(); + return winTheme; +} +#endif + +RenderThemeWin::RenderThemeWin() + : m_buttonTheme(0) + , m_textFieldTheme(0) + , m_menuListTheme(0) + , m_sliderTheme(0) + , m_spinButtonTheme(0) +{ + haveTheme = uxthemeLibrary() && IsThemeActive(); +} + +RenderThemeWin::~RenderThemeWin() +{ + // If WebKit is being unloaded, then uxtheme.dll is no longer available. + if (gWebKitIsBeingUnloaded || !uxthemeLibrary()) + return; + close(); +} + +HANDLE RenderThemeWin::buttonTheme() const +{ + if (haveTheme && !m_buttonTheme) + m_buttonTheme = OpenThemeData(0, L"Button"); + return m_buttonTheme; +} + +HANDLE RenderThemeWin::textFieldTheme() const +{ + if (haveTheme && !m_textFieldTheme) + m_textFieldTheme = OpenThemeData(0, L"Edit"); + return m_textFieldTheme; +} + +HANDLE RenderThemeWin::menuListTheme() const +{ + if (haveTheme && !m_menuListTheme) + m_menuListTheme = OpenThemeData(0, L"ComboBox"); + return m_menuListTheme; +} + +HANDLE RenderThemeWin::sliderTheme() const +{ + if (haveTheme && !m_sliderTheme) + m_sliderTheme = OpenThemeData(0, L"TrackBar"); + return m_sliderTheme; +} + +HANDLE RenderThemeWin::spinButtonTheme() const +{ + if (haveTheme && !m_spinButtonTheme) + m_spinButtonTheme = OpenThemeData(0, L"Spin"); + return m_spinButtonTheme; +} + +void RenderThemeWin::close() +{ + // This method will need to be called when the OS theme changes to flush our cached themes. + if (m_buttonTheme) + CloseThemeData(m_buttonTheme); + if (m_textFieldTheme) + CloseThemeData(m_textFieldTheme); + if (m_menuListTheme) + CloseThemeData(m_menuListTheme); + if (m_sliderTheme) + CloseThemeData(m_sliderTheme); + if (m_spinButtonTheme) + CloseThemeData(m_spinButtonTheme); + m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = m_spinButtonTheme = 0; + + haveTheme = uxthemeLibrary() && IsThemeActive(); +} + +void RenderThemeWin::themeChanged() +{ + close(); +} + +String RenderThemeWin::extraDefaultStyleSheet() +{ + return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); +} + +String RenderThemeWin::extraQuirksStyleSheet() +{ + return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); +} + +bool RenderThemeWin::supportsHover(const RenderStyle*) const +{ + // The Classic/2k look has no hover effects. + return haveTheme; +} + +Color RenderThemeWin::platformActiveSelectionBackgroundColor() const +{ + COLORREF color = GetSysColor(COLOR_HIGHLIGHT); + return Color(GetRValue(color), GetGValue(color), GetBValue(color)); +} + +Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const +{ + // This color matches Firefox. + return Color(176, 176, 176); +} + +Color RenderThemeWin::platformActiveSelectionForegroundColor() const +{ + COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); + return Color(GetRValue(color), GetGValue(color), GetBValue(color)); +} + +Color RenderThemeWin::platformInactiveSelectionForegroundColor() const +{ + return platformActiveSelectionForegroundColor(); +} + +static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize) +{ + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::NoFamily); + fontDescription.firstFamily().setFamily(String(logFont.lfFaceName)); + fontDescription.setSpecifiedSize(fontSize); + fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight. + fontDescription.setItalic(logFont.lfItalic); +} + +static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont) +{ + fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); +} + +void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const +{ + static FontDescription captionFont; + static FontDescription controlFont; + static FontDescription smallCaptionFont; + static FontDescription menuFont; + static FontDescription iconFont; + static FontDescription messageBoxFont; + static FontDescription statusBarFont; + static FontDescription systemFont; + + static bool initialized; + static NONCLIENTMETRICS ncm; + + if (!initialized) { + initialized = true; + ncm.cbSize = sizeof(NONCLIENTMETRICS); + ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); + } + + switch (propId) { + case CSSValueIcon: { + if (!iconFont.isAbsoluteSize()) { + LOGFONT logFont; + ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); + fillFontDescription(iconFont, logFont); + } + fontDescription = iconFont; + break; + } + case CSSValueMenu: + if (!menuFont.isAbsoluteSize()) + fillFontDescription(menuFont, ncm.lfMenuFont); + fontDescription = menuFont; + break; + case CSSValueMessageBox: + if (!messageBoxFont.isAbsoluteSize()) + fillFontDescription(messageBoxFont, ncm.lfMessageFont); + fontDescription = messageBoxFont; + break; + case CSSValueStatusBar: + if (!statusBarFont.isAbsoluteSize()) + fillFontDescription(statusBarFont, ncm.lfStatusFont); + fontDescription = statusBarFont; + break; + case CSSValueCaption: + if (!captionFont.isAbsoluteSize()) + fillFontDescription(captionFont, ncm.lfCaptionFont); + fontDescription = captionFont; + break; + case CSSValueSmallCaption: + if (!smallCaptionFont.isAbsoluteSize()) + fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); + fontDescription = smallCaptionFont; + break; + case CSSValueWebkitSmallControl: + case CSSValueWebkitMiniControl: // Just map to small. + case CSSValueWebkitControl: // Just map to small. + if (!controlFont.isAbsoluteSize()) { + HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); + if (hGDI) { + LOGFONT logFont; + if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) + fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); + } + } + fontDescription = controlFont; + break; + default: { // Everything else uses the stock GUI font. + if (!systemFont.isAbsoluteSize()) { + HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); + if (hGDI) { + LOGFONT logFont; + if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) + fillFontDescription(systemFont, logFont); + } + } + fontDescription = systemFont; + } + } +} + +bool RenderThemeWin::supportsFocus(ControlPart appearance) const +{ + switch (appearance) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + return true; + default: + return false; + } +} + +bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const +{ + return supportsFocus(style->appearance()); +} + +unsigned RenderThemeWin::determineClassicState(RenderObject* o, ControlSubPart subPart) +{ + unsigned state = 0; + switch (o->style()->appearance()) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + state = DFCS_BUTTONPUSH; + if (!isEnabled(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o)) + state |= DFCS_PUSHED; + break; + case RadioPart: + case CheckboxPart: + state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK; + if (isChecked(o)) + state |= DFCS_CHECKED; + if (!isEnabled(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o)) + state |= DFCS_PUSHED; + break; + case MenulistPart: + state = DFCS_SCROLLCOMBOBOX; + if (!isEnabled(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o)) + state |= DFCS_PUSHED; + break; + case InnerSpinButtonPart: { + bool isUpButton = subPart == SpinButtonUp; + state = isUpButton ? DFCS_SCROLLUP : DFCS_SCROLLDOWN; + if (!isEnabled(o) || isReadOnlyControl(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o)) + state |= DFCS_PUSHED; + else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o)) + state |= DFCS_HOT; + break; + } + default: + break; + } + return state; +} + +unsigned RenderThemeWin::determineState(RenderObject* o) +{ + unsigned result = TS_NORMAL; + ControlPart appearance = o->style()->appearance(); + if (!isEnabled(o)) + result = TS_DISABLED; + else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance)) + result = TFS_READONLY; // Readonly is supported on textfields. + else if (isPressed(o)) // Active overrides hover and focused. + result = TS_ACTIVE; + else if (supportsFocus(appearance) && isFocused(o)) + result = TS_FOCUSED; + else if (isHovered(o)) + result = TS_HOVER; + if (isChecked(o)) + result += 4; // 4 unchecked states, 4 checked states. + return result; +} + +unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o) +{ + unsigned result = TUS_NORMAL; + if (!isEnabled(o->parent())) + result = TUS_DISABLED; + else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) + result = TUS_FOCUSED; + else if (toRenderSlider(o->parent())->inDragMode()) + result = TUS_PRESSED; + else if (isHovered(o)) + result = TUS_HOT; + return result; +} + +unsigned RenderThemeWin::determineButtonState(RenderObject* o) +{ + unsigned result = PBS_NORMAL; + if (!isEnabled(o)) + result = PBS_DISABLED; + else if (isPressed(o)) + result = PBS_PRESSED; + else if (supportsFocus(o->style()->appearance()) && isFocused(o)) + result = PBS_DEFAULTED; + else if (isHovered(o)) + result = PBS_HOT; + else if (isDefault(o)) + result = PBS_DEFAULTED; + return result; +} + +unsigned RenderThemeWin::determineSpinButtonState(RenderObject* o, ControlSubPart subPart) +{ + bool isUpButton = subPart == SpinButtonUp; + unsigned result = isUpButton ? UPS_NORMAL : DNS_NORMAL; + if (!isEnabled(o) || isReadOnlyControl(o)) + result = isUpButton ? UPS_DISABLED : DNS_DISABLED; + else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o)) + result = isUpButton ? UPS_PRESSED : DNS_PRESSED; + else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o)) + result = isUpButton ? UPS_HOT : DNS_HOT; + return result; +} + +ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o, ControlSubPart subPart) +{ + ThemeData result; + switch (o->style()->appearance()) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + case CheckboxPart: + case RadioPart: + result.m_part = DFC_BUTTON; + result.m_state = determineClassicState(o); + break; + case MenulistPart: + result.m_part = DFC_SCROLL; + result.m_state = determineClassicState(o); + break; + case SearchFieldPart: + case TextFieldPart: + case TextAreaPart: + result.m_part = TFP_TEXTFIELD; + result.m_state = determineState(o); + break; + case SliderHorizontalPart: + result.m_part = TKP_TRACK; + result.m_state = TS_NORMAL; + break; + case SliderVerticalPart: + result.m_part = TKP_TRACKVERT; + result.m_state = TS_NORMAL; + break; + case SliderThumbHorizontalPart: + result.m_part = TKP_THUMBBOTTOM; + result.m_state = determineSliderThumbState(o); + break; + case SliderThumbVerticalPart: + result.m_part = TKP_THUMBRIGHT; + result.m_state = determineSliderThumbState(o); + break; + case InnerSpinButtonPart: + result.m_part = DFC_SCROLL; + result.m_state = determineClassicState(o, subPart); + break; + default: + break; + } + return result; +} + +ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart) +{ + if (!haveTheme) + return getClassicThemeData(o, subPart); + + ThemeData result; + switch (o->style()->appearance()) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + result.m_part = BP_BUTTON; + result.m_state = determineButtonState(o); + break; + case CheckboxPart: + result.m_part = BP_CHECKBOX; + result.m_state = determineState(o); + break; + case MenulistPart: + case MenulistButtonPart: + result.m_part = isRunningOnVistaOrLater() ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON; + if (isRunningOnVistaOrLater() && documentIsInApplicationChromeMode(o->document())) { + // The "readonly" look we use in application chrome mode + // only uses a "normal" look for the drop down button. + result.m_state = TS_NORMAL; + } else + result.m_state = determineState(o); + break; + case RadioPart: + result.m_part = BP_RADIO; + result.m_state = determineState(o); + break; + case SearchFieldPart: + case TextFieldPart: + case TextAreaPart: + result.m_part = isRunningOnVistaOrLater() ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD; + result.m_state = determineState(o); + break; + case SliderHorizontalPart: + result.m_part = TKP_TRACK; + result.m_state = TS_NORMAL; + break; + case SliderVerticalPart: + result.m_part = TKP_TRACKVERT; + result.m_state = TS_NORMAL; + break; + case SliderThumbHorizontalPart: + result.m_part = TKP_THUMBBOTTOM; + result.m_state = determineSliderThumbState(o); + break; + case SliderThumbVerticalPart: + result.m_part = TKP_THUMBRIGHT; + result.m_state = determineSliderThumbState(o); + break; + case InnerSpinButtonPart: + result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN; + result.m_state = determineSpinButtonState(o, subPart); + break; + } + + return result; +} + +static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r) +{ + bool alphaBlend = false; + if (theme) + alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state); + LocalWindowsContext windowsContext(context, r, alphaBlend); + RECT widgetRect = r; + if (theme) + DrawThemeBackground(theme, windowsContext.hdc(), themeData.m_part, themeData.m_state, &widgetRect, 0); + else { + HDC hdc = windowsContext.hdc(); + if (themeData.m_part == TFP_TEXTFIELD) { + ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY) + ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1)); + else + ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1)); + } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) { + ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH)); + } else if ((o->style()->appearance() == SliderThumbHorizontalPart || + o->style()->appearance() == SliderThumbVerticalPart) && + (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP || + themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) { + ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST); + if (themeData.m_state == TUS_DISABLED) { + static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}; + HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits); + if (patternBmp) { + HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp); + COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE)); + COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); + POINT p; + ::GetViewportOrgEx(hdc, &p); + ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL); + HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush); + ::FillRect(hdc, &widgetRect, brush); + ::SetTextColor(hdc, oldForeColor); + ::SetBkColor(hdc, oldBackColor); + ::SelectObject(hdc, oldBrush); + ::DeleteObject(brush); + } else + ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT); + ::DeleteObject(patternBmp); + } + } else { + // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists. + if (o->style()->appearance() == DefaultButtonPart) { + HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW); + ::FrameRect(hdc, &widgetRect, brush); + ::InflateRect(&widgetRect, -1, -1); + ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE); + } + ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state); + } + } +} + +bool RenderThemeWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + drawControl(i.context, o, buttonTheme(), getThemeData(o), r); + return false; +} + +void RenderThemeWin::adjustInnerSpinButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + int width = ::GetSystemMetrics(SM_CXVSCROLL); + if (width <= 0) + width = 17; // Vista's default. + style->setWidth(Length(width, Fixed)); + style->setMinWidth(Length(width, Fixed)); +} + +bool RenderThemeWin::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + // We split the specified rectangle into two vertically. We can't draw a + // spin button of which height is less than 2px. + if (r.height() < 2) + return false; + IntRect upRect(r); + upRect.setHeight(r.height() / 2); + IntRect downRect(r); + downRect.setY(upRect.bottom()); + downRect.setHeight(r.height() - upRect.height()); + drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonUp), upRect); + drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonDown), downRect); + return false; +} + +void RenderThemeWin::setCheckboxSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. + // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for + // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's + // metrics. + if (style->width().isIntrinsicOrAuto()) + style->setWidth(Length(13, Fixed)); + if (style->height().isAuto()) + style->setHeight(Length(13, Fixed)); +} + +bool RenderThemeWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + drawControl(i.context, o, textFieldTheme(), getThemeData(o), r); + return false; +} + +bool RenderThemeWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + HANDLE theme; + int part; + if (haveTheme && isRunningOnVistaOrLater()) { + theme = menuListTheme(); + if (documentIsInApplicationChromeMode(o->document())) + part = CP_READONLY; + else + part = CP_BORDER; + } else { + theme = textFieldTheme(); + part = TFP_TEXTFIELD; + } + + drawControl(i.context, o, theme, ThemeData(part, determineState(o)), r); + + return paintMenuListButton(o, i, r); +} + +void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->resetBorder(); + adjustMenuListButtonStyle(selector, style, e); +} + +void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // These are the paddings needed to place the text correctly in the <select> box + const int dropDownBoxPaddingTop = 2; + const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4; + const int dropDownBoxPaddingBottom = 2; + const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth; + // The <select> box must be at least 12px high for the button to render nicely on Windows + const int dropDownBoxMinHeight = 12; + + // Position the text correctly within the select box and make the box wide enough to fit the dropdown button + style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed)); + style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed)); + style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed)); + style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed)); + + // Height is locked to auto + style->setHeight(Length(Auto)); + + // Calculate our min-height + int minHeight = style->font().height(); + minHeight = max(minHeight, dropDownBoxMinHeight); + + style->setMinHeight(Length(minHeight, Fixed)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); +} + +bool RenderThemeWin::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border. + int borderThickness = haveTheme ? 1 : 2; + + // Paint the dropdown button on the inner edge of the text field, + // leaving space for the text field's 1px border + IntRect buttonRect(r); + buttonRect.inflate(-borderThickness); + if (o->style()->direction() == LTR) + buttonRect.setX(buttonRect.right() - dropDownButtonWidth); + buttonRect.setWidth(dropDownButtonWidth); + + if (isRunningOnVistaOrLater()) { + // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border. + buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset); + buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset); + buttonRect.setWidth(buttonRect.width() + vistaMenuListButtonOutset); + } + + drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect); + + return false; +} + +const int trackWidth = 4; + +bool RenderThemeWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + IntRect bounds = r; + + if (o->style()->appearance() == SliderHorizontalPart) { + bounds.setHeight(trackWidth); + bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); + } else if (o->style()->appearance() == SliderVerticalPart) { + bounds.setWidth(trackWidth); + bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); + } + + drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds); + return false; +} + +bool RenderThemeWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + drawControl(i.context, o, sliderTheme(), getThemeData(o), r); + return false; +} + +const int sliderThumbWidth = 7; +const int sliderThumbHeight = 15; + +void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const +{ + ControlPart part = o->style()->appearance(); + if (part == SliderThumbVerticalPart) { + o->style()->setWidth(Length(sliderThumbHeight, Fixed)); + o->style()->setHeight(Length(sliderThumbWidth, Fixed)); + } else if (part == SliderThumbHorizontalPart) { + o->style()->setWidth(Length(sliderThumbWidth, Fixed)); + o->style()->setHeight(Length(sliderThumbHeight, Fixed)); + } +#if ENABLE(VIDEO) + else if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) + RenderMediaControls::adjustMediaSliderThumbSize(o); +#endif +} + +bool RenderThemeWin::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + return paintTextField(o, i, r); +} + +void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // Override paddingSize to match AppKit text positioning. + const int padding = 1; + style->setPaddingLeft(Length(padding, Fixed)); + style->setPaddingRight(Length(padding, Fixed)); + style->setPaddingTop(Length(padding, Fixed)); + style->setPaddingBottom(Length(padding, Fixed)); + if (e && e->focused() && e->document()->frame()->selection()->isFocusedAndActive()) + style->setOutlineOffset(-2); +} + +bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = r; + ASSERT(o->parent()); + if (!o->parent() || !o->parent()->isBox()) + return false; + + RenderBox* parentRenderBox = toRenderBox(o->parent()); + + IntRect parentBox = parentRenderBox->absoluteContentBox(); + + // Make sure the scaled button stays square and will fit in its parent's box + bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); + bounds.setWidth(bounds.height()); + + // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will + // be one pixel closer to the bottom of the field. This tends to look better with the text. + bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); + + static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); + static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); + paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds); + return false; +} + +void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // Scale the button size based on the font size + float fontScale = style->fontSize() / defaultControlFontPixelSize; + int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); + style->setWidth(Length(cancelButtonSize, Fixed)); + style->setHeight(Length(cancelButtonSize, Fixed)); +} + +void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize emptySize(1, 11); + style->setWidth(Length(emptySize.width(), Fixed)); + style->setHeight(Length(emptySize.height(), Fixed)); +} + +void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // Scale the decoration size based on the font size + float fontScale = style->fontSize() / defaultControlFontPixelSize; + int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), + maxSearchFieldResultsDecorationSize)); + style->setWidth(Length(magnifierSize, Fixed)); + style->setHeight(Length(magnifierSize, Fixed)); +} + +bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = r; + ASSERT(o->parent()); + if (!o->parent() || !o->parent()->isBox()) + return false; + + RenderBox* parentRenderBox = toRenderBox(o->parent()); + IntRect parentBox = parentRenderBox->absoluteContentBox(); + + // Make sure the scaled decoration stays square and will fit in its parent's box + bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height()))); + bounds.setWidth(bounds.height()); + + // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will + // be one pixel closer to the bottom of the field. This tends to look better with the text. + bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); + + static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); + paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); + return false; +} + +void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // Scale the button size based on the font size + float fontScale = style->fontSize() / defaultControlFontPixelSize; + int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), + maxSearchFieldResultsDecorationSize)); + int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); + style->setWidth(Length(magnifierWidth, Fixed)); + style->setHeight(Length(magnifierHeight, Fixed)); +} + +bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = r; + ASSERT(o->parent()); + if (!o->parent()) + return false; + if (!o->parent() || !o->parent()->isBox()) + return false; + + RenderBox* parentRenderBox = toRenderBox(o->parent()); + IntRect parentBox = parentRenderBox->absoluteContentBox(); + + // Make sure the scaled decoration will fit in its parent's box + bounds.setHeight(min(parentBox.height(), bounds.height())); + bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize))); + + // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will + // be one pixel closer to the bottom of the field. This tends to look better with the text. + bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); + + static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); + paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds); + return false; +} + +// Map a CSSValue* system color to an index understood by GetSysColor +static int cssValueIdToSysColorIndex(int cssValueId) +{ + switch (cssValueId) { + case CSSValueActiveborder: return COLOR_ACTIVEBORDER; + case CSSValueActivecaption: return COLOR_ACTIVECAPTION; + case CSSValueAppworkspace: return COLOR_APPWORKSPACE; + case CSSValueBackground: return COLOR_BACKGROUND; + case CSSValueButtonface: return COLOR_BTNFACE; + case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; + case CSSValueButtonshadow: return COLOR_BTNSHADOW; + case CSSValueButtontext: return COLOR_BTNTEXT; + case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; + case CSSValueGraytext: return COLOR_GRAYTEXT; + case CSSValueHighlight: return COLOR_HIGHLIGHT; + case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; + case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; + case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; + case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; + case CSSValueInfobackground: return COLOR_INFOBK; + case CSSValueInfotext: return COLOR_INFOTEXT; + case CSSValueMenu: return COLOR_MENU; + case CSSValueMenutext: return COLOR_MENUTEXT; + case CSSValueScrollbar: return COLOR_SCROLLBAR; + case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; + case CSSValueThreedface: return COLOR_3DFACE; + case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; + case CSSValueThreedlightshadow: return COLOR_3DLIGHT; + case CSSValueThreedshadow: return COLOR_3DSHADOW; + case CSSValueWindow: return COLOR_WINDOW; + case CSSValueWindowframe: return COLOR_WINDOWFRAME; + case CSSValueWindowtext: return COLOR_WINDOWTEXT; + default: return -1; // Unsupported CSSValue + } +} + +Color RenderThemeWin::systemColor(int cssValueId) const +{ + int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); + if (sysColorIndex == -1) + return RenderTheme::systemColor(cssValueId); + + COLORREF color = GetSysColor(sysColorIndex); + return Color(GetRValue(color), GetGValue(color), GetBValue(color)); +} + +#if ENABLE(VIDEO) + +String RenderThemeWin::extraMediaControlsStyleSheet() +{ + return String(mediaControlsQuickTimeUserAgentStyleSheet, sizeof(mediaControlsQuickTimeUserAgentStyleSheet)); +} + +bool RenderThemeWin::shouldRenderMediaControlPart(ControlPart part, Element* element) +{ + if (part == MediaToggleClosedCaptionsButtonPart) { + // We rely on QuickTime to render captions so only enable the button for a video element. +#if SAFARI_THEME_VERSION >= 4 + if (!element->hasTagName(videoTag)) + return false; +#else + return false; +#endif + } + + return RenderTheme::shouldRenderMediaControlPart(part, element); +} + + +bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaRewindButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaRewindButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaTimelineContainer, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaVolumeSliderContainer(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderContainer, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaVolumeSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaVolumeSlider, o, paintInfo, r); +} + +bool RenderThemeWin::paintMediaVolumeSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderThumb, o, paintInfo, r); +} + +IntPoint RenderThemeWin::volumeSliderOffsetFromMuteButton(Node* muteButton, const IntSize& size) const +{ + return RenderMediaControls::volumeSliderOffsetFromMuteButton(muteButton, size); +} + + +#endif + +} |