/* * This file is part of the WebKit project. * * Copyright (C) 2006 Apple Computer, Inc. * * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "RenderThemeWin.h" #include #include "Document.h" #include "GraphicsContext.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 */ #define THEME_COLOR 204 #define THEME_FONT 210 // 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 TFS_READONLY 6 // Combobox constants #define CP_DROPDOWNBUTTON 1 typedef HANDLE (WINAPI*openThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList); typedef HRESULT (WINAPI*closeThemeDataPtr)(HANDLE hTheme); typedef HRESULT (WINAPI*drawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT* pClipRect); typedef HRESULT (WINAPI*drawThemeEdgePtr)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, unsigned uEdge, unsigned uFlags, const RECT* pClipRect); typedef HRESULT (WINAPI*getThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, RECT* pContentRect); typedef HRESULT (WINAPI*getThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, RECT* prc, int ts, SIZE* psz); typedef HRESULT (WINAPI*getThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont); typedef HRESULT (WINAPI*getThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId, int iStateId, int iPropId, OUT COLORREF* pFont); static openThemeDataPtr openTheme = 0; static closeThemeDataPtr closeTheme = 0; static drawThemeBackgroundPtr drawThemeBG = 0; static drawThemeEdgePtr drawThemeEdge = 0; static getThemeContentRectPtr getThemeContentRect = 0; static getThemePartSizePtr getThemePartSize = 0; static getThemeSysFontPtr getThemeSysFont = 0; static getThemeColorPtr getThemeColor = 0; namespace WebCore { RenderTheme* theme() { static RenderThemeWin winTheme; return &winTheme; } RenderThemeWin::RenderThemeWin() :m_themeDLL(0), m_buttonTheme(0), m_textFieldTheme(0), m_menuListTheme(0) { m_themeDLL = ::LoadLibrary(L"uxtheme.dll"); if (m_themeDLL) { openTheme = (openThemeDataPtr)GetProcAddress(m_themeDLL, "OpenThemeData"); closeTheme = (closeThemeDataPtr)GetProcAddress(m_themeDLL, "CloseThemeData"); drawThemeBG = (drawThemeBackgroundPtr)GetProcAddress(m_themeDLL, "DrawThemeBackground"); drawThemeEdge = (drawThemeEdgePtr)GetProcAddress(m_themeDLL, "DrawThemeEdge"); getThemeContentRect = (getThemeContentRectPtr)GetProcAddress(m_themeDLL, "GetThemeBackgroundContentRect"); getThemePartSize = (getThemePartSizePtr)GetProcAddress(m_themeDLL, "GetThemePartSize"); getThemeSysFont = (getThemeSysFontPtr)GetProcAddress(m_themeDLL, "GetThemeSysFont"); getThemeColor = (getThemeColorPtr)GetProcAddress(m_themeDLL, "GetThemeColor"); } } RenderThemeWin::~RenderThemeWin() { if (!m_themeDLL) return; close(); ::FreeLibrary(m_themeDLL); } void RenderThemeWin::close() { // This method will need to be called when the OS theme changes to flush our cached themes. if (m_buttonTheme) closeTheme(m_buttonTheme); if (m_textFieldTheme) closeTheme(m_textFieldTheme); if (m_menuListTheme) closeTheme(m_menuListTheme); m_buttonTheme = m_textFieldTheme = m_menuListTheme = 0; } Color RenderThemeWin::platformActiveSelectionBackgroundColor() const { COLORREF color = GetSysColor(COLOR_HIGHLIGHT); return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); } Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const { COLORREF color = GetSysColor(COLOR_GRAYTEXT); return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); } Color RenderThemeWin::platformActiveSelectionForegroundColor() const { COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); } Color RenderThemeWin::platformInactiveSelectionForegroundColor() const { return Color::white; } bool RenderThemeWin::supportsFocus(EAppearance appearance) { switch (appearance) { case PushButtonAppearance: case ButtonAppearance: case TextFieldAppearance: case TextAreaAppearance: return true; default: return false; } return false; } unsigned RenderThemeWin::determineState(RenderObject* o) { unsigned result = TS_NORMAL; if (!isEnabled(o)) result = TS_DISABLED; else if (isReadOnlyControl(o)) result = TFS_READONLY; // Readonly is supported on textfields. else if (supportsFocus(o->style()->appearance()) && isFocused(o)) result = TS_FOCUSED; else if (isPressed(o)) // Active overrides hover. result = TS_ACTIVE; else if (isHovered(o)) result = TS_HOVER; if (isChecked(o)) result += 4; // 4 unchecked states, 4 checked states. return result; } unsigned RenderThemeWin::determineClassicState(RenderObject* o) { unsigned result = 0; if (!isEnabled(o) || isReadOnlyControl(o)) result = DFCS_INACTIVE; else if (isPressed(o)) // Active supersedes hover result = DFCS_PUSHED; else if (isHovered(o)) result = DFCS_HOT; if (isChecked(o)) result |= DFCS_CHECKED; return result; } ThemeData RenderThemeWin::getThemeData(RenderObject* o) { ThemeData result; switch (o->style()->appearance()) { case PushButtonAppearance: case ButtonAppearance: result.m_part = BP_BUTTON; result.m_classicState = DFCS_BUTTONPUSH; break; case CheckboxAppearance: result.m_part = BP_CHECKBOX; result.m_classicState = DFCS_BUTTONCHECK; break; case RadioAppearance: result.m_part = BP_RADIO; result.m_classicState = DFCS_BUTTONRADIO; break; case ListboxAppearance: case MenulistAppearance: case TextFieldAppearance: case TextAreaAppearance: result.m_part = TFP_TEXTFIELD; break; } result.m_state = determineState(o); result.m_classicState |= determineClassicState(o); return result; } // May need to add stuff to these later, so keep the graphics context retrieval/release in some helpers. static HDC prepareForDrawing(GraphicsContext* g) { return g->getWindowsContext(); } static void doneDrawing(GraphicsContext* g, HDC hdc) { g->releaseWindowsContext(hdc); } bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { // Get the correct theme data for a button ThemeData themeData = getThemeData(o); // Now paint the button. HDC hdc = prepareForDrawing(i.context); RECT widgetRect = r; if (m_themeDLL && !m_buttonTheme) m_buttonTheme = openTheme(0, L"Button"); if (m_buttonTheme && drawThemeBG) { drawThemeBG(m_buttonTheme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); } else { if ((themeData.m_part == BP_BUTTON) && isFocused(o)) { // Draw black focus rect around button outer edge HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW); if (brush) { FrameRect(hdc, &widgetRect, brush); InflateRect(&widgetRect, -1, -1); } } DrawFrameControl(hdc, &widgetRect, DFC_BUTTON, themeData.m_classicState); if ((themeData.m_part != BP_BUTTON) && isFocused(o)) { DrawFocusRect(hdc, &widgetRect); } } doneDrawing(i.context, hdc); 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 RenderObject::PaintInfo& i, const IntRect& r) { // Get the correct theme data for a textfield ThemeData themeData = getThemeData(o); // Now paint the text field. HDC hdc = prepareForDrawing(i.context); RECT widgetRect = r; if (m_themeDLL && !m_textFieldTheme) m_textFieldTheme = openTheme(0, L"Edit"); if (m_textFieldTheme && drawThemeBG) { drawThemeBG(m_textFieldTheme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); } else { DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); FillRect(hdc, &widgetRect, reinterpret_cast(((themeData.m_classicState & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW) + 1)); } doneDrawing(i.context, hdc); return false; } void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { // Height is locked to auto. style->setHeight(Length(Auto)); // White-space is locked to pre style->setWhiteSpace(PRE); // Add in the padding that we'd like to use. const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL); style->setPaddingLeft(Length(2, Fixed)); style->setPaddingRight(Length(buttonWidth + 2, Fixed)); style->setPaddingTop(Length(1, Fixed)); style->setPaddingBottom(Length(1, Fixed)); } bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { // FIXME: All these inflate() calls are bogus, causing painting problems, // as well as sizing wackiness in Classic mode IntRect editRect(r); paintTextField(o, i, editRect); const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL); IntRect buttonRect(r.right() - buttonWidth - 1, r.y(), buttonWidth, r.height()); buttonRect.inflateY(-1); paintMenuListButton(o, i, buttonRect); return false; } bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) { HDC hdc = prepareForDrawing(i.context); RECT widgetRect = r; if (m_themeDLL && !m_menuListTheme) m_menuListTheme = openTheme(0, L"Combobox"); if (m_menuListTheme && drawThemeBG) drawThemeBG(m_menuListTheme, hdc, CP_DROPDOWNBUTTON, determineState(o), &widgetRect, NULL); else DrawFrameControl(hdc, &widgetRect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | determineClassicState(o)); doneDrawing(i.context, hdc); return false; } }