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