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