blob: cfd6e4f1a626c98b2ee314522ec7d2f0cf9491f2 [file] [log] [blame]
/*
* 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 <cairo-win32.h>
#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 DefaultButtonAppearance:
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<HBRUSH>(((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;
}
}