1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/native_theme/native_theme_win.h"
6
7#include <windows.h>
8#include <uxtheme.h>
9#include <vsstyle.h>
10#include <vssym32.h>
11
12#include "base/basictypes.h"
13#include "base/logging.h"
14#include "base/memory/scoped_handle.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/win/scoped_gdi_object.h"
17#include "base/win/scoped_hdc.h"
18#include "base/win/scoped_select_object.h"
19#include "base/win/windows_version.h"
20#include "skia/ext/bitmap_platform_device.h"
21#include "skia/ext/platform_canvas.h"
22#include "skia/ext/skia_utils_win.h"
23#include "third_party/skia/include/core/SkCanvas.h"
24#include "third_party/skia/include/core/SkColorPriv.h"
25#include "third_party/skia/include/core/SkShader.h"
26#include "ui/base/win/dpi.h"
27#include "ui/gfx/color_utils.h"
28#include "ui/gfx/gdi_util.h"
29#include "ui/gfx/rect.h"
30#include "ui/gfx/rect_conversions.h"
31#include "ui/gfx/sys_color_change_listener.h"
32#include "ui/native_theme/common_theme.h"
33
34// This was removed from Winvers.h but is still used.
35#if !defined(COLOR_MENUHIGHLIGHT)
36#define COLOR_MENUHIGHLIGHT 29
37#endif
38
39namespace {
40
41// TODO: Obtain the correct colors using GetSysColor.
42// Theme colors returned by GetSystemColor().
43const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
44// Dialogs:
45const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
46// FocusableBorder:
47const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
48const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
49// Button:
50const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
51const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
52const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117);
53// MenuItem:
54const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117);
55const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
56const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
57const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
58// Table:
59const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240);
60
61// Windows system color IDs cached and updated by the native theme.
62const int kSystemColors[] = {
63  COLOR_3DFACE,
64  COLOR_BTNTEXT,
65  COLOR_GRAYTEXT,
66  COLOR_HIGHLIGHT,
67  COLOR_HIGHLIGHTTEXT,
68  COLOR_SCROLLBAR,
69  COLOR_WINDOW,
70  COLOR_WINDOWTEXT,
71  COLOR_BTNFACE,
72  COLOR_MENUHIGHLIGHT,
73};
74
75void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
76  // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
77  const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE);
78  const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT);
79  SkColor buffer[] = { face, highlight, highlight, face };
80  // Confusing bit: we first create a temporary bitmap with our desired pattern,
81  // then copy it to another bitmap.  The temporary bitmap doesn't take
82  // ownership of the pixel data, and so will point to garbage when this
83  // function returns.  The copy will copy the pixel data into a place owned by
84  // the bitmap, which is in turn owned by the shader, etc., so it will live
85  // until we're done using it.
86  SkBitmap temp_bitmap;
87  temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
88  temp_bitmap.setPixels(buffer);
89  SkBitmap bitmap;
90  temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
91  skia::RefPtr<SkShader> shader = skia::AdoptRef(
92      SkShader::CreateBitmapShader(
93          bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
94
95  // Align the pattern with the upper corner of |align_rect|.
96  SkMatrix matrix;
97  matrix.setTranslate(SkIntToScalar(align_rect.left),
98                      SkIntToScalar(align_rect.top));
99  shader->setLocalMatrix(matrix);
100  paint->setShader(shader.get());
101}
102
103//    <-a->
104// [  *****             ]
105//  ____ |              |
106//  <-a-> <------b----->
107// a: object_width
108// b: frame_width
109// *: animating object
110//
111// - the animation goes from "[" to "]" repeatedly.
112// - the animation offset is at first "|"
113//
114int ComputeAnimationProgress(int frame_width,
115                             int object_width,
116                             int pixels_per_second,
117                             double animated_seconds) {
118  int animation_width = frame_width + object_width;
119  double interval = static_cast<double>(animation_width) / pixels_per_second;
120  double ratio = fmod(animated_seconds, interval) / interval;
121  return static_cast<int>(animation_width * ratio) - object_width;
122}
123
124RECT InsetRect(const RECT* rect, int size) {
125  gfx::Rect result(*rect);
126  result.Inset(size, size);
127  return result.ToRECT();
128}
129
130// Returns true if using a high contrast theme.
131bool UsingHighContrastTheme() {
132  HIGHCONTRAST result;
133  result.cbSize = sizeof(HIGHCONTRAST);
134  return SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
135      (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
136}
137
138}  // namespace
139
140namespace ui {
141
142bool NativeThemeWin::IsThemingActive() const {
143  if (is_theme_active_)
144    return !!is_theme_active_();
145  return false;
146}
147
148HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
149                                      int part_id,
150                                      int state_id,
151                                      int prop_id,
152                                      SkColor* color) const {
153  HANDLE handle = GetThemeHandle(theme);
154  if (handle && get_theme_color_) {
155    COLORREF color_ref;
156    if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
157        S_OK) {
158      *color = skia::COLORREFToSkColor(color_ref);
159      return S_OK;
160    }
161  }
162  return E_NOTIMPL;
163}
164
165SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
166                                                 int part_id,
167                                                 int state_id,
168                                                 int prop_id,
169                                                 int default_sys_color) const {
170  SkColor color;
171  if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
172    color = color_utils::GetSysSkColor(default_sys_color);
173  return color;
174}
175
176gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
177  // For simplicity use the wildcard state==0, part==0, since it works
178  // for the cases we currently depend on.
179  int border;
180  if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
181    return gfx::Size(border, border);
182  else
183    return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
184}
185
186void NativeThemeWin::DisableTheming() const {
187  if (!set_theme_properties_)
188    return;
189  set_theme_properties_(0);
190}
191
192void NativeThemeWin::CloseHandles() const {
193  if (!close_theme_)
194    return;
195
196  for (int i = 0; i < LAST; ++i) {
197    if (theme_handles_[i]) {
198      close_theme_(theme_handles_[i]);
199      theme_handles_[i] = NULL;
200    }
201  }
202}
203
204bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
205  if (!theme_dll_)
206    return true;
207
208  return !GetThemeHandle(name);
209}
210
211// TODO(sky): seems like we should default to NativeThemeWin, but that currently
212// breaks a couple of tests (FocusTraversalTest.NormalTraversal in
213// views_unittests).
214#if !defined(USE_AURA)
215// static
216NativeTheme* NativeTheme::instance() {
217  return NativeThemeWin::instance();
218}
219#endif
220
221// static
222NativeThemeWin* NativeThemeWin::instance() {
223  CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
224  return &s_native_theme;
225}
226
227gfx::Size NativeThemeWin::GetPartSize(Part part,
228                                      State state,
229                                      const ExtraParams& extra) const {
230  gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
231  if (!part_size.IsEmpty())
232    return part_size;
233
234  // The GetThemePartSize call below returns the default size without
235  // accounting for user customization (crbug/218291).
236  SIZE size;
237  switch (part) {
238    case kScrollbarDownArrow:
239    case kScrollbarLeftArrow:
240    case kScrollbarRightArrow:
241    case kScrollbarUpArrow:
242    case kScrollbarHorizontalThumb:
243    case kScrollbarVerticalThumb:
244    case kScrollbarHorizontalTrack:
245    case kScrollbarVerticalTrack:
246      size.cx = size.cy = ui::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
247      return gfx::Size(size.cx, size.cy);
248  }
249
250  int part_id = GetWindowsPart(part, state, extra);
251  int state_id = GetWindowsState(part, state, extra);
252
253  HDC hdc = GetDC(NULL);
254  HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
255                                NULL, TS_TRUE, &size);
256  ReleaseDC(NULL, hdc);
257
258  if (FAILED(hr)) {
259    // TODO(rogerta): For now, we need to support radio buttons and checkboxes
260    // when theming is not enabled.  Support for other parts can be added
261    // if/when needed.
262    switch (part) {
263      case kCheckbox:
264      case kRadio:
265        // TODO(rogerta): I was not able to find any API to get the default
266        // size of these controls, so determined these values empirically.
267        size.cx = 13;
268        size.cy = 13;
269        break;
270      default:
271        size.cx = 0;
272        size.cy = 0;
273        break;
274    }
275  }
276
277  return gfx::Size(size.cx, size.cy);
278}
279
280void NativeThemeWin::Paint(SkCanvas* canvas,
281                           Part part,
282                           State state,
283                           const gfx::Rect& rect,
284                           const ExtraParams& extra) const {
285  if (rect.IsEmpty())
286    return;
287
288  switch (part) {
289    case kMenuPopupGutter:
290      CommonThemePaintMenuGutter(canvas, rect);
291      return;
292    case kMenuPopupSeparator:
293      CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
294      return;
295    case kMenuPopupBackground:
296      CommonThemePaintMenuBackground(canvas, rect);
297      return;
298    case kMenuItemBackground:
299      CommonThemePaintMenuItemBackground(canvas, state, rect);
300      return;
301  }
302
303  bool needs_paint_indirect = false;
304  if (!skia::SupportsPlatformPaint(canvas)) {
305    // This block will only get hit with --enable-accelerated-drawing flag.
306    needs_paint_indirect = true;
307  } else {
308    // Scrollbar components on Windows Classic theme (on all Windows versions)
309    // have particularly problematic alpha values, so always draw them
310    // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
311    // theme (available only on Windows XP) also need their alpha values
312    // fixed.
313    switch (part) {
314      case kScrollbarDownArrow:
315      case kScrollbarUpArrow:
316      case kScrollbarLeftArrow:
317      case kScrollbarRightArrow:
318        if (!GetThemeHandle(SCROLLBAR))
319          needs_paint_indirect = true;
320        break;
321      case kScrollbarHorizontalThumb:
322      case kScrollbarVerticalThumb:
323      case kScrollbarHorizontalGripper:
324      case kScrollbarVerticalGripper:
325        if (!GetThemeHandle(SCROLLBAR) ||
326            base::win::GetVersion() == base::win::VERSION_XP)
327          needs_paint_indirect = true;
328        break;
329      default:
330        break;
331    }
332  }
333
334  if (needs_paint_indirect)
335    PaintIndirect(canvas, part, state, rect, extra);
336  else
337    PaintDirect(canvas, part, state, rect, extra);
338}
339
340NativeThemeWin::NativeThemeWin()
341    : theme_dll_(LoadLibrary(L"uxtheme.dll")),
342      draw_theme_(NULL),
343      draw_theme_ex_(NULL),
344      get_theme_color_(NULL),
345      get_theme_content_rect_(NULL),
346      get_theme_part_size_(NULL),
347      open_theme_(NULL),
348      close_theme_(NULL),
349      set_theme_properties_(NULL),
350      is_theme_active_(NULL),
351      get_theme_int_(NULL),
352      color_change_listener_(this) {
353  if (theme_dll_) {
354    draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
355        GetProcAddress(theme_dll_, "DrawThemeBackground"));
356    draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
357        GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
358    get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
359        GetProcAddress(theme_dll_, "GetThemeColor"));
360    get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
361        GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
362    get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
363        GetProcAddress(theme_dll_, "GetThemePartSize"));
364    open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
365        GetProcAddress(theme_dll_, "OpenThemeData"));
366    close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
367        GetProcAddress(theme_dll_, "CloseThemeData"));
368    set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
369        GetProcAddress(theme_dll_, "SetThemeAppProperties"));
370    is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
371        GetProcAddress(theme_dll_, "IsThemeActive"));
372    get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
373        GetProcAddress(theme_dll_, "GetThemeInt"));
374  }
375  memset(theme_handles_, 0, sizeof(theme_handles_));
376
377  // Initialize the cached system colors.
378  UpdateSystemColors();
379}
380
381NativeThemeWin::~NativeThemeWin() {
382  if (theme_dll_) {
383    // todo (cpu): fix this soon.  Making a call to CloseHandles() here breaks
384    // certain tests and the reliability bots.
385    // CloseHandles();
386    FreeLibrary(theme_dll_);
387  }
388}
389
390void NativeThemeWin::OnSysColorChange() {
391  UpdateSystemColors();
392}
393
394void NativeThemeWin::UpdateSystemColors() {
395  for (int i = 0; i < arraysize(kSystemColors); ++i) {
396    system_colors_[kSystemColors[i]] =
397        color_utils::GetSysSkColor(kSystemColors[i]);
398  }
399}
400
401void NativeThemeWin::PaintDirect(SkCanvas* canvas,
402                                 Part part,
403                                 State state,
404                                 const gfx::Rect& rect,
405                                 const ExtraParams& extra) const {
406  skia::ScopedPlatformPaint scoped_platform_paint(canvas);
407  HDC hdc = scoped_platform_paint.GetPlatformSurface();
408
409  switch (part) {
410    case kCheckbox:
411      PaintCheckbox(hdc, part, state, rect, extra.button);
412      break;
413    case kRadio:
414      PaintRadioButton(hdc, part, state, rect, extra.button);
415      break;
416    case kPushButton:
417      PaintPushButton(hdc, part, state, rect, extra.button);
418      break;
419    case kMenuPopupArrow:
420      PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
421      break;
422    case kMenuPopupGutter:
423      PaintMenuGutter(hdc, rect);
424      break;
425    case kMenuPopupSeparator:
426      PaintMenuSeparator(hdc, rect, extra.menu_separator);
427      break;
428    case kMenuPopupBackground:
429      PaintMenuBackground(hdc, rect);
430      break;
431    case kMenuCheck:
432      PaintMenuCheck(hdc, state, rect, extra.menu_check);
433      break;
434    case kMenuCheckBackground:
435      PaintMenuCheckBackground(hdc, state, rect);
436      break;
437    case kMenuItemBackground:
438      PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
439      break;
440    case kMenuList:
441      PaintMenuList(hdc, state, rect, extra.menu_list);
442      break;
443    case kScrollbarDownArrow:
444    case kScrollbarUpArrow:
445    case kScrollbarLeftArrow:
446    case kScrollbarRightArrow:
447      PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
448      break;
449    case kScrollbarHorizontalTrack:
450    case kScrollbarVerticalTrack:
451      PaintScrollbarTrack(canvas, hdc, part, state, rect,
452                          extra.scrollbar_track);
453      break;
454    case kScrollbarHorizontalThumb:
455    case kScrollbarVerticalThumb:
456    case kScrollbarHorizontalGripper:
457    case kScrollbarVerticalGripper:
458      PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
459      break;
460    case kInnerSpinButton:
461      PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
462      break;
463    case kTrackbarThumb:
464    case kTrackbarTrack:
465      PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
466      break;
467    case kProgressBar:
468      PaintProgressBar(hdc, rect, extra.progress_bar);
469      break;
470    case kWindowResizeGripper:
471      PaintWindowResizeGripper(hdc, rect);
472      break;
473    case kTabPanelBackground:
474      PaintTabPanelBackground(hdc, rect);
475      break;
476    case kTextField:
477      PaintTextField(hdc, part, state, rect, extra.text_field);
478      break;
479
480    case kSliderTrack:
481    case kSliderThumb:
482    default:
483      // While transitioning NativeThemeWin to the single Paint() entry point,
484      // unsupported parts will DCHECK here.
485      NOTREACHED();
486  }
487}
488
489SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
490  SkColor color;
491  if (CommonThemeGetSystemColor(color_id, &color))
492    return color;
493
494  switch (color_id) {
495    // Windows
496    case kColorId_WindowBackground:
497      return system_colors_[COLOR_WINDOW];
498
499    // Dialogs
500    case kColorId_DialogBackground:
501      if (gfx::IsInvertedColorScheme())
502        return color_utils::InvertColor(kDialogBackgroundColor);
503      return kDialogBackgroundColor;
504
505    // FocusableBorder
506    case kColorId_FocusedBorderColor:
507      return kFocusedBorderColor;
508    case kColorId_UnfocusedBorderColor:
509      return kUnfocusedBorderColor;
510
511    // Button
512    case kColorId_ButtonBackgroundColor:
513      return kButtonBackgroundColor;
514    case kColorId_ButtonEnabledColor:
515      return system_colors_[COLOR_BTNTEXT];
516    case kColorId_ButtonDisabledColor:
517      return system_colors_[COLOR_GRAYTEXT];
518    case kColorId_ButtonHighlightColor:
519      return kButtonHighlightColor;
520    case kColorId_ButtonHoverColor:
521      return kButtonHoverColor;
522
523    // MenuItem
524    case kColorId_EnabledMenuItemForegroundColor:
525      return kEnabledMenuItemForegroundColor;
526    case kColorId_DisabledMenuItemForegroundColor:
527      return kDisabledMenuItemForegroundColor;
528    case kColorId_FocusedMenuItemBackgroundColor:
529      return kFocusedMenuItemBackgroundColor;
530    case kColorId_MenuSeparatorColor:
531      return kMenuSeparatorColor;
532
533    // Label
534    case kColorId_LabelEnabledColor:
535      return system_colors_[COLOR_BTNTEXT];
536    case kColorId_LabelDisabledColor:
537      return system_colors_[COLOR_GRAYTEXT];
538    case kColorId_LabelBackgroundColor:
539      return system_colors_[COLOR_WINDOW];
540
541    // Textfield
542    case kColorId_TextfieldDefaultColor:
543      return system_colors_[COLOR_WINDOWTEXT];
544    case kColorId_TextfieldDefaultBackground:
545      return system_colors_[COLOR_WINDOW];
546    case kColorId_TextfieldReadOnlyColor:
547      return system_colors_[COLOR_GRAYTEXT];
548    case kColorId_TextfieldReadOnlyBackground:
549      return system_colors_[COLOR_3DFACE];
550    case kColorId_TextfieldSelectionColor:
551      return system_colors_[COLOR_HIGHLIGHTTEXT];
552    case kColorId_TextfieldSelectionBackgroundFocused:
553      return system_colors_[COLOR_HIGHLIGHT];
554
555    // Tree
556    // NOTE: these aren't right for all themes, but as close as I could get.
557    case kColorId_TreeBackground:
558      return system_colors_[COLOR_WINDOW];
559    case kColorId_TreeText:
560      return system_colors_[COLOR_WINDOWTEXT];
561    case kColorId_TreeSelectedText:
562      return system_colors_[COLOR_HIGHLIGHTTEXT];
563    case kColorId_TreeSelectedTextUnfocused:
564      return system_colors_[COLOR_BTNTEXT];
565    case kColorId_TreeSelectionBackgroundFocused:
566      return system_colors_[COLOR_HIGHLIGHT];
567    case kColorId_TreeSelectionBackgroundUnfocused:
568      return system_colors_[UsingHighContrastTheme() ?
569                              COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
570    case kColorId_TreeArrow:
571      return system_colors_[COLOR_WINDOWTEXT];
572
573    // Table
574    case kColorId_TableBackground:
575      return system_colors_[COLOR_WINDOW];
576    case kColorId_TableText:
577      return system_colors_[COLOR_WINDOWTEXT];
578    case kColorId_TableSelectedText:
579      return system_colors_[COLOR_HIGHLIGHTTEXT];
580    case kColorId_TableSelectedTextUnfocused:
581      return system_colors_[COLOR_BTNTEXT];
582    case kColorId_TableSelectionBackgroundFocused:
583      return system_colors_[COLOR_HIGHLIGHT];
584    case kColorId_TableSelectionBackgroundUnfocused:
585      return system_colors_[UsingHighContrastTheme() ?
586                              COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
587    case kColorId_TableGroupingIndicatorColor:
588      return system_colors_[COLOR_GRAYTEXT];
589
590    default:
591      NOTREACHED();
592      break;
593  }
594  return kInvalidColorIdColor;
595}
596
597void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
598                                   Part part,
599                                   State state,
600                                   const gfx::Rect& rect,
601                                   const ExtraParams& extra) const {
602  // TODO(asvitkine): This path is pretty inefficient - for each paint operation
603  //                  it creates a new offscreen bitmap Skia canvas. This can
604  //                  be sped up by doing it only once per part/state and
605  //                  keeping a cache of the resulting bitmaps.
606
607  // Create an offscreen canvas that is backed by an HDC.
608  skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
609      skia::BitmapPlatformDevice::Create(
610          rect.width(), rect.height(), false, NULL));
611  DCHECK(device);
612  SkCanvas offscreen_canvas(device.get());
613  DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
614
615  // Some of the Windows theme drawing operations do not write correct alpha
616  // values for fully-opaque pixels; instead the pixels get alpha 0. This is
617  // especially a problem on Windows XP or when using the Classic theme.
618  //
619  // To work-around this, mark all pixels with a placeholder value, to detect
620  // which pixels get touched by the paint operation. After paint, set any
621  // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
622  const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
623  offscreen_canvas.clear(placeholder);
624
625  // Offset destination rects to have origin (0,0).
626  gfx::Rect adjusted_rect(rect.size());
627  ExtraParams adjusted_extra(extra);
628  switch (part) {
629    case kProgressBar:
630      adjusted_extra.progress_bar.value_rect_x = 0;
631      adjusted_extra.progress_bar.value_rect_y = 0;
632      break;
633    case kScrollbarHorizontalTrack:
634    case kScrollbarVerticalTrack:
635      adjusted_extra.scrollbar_track.track_x = 0;
636      adjusted_extra.scrollbar_track.track_y = 0;
637      break;
638    default: break;
639  }
640  // Draw the theme controls using existing HDC-drawing code.
641  PaintDirect(&offscreen_canvas,
642              part,
643              state,
644              adjusted_rect,
645              adjusted_extra);
646
647  // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
648  // necessary to have when drawing to a SkPicture.
649  const SkBitmap& hdc_bitmap =
650      offscreen_canvas.getDevice()->accessBitmap(false);
651  SkBitmap bitmap;
652  hdc_bitmap.copyTo(&bitmap, SkBitmap::kARGB_8888_Config);
653
654  // Post-process the pixels to fix up the alpha values (see big comment above).
655  const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
656  const int pixel_count = rect.width() * rect.height();
657  SkPMColor* pixels = bitmap.getAddr32(0, 0);
658  for (int i = 0; i < pixel_count; i++) {
659    if (pixels[i] == placeholder_value) {
660      // Pixel wasn't touched - make it fully transparent.
661      pixels[i] = SkPackARGB32(0, 0, 0, 0);
662    } else if (SkGetPackedA32(pixels[i]) == 0) {
663      // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
664      pixels[i] = SkPackARGB32(0xFF,
665                               SkGetPackedR32(pixels[i]),
666                               SkGetPackedG32(pixels[i]),
667                               SkGetPackedB32(pixels[i]));
668    }
669  }
670
671  // Draw the offscreen bitmap to the destination canvas.
672  canvas->drawBitmap(bitmap, rect.x(), rect.y());
673}
674
675HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
676                                         HDC hdc,
677                                         int part_id,
678                                         int state_id,
679                                         RECT* rect,
680                                         int ts,
681                                         SIZE* size) const {
682  HANDLE handle = GetThemeHandle(theme_name);
683  if (handle && get_theme_part_size_)
684    return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
685
686  return E_NOTIMPL;
687}
688
689HRESULT NativeThemeWin::PaintButton(HDC hdc,
690                                    State state,
691                                    const ButtonExtraParams& extra,
692                                    int part_id,
693                                    int state_id,
694                                    RECT* rect) const {
695  HANDLE handle = GetThemeHandle(BUTTON);
696  if (handle && draw_theme_)
697    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
698
699  // Adjust classic_state based on part, state, and extras.
700  int classic_state = extra.classic_state;
701  switch (part_id) {
702    case BP_CHECKBOX:
703      classic_state |= DFCS_BUTTONCHECK;
704      break;
705    case BP_RADIOBUTTON:
706      classic_state |= DFCS_BUTTONRADIO;
707      break;
708    case BP_PUSHBUTTON:
709      classic_state |= DFCS_BUTTONPUSH;
710      break;
711    default:
712      NOTREACHED() << "Unknown part_id: " << part_id;
713      break;
714  }
715
716  switch (state) {
717    case kDisabled:
718      classic_state |= DFCS_INACTIVE;
719      break;
720    case kPressed:
721      classic_state |= DFCS_PUSHED;
722      break;
723    case kNormal:
724    case kHovered:
725      break;
726    default:
727      NOTREACHED() << "Unknown state: " << state;
728      break;
729  }
730
731  if (extra.checked)
732    classic_state |= DFCS_CHECKED;
733
734  // Draw it manually.
735  // All pressed states have both low bits set, and no other states do.
736  const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
737  const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
738  if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
739    // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
740    // button itself is shrunk by 1 pixel.
741    HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
742    if (brush) {
743      FrameRect(hdc, rect, brush);
744      InflateRect(rect, -1, -1);
745    }
746  }
747  DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
748
749  // Draw the focus rectangle (the dotted line box) only on buttons.  For radio
750  // and checkboxes, we let webkit draw the focus rectangle (orange glow).
751  if ((BP_PUSHBUTTON == part_id) && focused) {
752    // The focus rect is inside the button.  The exact number of pixels depends
753    // on whether we're in classic mode or using uxtheme.
754    if (handle && get_theme_content_rect_) {
755      get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
756    } else {
757      InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
758                  -GetSystemMetrics(SM_CYEDGE));
759    }
760    DrawFocusRect(hdc, rect);
761  }
762
763  // Classic theme doesn't support indeterminate checkboxes.  We draw
764  // a recangle inside a checkbox like IE10 does.
765  if (part_id == BP_CHECKBOX && extra.indeterminate) {
766    RECT inner_rect = *rect;
767    // "4 / 13" is same as IE10 in classic theme.
768    int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
769    InflateRect(&inner_rect, -padding, -padding);
770    int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
771    FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
772  }
773
774  return S_OK;
775}
776
777HRESULT NativeThemeWin::PaintMenuSeparator(
778    HDC hdc,
779    const gfx::Rect& rect,
780    const MenuSeparatorExtraParams& extra) const {
781  RECT rect_win = rect.ToRECT();
782
783  HANDLE handle = GetThemeHandle(MENU);
784  if (handle && draw_theme_) {
785    // Delta is needed for non-classic to move separator up slightly.
786    --rect_win.top;
787    --rect_win.bottom;
788    return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
789                       NULL);
790  }
791
792  DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
793  return S_OK;
794}
795
796HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
797                                        const gfx::Rect& rect) const {
798  RECT rect_win = rect.ToRECT();
799  HANDLE handle = GetThemeHandle(MENU);
800  if (handle && draw_theme_)
801    return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
802                       NULL);
803  return E_NOTIMPL;
804}
805
806HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
807                                       State state,
808                                       const gfx::Rect& rect,
809                                       const MenuArrowExtraParams& extra)
810    const {
811  int state_id = MSM_NORMAL;
812  if (state == kDisabled)
813    state_id = MSM_DISABLED;
814
815  HANDLE handle = GetThemeHandle(MENU);
816  RECT rect_win = rect.ToRECT();
817  if (handle && draw_theme_) {
818    if (extra.pointing_right) {
819      return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
820                         NULL);
821    } else {
822      // There is no way to tell the uxtheme API to draw a left pointing arrow;
823      // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT.  But they
824      // are needed for RTL locales on Vista.  So use a memory DC and mirror
825      // the region with GDI's StretchBlt.
826      gfx::Rect r(rect);
827      base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
828      base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
829                                                                r.height()));
830      base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
831      // Copy and horizontally mirror the background from hdc into mem_dc. Use
832      // a negative-width source rect, starting at the rightmost pixel.
833      StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
834                 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
835      // Draw the arrow.
836      RECT theme_rect = {0, 0, r.width(), r.height()};
837      HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
838                                   state_id, &theme_rect, NULL);
839      // Copy and mirror the result back into mem_dc.
840      StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
841                 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
842      return result;
843    }
844  }
845
846  // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
847  // left pointing arrow. This makes the following 'if' statement slightly
848  // counterintuitive.
849  UINT pfc_state;
850  if (extra.pointing_right)
851    pfc_state = DFCS_MENUARROW;
852  else
853    pfc_state = DFCS_MENUARROWRIGHT;
854  return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
855                           state);
856}
857
858HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
859                                            const gfx::Rect& rect) const {
860  HANDLE handle = GetThemeHandle(MENU);
861  RECT rect_win = rect.ToRECT();
862  if (handle && draw_theme_) {
863    HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
864                                 &rect_win, NULL);
865    FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
866    return result;
867  }
868
869  FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
870  DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
871  return S_OK;
872}
873
874HRESULT NativeThemeWin::PaintMenuCheck(
875    HDC hdc,
876    State state,
877    const gfx::Rect& rect,
878    const MenuCheckExtraParams& extra) const {
879  HANDLE handle = GetThemeHandle(MENU);
880  int state_id;
881  if (extra.is_radio) {
882    state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
883  } else {
884    state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
885  }
886
887  RECT rect_win = rect.ToRECT();
888  if (handle && draw_theme_)
889    return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
890
891  return PaintFrameControl(hdc, rect, DFC_MENU,
892                           extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
893                           extra.is_selected, state);
894}
895
896HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
897                                                 State state,
898                                                 const gfx::Rect& rect) const {
899  HANDLE handle = GetThemeHandle(MENU);
900  int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
901  RECT rect_win = rect.ToRECT();
902  if (handle && draw_theme_)
903    return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
904                       &rect_win, NULL);
905  // Nothing to do for background.
906  return S_OK;
907}
908
909HRESULT NativeThemeWin::PaintMenuItemBackground(
910    HDC hdc,
911    State state,
912    const gfx::Rect& rect,
913    const MenuItemExtraParams& extra) const {
914  HANDLE handle = GetThemeHandle(MENU);
915  RECT rect_win = rect.ToRECT();
916  int state_id;
917  switch (state) {
918    case kNormal:
919      state_id = MPI_NORMAL;
920      break;
921    case kDisabled:
922      state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
923      break;
924    case kHovered:
925      state_id = MPI_HOT;
926      break;
927    default:
928      NOTREACHED() << "Invalid state " << state;
929      break;
930  }
931
932  if (handle && draw_theme_)
933    return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
934
935  if (extra.is_selected)
936    FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
937  return S_OK;
938}
939
940HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
941                                        Part part,
942                                        State state,
943                                        const gfx::Rect& rect,
944                                        const ButtonExtraParams& extra) const {
945  int state_id;
946  switch (state) {
947    case kDisabled:
948      state_id = PBS_DISABLED;
949      break;
950    case kHovered:
951      state_id = PBS_HOT;
952      break;
953    case kNormal:
954      state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
955      break;
956    case kPressed:
957      state_id = PBS_PRESSED;
958      break;
959    default:
960      NOTREACHED() << "Invalid state: " << state;
961      break;
962  }
963
964  RECT rect_win = rect.ToRECT();
965  return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
966}
967
968HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
969                                         Part part,
970                                         State state,
971                                         const gfx::Rect& rect,
972                                         const ButtonExtraParams& extra) const {
973  int state_id;
974  switch (state) {
975    case kDisabled:
976      state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
977      break;
978    case kHovered:
979      state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
980      break;
981    case kNormal:
982      state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
983      break;
984    case kPressed:
985      state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
986      break;
987    default:
988      NOTREACHED() << "Invalid state: " << state;
989      break;
990  }
991
992  RECT rect_win = rect.ToRECT();
993  return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
994}
995
996HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
997                                      Part part,
998                                      State state,
999                                      const gfx::Rect& rect,
1000                                      const ButtonExtraParams& extra) const {
1001  int state_id;
1002  switch (state) {
1003    case kDisabled:
1004      state_id = extra.checked ? CBS_CHECKEDDISABLED :
1005          extra.indeterminate ? CBS_MIXEDDISABLED :
1006              CBS_UNCHECKEDDISABLED;
1007      break;
1008    case kHovered:
1009      state_id = extra.checked ? CBS_CHECKEDHOT :
1010          extra.indeterminate ? CBS_MIXEDHOT :
1011              CBS_UNCHECKEDHOT;
1012      break;
1013    case kNormal:
1014      state_id = extra.checked ? CBS_CHECKEDNORMAL :
1015          extra.indeterminate ? CBS_MIXEDNORMAL :
1016              CBS_UNCHECKEDNORMAL;
1017      break;
1018    case kPressed:
1019      state_id = extra.checked ? CBS_CHECKEDPRESSED :
1020          extra.indeterminate ? CBS_MIXEDPRESSED :
1021              CBS_UNCHECKEDPRESSED;
1022      break;
1023    default:
1024      NOTREACHED() << "Invalid state: " << state;
1025      break;
1026  }
1027
1028  RECT rect_win = rect.ToRECT();
1029  return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
1030}
1031
1032HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
1033                                      State state,
1034                                      const gfx::Rect& rect,
1035                                      const MenuListExtraParams& extra) const {
1036  HANDLE handle = GetThemeHandle(MENULIST);
1037  RECT rect_win = rect.ToRECT();
1038  int state_id;
1039  switch (state) {
1040    case kNormal:
1041      state_id = CBXS_NORMAL;
1042      break;
1043    case kDisabled:
1044      state_id = CBXS_DISABLED;
1045      break;
1046    case kHovered:
1047      state_id = CBXS_HOT;
1048      break;
1049    case kPressed:
1050      state_id = CBXS_PRESSED;
1051      break;
1052    default:
1053      NOTREACHED() << "Invalid state " << state;
1054      break;
1055  }
1056
1057  if (handle && draw_theme_)
1058    return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
1059                       NULL);
1060
1061  // Draw it manually.
1062  DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
1063                   DFCS_SCROLLCOMBOBOX | extra.classic_state);
1064  return S_OK;
1065}
1066
1067HRESULT NativeThemeWin::PaintScrollbarArrow(
1068    HDC hdc,
1069    Part part,
1070    State state,
1071    const gfx::Rect& rect,
1072    const ScrollbarArrowExtraParams& extra) const {
1073  static const int state_id_matrix[4][kMaxState] = {
1074      ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
1075      ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
1076      ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
1077      ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
1078  };
1079  HANDLE handle = GetThemeHandle(SCROLLBAR);
1080  RECT rect_win = rect.ToRECT();
1081  if (handle && draw_theme_) {
1082    int index = part - kScrollbarDownArrow;
1083    DCHECK(index >=0 && index < 4);
1084    int state_id = state_id_matrix[index][state];
1085
1086    // Hovering means that the cursor is over the scroolbar, but not over the
1087    // specific arrow itself.  We don't want to show it "hot" mode, but only
1088    // in "hover" mode.
1089    if (state == kHovered && extra.is_hovering) {
1090      switch (part) {
1091        case kScrollbarDownArrow:
1092          state_id = ABS_DOWNHOVER;
1093          break;
1094        case kScrollbarLeftArrow:
1095          state_id = ABS_LEFTHOVER;
1096          break;
1097        case kScrollbarRightArrow:
1098          state_id = ABS_RIGHTHOVER;
1099          break;
1100        case kScrollbarUpArrow:
1101          state_id = ABS_UPHOVER;
1102          break;
1103        default:
1104          NOTREACHED() << "Invalid part: " << part;
1105          break;
1106      }
1107    }
1108    return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
1109  }
1110
1111  int classic_state = DFCS_SCROLLDOWN;
1112  switch (part) {
1113    case kScrollbarDownArrow:
1114      classic_state = DFCS_SCROLLDOWN;
1115      break;
1116    case kScrollbarLeftArrow:
1117      classic_state = DFCS_SCROLLLEFT;
1118      break;
1119    case kScrollbarRightArrow:
1120      classic_state = DFCS_SCROLLRIGHT;
1121      break;
1122    case kScrollbarUpArrow:
1123      classic_state = DFCS_SCROLLUP;
1124      break;
1125    default:
1126      NOTREACHED() << "Invalid part: " << part;
1127      break;
1128  }
1129  switch (state) {
1130    case kDisabled:
1131      classic_state |= DFCS_INACTIVE;
1132      break;
1133    case kHovered:
1134      classic_state |= DFCS_HOT;
1135      break;
1136    case kNormal:
1137      break;
1138    case kPressed:
1139      classic_state |= DFCS_PUSHED;
1140      break;
1141    default:
1142      NOTREACHED() << "Invalid state: " << state;
1143      break;
1144  }
1145  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1146  return S_OK;
1147}
1148
1149HRESULT NativeThemeWin::PaintScrollbarThumb(
1150    HDC hdc,
1151    Part part,
1152    State state,
1153    const gfx::Rect& rect,
1154    const ScrollbarThumbExtraParams& extra) const {
1155  HANDLE handle = GetThemeHandle(SCROLLBAR);
1156  RECT rect_win = rect.ToRECT();
1157  int part_id;
1158  int state_id;
1159
1160  switch (part) {
1161    case NativeTheme::kScrollbarHorizontalThumb:
1162      part_id = SBP_THUMBBTNHORZ;
1163      break;
1164    case NativeTheme::kScrollbarVerticalThumb:
1165      part_id = SBP_THUMBBTNVERT;
1166      break;
1167    case NativeTheme::kScrollbarHorizontalGripper:
1168      part_id = SBP_GRIPPERHORZ;
1169      break;
1170    case NativeTheme::kScrollbarVerticalGripper:
1171      part_id = SBP_GRIPPERVERT;
1172      break;
1173    default:
1174      NOTREACHED() << "Invalid part: " << part;
1175      break;
1176  }
1177
1178  switch (state) {
1179    case kDisabled:
1180      state_id = SCRBS_DISABLED;
1181      break;
1182    case kHovered:
1183      state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1184      break;
1185    case kNormal:
1186      state_id = SCRBS_NORMAL;
1187      break;
1188    case kPressed:
1189      state_id = SCRBS_PRESSED;
1190      break;
1191    default:
1192      NOTREACHED() << "Invalid state: " << state;
1193      break;
1194  }
1195
1196  if (handle && draw_theme_)
1197    return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
1198
1199  // Draw it manually.
1200  if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
1201    DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
1202  // Classic mode doesn't have a gripper.
1203  return S_OK;
1204}
1205
1206HRESULT NativeThemeWin::PaintScrollbarTrack(
1207    SkCanvas* canvas,
1208    HDC hdc,
1209    Part part,
1210    State state,
1211    const gfx::Rect& rect,
1212    const ScrollbarTrackExtraParams& extra) const {
1213  HANDLE handle = GetThemeHandle(SCROLLBAR);
1214  RECT rect_win = rect.ToRECT();
1215  int part_id;
1216  int state_id;
1217
1218  switch (part) {
1219    case NativeTheme::kScrollbarHorizontalTrack:
1220      part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
1221      break;
1222    case NativeTheme::kScrollbarVerticalTrack:
1223      part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
1224      break;
1225    default:
1226      NOTREACHED() << "Invalid part: " << part;
1227      break;
1228  }
1229
1230  switch (state) {
1231    case kDisabled:
1232      state_id = SCRBS_DISABLED;
1233      break;
1234    case kHovered:
1235      state_id = SCRBS_HOVER;
1236      break;
1237    case kNormal:
1238      state_id = SCRBS_NORMAL;
1239      break;
1240    case kPressed:
1241      state_id = SCRBS_PRESSED;
1242      break;
1243    default:
1244      NOTREACHED() << "Invalid state: " << state;
1245      break;
1246  }
1247
1248  if (handle && draw_theme_)
1249    return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1250
1251  // Draw it manually.
1252  if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
1253      (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
1254    FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
1255  } else {
1256    SkPaint paint;
1257    RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
1258                                extra.track_height).ToRECT();
1259    SetCheckerboardShader(&paint, align_rect);
1260    canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
1261  }
1262  if (extra.classic_state & DFCS_PUSHED)
1263    InvertRect(hdc, &rect_win);
1264  return S_OK;
1265}
1266
1267HRESULT NativeThemeWin::PaintSpinButton(
1268    HDC hdc,
1269    Part part,
1270    State state,
1271    const gfx::Rect& rect,
1272    const InnerSpinButtonExtraParams& extra) const {
1273  HANDLE handle = GetThemeHandle(SPIN);
1274  RECT rect_win = rect.ToRECT();
1275  int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
1276  int state_id;
1277  switch (state) {
1278    case kDisabled:
1279      state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1280      break;
1281    case kHovered:
1282      state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1283      break;
1284    case kNormal:
1285      state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1286      break;
1287    case kPressed:
1288      state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1289      break;
1290    default:
1291      NOTREACHED() << "Invalid state " << state;
1292      break;
1293  }
1294
1295  if (handle && draw_theme_)
1296    return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1297  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
1298  return S_OK;
1299}
1300
1301HRESULT NativeThemeWin::PaintTrackbar(
1302    SkCanvas* canvas,
1303    HDC hdc,
1304    Part part,
1305    State state,
1306    const gfx::Rect& rect,
1307    const TrackbarExtraParams& extra) const {
1308  int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
1309  if (extra.vertical)
1310    part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
1311
1312  int state_id = 0;
1313  switch (state) {
1314    case kDisabled:
1315      state_id = TUS_DISABLED;
1316      break;
1317    case kHovered:
1318      state_id = TUS_HOT;
1319      break;
1320    case kNormal:
1321      state_id = TUS_NORMAL;
1322      break;
1323    case kPressed:
1324      state_id = TUS_PRESSED;
1325      break;
1326    default:
1327      NOTREACHED() << "Invalid state " << state;
1328      break;
1329  }
1330
1331  // Make the channel be 4 px thick in the center of the supplied rect.  (4 px
1332  // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1333  // return good values here.)
1334  RECT rect_win = rect.ToRECT();
1335  RECT channel_rect = rect.ToRECT();
1336  const int channel_thickness = 4;
1337  if (part_id == TKP_TRACK) {
1338    channel_rect.top +=
1339        ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
1340    channel_rect.bottom = channel_rect.top + channel_thickness;
1341  } else if (part_id == TKP_TRACKVERT) {
1342    channel_rect.left +=
1343        ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
1344    channel_rect.right = channel_rect.left + channel_thickness;
1345  }  // else this isn't actually a channel, so |channel_rect| == |rect|.
1346
1347  HANDLE handle = GetThemeHandle(TRACKBAR);
1348  if (handle && draw_theme_)
1349    return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1350
1351  // Classic mode, draw it manually.
1352  if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
1353    DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
1354  } else if (part_id == TKP_THUMBVERT) {
1355    DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
1356  } else {
1357    // Split rect into top and bottom pieces.
1358    RECT top_section = rect.ToRECT();
1359    RECT bottom_section = rect.ToRECT();
1360    top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
1361    bottom_section.top = top_section.bottom;
1362    DrawEdge(hdc, &top_section, EDGE_RAISED,
1363             BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1364
1365    // Split triangular piece into two diagonals.
1366    RECT& left_half = bottom_section;
1367    RECT right_half = bottom_section;
1368    right_half.left += ((bottom_section.right - bottom_section.left) / 2);
1369    left_half.right = right_half.left;
1370    DrawEdge(hdc, &left_half, EDGE_RAISED,
1371             BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1372    DrawEdge(hdc, &right_half, EDGE_RAISED,
1373             BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1374
1375    // If the button is pressed, draw hatching.
1376    if (extra.classic_state & DFCS_PUSHED) {
1377      SkPaint paint;
1378      SetCheckerboardShader(&paint, rect_win);
1379
1380      // Fill all three pieces with the pattern.
1381      canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1382
1383      SkScalar left_triangle_top = SkIntToScalar(left_half.top);
1384      SkScalar left_triangle_right = SkIntToScalar(left_half.right);
1385      SkPath left_triangle;
1386      left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
1387      left_triangle.lineTo(left_triangle_right, left_triangle_top);
1388      left_triangle.lineTo(left_triangle_right,
1389                           SkIntToScalar(left_half.bottom));
1390      left_triangle.close();
1391      canvas->drawPath(left_triangle, paint);
1392
1393      SkScalar right_triangle_left = SkIntToScalar(right_half.left);
1394      SkScalar right_triangle_top = SkIntToScalar(right_half.top);
1395      SkPath right_triangle;
1396      right_triangle.moveTo(right_triangle_left, right_triangle_top);
1397      right_triangle.lineTo(SkIntToScalar(right_half.right),
1398                            right_triangle_top);
1399      right_triangle.lineTo(right_triangle_left,
1400                            SkIntToScalar(right_half.bottom));
1401      right_triangle.close();
1402      canvas->drawPath(right_triangle, paint);
1403    }
1404  }
1405  return S_OK;
1406}
1407
1408HRESULT NativeThemeWin::PaintProgressBar(
1409    HDC hdc,
1410    const gfx::Rect& rect,
1411    const ProgressBarExtraParams& extra) const {
1412  // There is no documentation about the animation speed, frame-rate, nor
1413  // size of moving overlay of the indeterminate progress bar.
1414  // So we just observed real-world programs and guessed following parameters.
1415  const int kDeteminateOverlayPixelsPerSecond = 300;
1416  const int kDeteminateOverlayWidth = 120;
1417  const int kIndeterminateOverlayPixelsPerSecond =  175;
1418  const int kVistaIndeterminateOverlayWidth = 120;
1419  const int kXPIndeterminateOverlayWidth = 55;
1420  // The thickness of the bar frame inside |value_rect|
1421  const int kXPBarPadding = 3;
1422
1423  RECT bar_rect = rect.ToRECT();
1424  RECT value_rect = gfx::Rect(extra.value_rect_x,
1425                              extra.value_rect_y,
1426                              extra.value_rect_width,
1427                              extra.value_rect_height).ToRECT();
1428
1429  bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
1430  HANDLE handle = GetThemeHandle(PROGRESS);
1431  if (handle && draw_theme_ && draw_theme_ex_) {
1432    draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
1433
1434    int bar_width = bar_rect.right - bar_rect.left;
1435    if (extra.determinate) {
1436      // TODO(morrita): this RTL guess can be wrong.
1437      // We should pass the direction from WebKit side.
1438      bool is_rtl = (bar_rect.right == value_rect.right &&
1439                     bar_rect.left != value_rect.left);
1440      // We should care the direction here because PP_CNUNK painting
1441      // is asymmetric.
1442      DTBGOPTS value_draw_options;
1443      value_draw_options.dwSize = sizeof(DTBGOPTS);
1444      value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
1445      value_draw_options.rcClip = bar_rect;
1446
1447      if (pre_vista) {
1448        // On XP, progress bar is chunk-style and has no glossy effect.
1449        // We need to shrink destination rect to fit the part inside the bar
1450        // with an appropriate margin.
1451        RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
1452        draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
1453                       &shrunk_value_rect, &value_draw_options);
1454      } else  {
1455        // On Vista or later, the progress bar part has a
1456        // single-block value part. It also has glossy effect.
1457        // And the value part has exactly same height as the bar part
1458        // so we don't need to shrink the rect.
1459        draw_theme_ex_(handle, hdc, PP_FILL, 0,
1460                       &value_rect, &value_draw_options);
1461
1462        int dx = ComputeAnimationProgress(bar_width,
1463                                          kDeteminateOverlayWidth,
1464                                          kDeteminateOverlayPixelsPerSecond,
1465                                          extra.animated_seconds);
1466        RECT overlay_rect = value_rect;
1467        overlay_rect.left += dx;
1468        overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
1469        draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
1470      }
1471    } else {
1472      // A glossy overlay for indeterminate progress bar has small pause
1473      // after each animation. We emulate this by adding an invisible margin
1474      // the animation has to traverse.
1475      int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
1476      int overlay_width = pre_vista ?
1477          kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
1478      int dx = ComputeAnimationProgress(width_with_margin,
1479                                        overlay_width,
1480                                        kIndeterminateOverlayPixelsPerSecond,
1481                                        extra.animated_seconds);
1482      RECT overlay_rect = bar_rect;
1483      overlay_rect.left += dx;
1484      overlay_rect.right = overlay_rect.left + overlay_width;
1485      if (pre_vista) {
1486        RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
1487        RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
1488        draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
1489      } else {
1490        draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1491      }
1492    }
1493
1494    return S_OK;
1495  }
1496
1497  HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
1498  HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
1499  FillRect(hdc, &bar_rect, bg_brush);
1500  FillRect(hdc, &value_rect, fg_brush);
1501  DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1502  return S_OK;
1503}
1504
1505HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
1506                                                 const gfx::Rect& rect) const {
1507  HANDLE handle = GetThemeHandle(STATUS);
1508  RECT rect_win = rect.ToRECT();
1509  if (handle && draw_theme_) {
1510    // Paint the status bar gripper.  There doesn't seem to be a
1511    // standard gripper in Windows for the space between
1512    // scrollbars.  This is pretty close, but it's supposed to be
1513    // painted over a status bar.
1514    return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
1515  }
1516
1517  // Draw a windows classic scrollbar gripper.
1518  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1519  return S_OK;
1520}
1521
1522HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
1523                                                const gfx::Rect& rect) const {
1524  HANDLE handle = GetThemeHandle(TAB);
1525  RECT rect_win = rect.ToRECT();
1526  if (handle && draw_theme_)
1527    return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
1528
1529  // Classic just renders a flat color background.
1530  FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1531  return S_OK;
1532}
1533
1534HRESULT NativeThemeWin::PaintTextField(
1535    HDC hdc,
1536    Part part,
1537    State state,
1538    const gfx::Rect& rect,
1539    const TextFieldExtraParams& extra) const {
1540  int part_id = EP_EDITTEXT;
1541  int state_id = ETS_NORMAL;
1542  switch (state) {
1543    case kNormal:
1544      if (extra.is_read_only) {
1545        state_id = ETS_READONLY;
1546      } else if (extra.is_focused) {
1547        state_id = ETS_FOCUSED;
1548      } else {
1549        state_id = ETS_NORMAL;
1550      }
1551      break;
1552    case kHovered:
1553      state_id = ETS_HOT;
1554      break;
1555    case kPressed:
1556      state_id = ETS_SELECTED;
1557      break;
1558    case kDisabled:
1559      state_id = ETS_DISABLED;
1560      break;
1561    default:
1562      NOTREACHED() << "Invalid state: " << state;
1563      break;
1564  }
1565
1566  RECT rect_win = rect.ToRECT();
1567  return PaintTextField(hdc, part_id, state_id, extra.classic_state,
1568                        &rect_win,
1569                        skia::SkColorToCOLORREF(extra.background_color),
1570                        extra.fill_content_area, extra.draw_edges);
1571}
1572
1573HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1574                                       int part_id,
1575                                       int state_id,
1576                                       int classic_state,
1577                                       RECT* rect,
1578                                       COLORREF color,
1579                                       bool fill_content_area,
1580                                       bool draw_edges) const {
1581  // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1582  // exclude individual edges from being drawn.
1583
1584  HANDLE handle = GetThemeHandle(TEXTFIELD);
1585  // TODO(mpcomplete): can we detect if the color is specified by the user,
1586  // and if not, just use the system color?
1587  // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1588  HBRUSH bg_brush = CreateSolidBrush(color);
1589  HRESULT hr;
1590  // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1591  // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1592  if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
1593    if (draw_theme_ex_) {
1594      static const DTBGOPTS omit_border_options = {
1595        sizeof(DTBGOPTS),
1596        DTBG_OMITBORDER,
1597        { 0, 0, 0, 0 }
1598      };
1599      const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
1600      hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
1601    } else {
1602      hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1603    }
1604
1605    // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1606    if (fill_content_area && get_theme_content_rect_) {
1607      RECT content_rect;
1608      hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1609                                   &content_rect);
1610      FillRect(hdc, &content_rect, bg_brush);
1611    }
1612  } else {
1613    // Draw it manually.
1614    if (draw_edges)
1615      DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1616
1617    if (fill_content_area) {
1618      FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1619                   reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1620    }
1621    hr = S_OK;
1622  }
1623  DeleteObject(bg_brush);
1624  return hr;
1625}
1626
1627HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
1628                                         HDC hdc,
1629                                         int part_id,
1630                                         int state_id,
1631                                         const gfx::Rect& rect) const {
1632  // Correct the scaling and positioning of sub-components such as scrollbar
1633  // arrows and thumb grippers in the event that the world transform applies
1634  // scaling (e.g. in high-DPI mode).
1635  XFORM save_transform;
1636  if (GetWorldTransform(hdc, &save_transform)) {
1637    float scale = save_transform.eM11;
1638    if (scale != 1 && save_transform.eM12 == 0) {
1639      ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
1640      gfx::Rect scaled_rect = gfx::ToEnclosedRect(
1641          gfx::ScaleRect(rect, scale));
1642      RECT bounds = gfx::Rect(scaled_rect.x() + save_transform.eDx,
1643                              scaled_rect.y() + save_transform.eDy,
1644                              scaled_rect.width(),
1645                              scaled_rect.height()).ToRECT();
1646      HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
1647                                   NULL);
1648      SetWorldTransform(hdc, &save_transform);
1649      return result;
1650    }
1651  }
1652  RECT bounds = rect.ToRECT();
1653  return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
1654}
1655
1656// static
1657NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1658  ThemeName name;
1659  switch (part) {
1660    case kCheckbox:
1661    case kRadio:
1662    case kPushButton:
1663      name = BUTTON;
1664      break;
1665    case kInnerSpinButton:
1666      name = SPIN;
1667      break;
1668    case kMenuCheck:
1669    case kMenuPopupGutter:
1670    case kMenuList:
1671    case kMenuPopupArrow:
1672    case kMenuPopupSeparator:
1673      name = MENU;
1674      break;
1675    case kProgressBar:
1676      name = PROGRESS;
1677      break;
1678    case kScrollbarDownArrow:
1679    case kScrollbarLeftArrow:
1680    case kScrollbarRightArrow:
1681    case kScrollbarUpArrow:
1682    case kScrollbarHorizontalThumb:
1683    case kScrollbarVerticalThumb:
1684    case kScrollbarHorizontalTrack:
1685    case kScrollbarVerticalTrack:
1686      name = SCROLLBAR;
1687      break;
1688    case kSliderTrack:
1689    case kSliderThumb:
1690      name = TRACKBAR;
1691      break;
1692    case kTextField:
1693      name = TEXTFIELD;
1694      break;
1695    case kWindowResizeGripper:
1696      name = STATUS;
1697      break;
1698    default:
1699      NOTREACHED() << "Invalid part: " << part;
1700      break;
1701  }
1702  return name;
1703}
1704
1705// static
1706int NativeThemeWin::GetWindowsPart(Part part,
1707                                   State state,
1708                                   const ExtraParams& extra) {
1709  int part_id;
1710  switch (part) {
1711    case kCheckbox:
1712      part_id = BP_CHECKBOX;
1713      break;
1714    case kMenuCheck:
1715      part_id = MENU_POPUPCHECK;
1716      break;
1717    case kMenuPopupArrow:
1718      part_id = MENU_POPUPSUBMENU;
1719      break;
1720    case kMenuPopupGutter:
1721      part_id = MENU_POPUPGUTTER;
1722      break;
1723    case kMenuPopupSeparator:
1724      part_id = MENU_POPUPSEPARATOR;
1725      break;
1726    case kPushButton:
1727      part_id = BP_PUSHBUTTON;
1728      break;
1729    case kRadio:
1730      part_id = BP_RADIOBUTTON;
1731      break;
1732    case kWindowResizeGripper:
1733      part_id = SP_GRIPPER;
1734      break;
1735    case kScrollbarDownArrow:
1736    case kScrollbarLeftArrow:
1737    case kScrollbarRightArrow:
1738    case kScrollbarUpArrow:
1739      part_id = SBP_ARROWBTN;
1740      break;
1741    case kScrollbarHorizontalThumb:
1742      part_id = SBP_THUMBBTNHORZ;
1743      break;
1744    case kScrollbarVerticalThumb:
1745      part_id = SBP_THUMBBTNVERT;
1746      break;
1747    default:
1748      NOTREACHED() << "Invalid part: " << part;
1749      break;
1750  }
1751  return part_id;
1752}
1753
1754int NativeThemeWin::GetWindowsState(Part part,
1755                                    State state,
1756                                    const ExtraParams& extra) {
1757  int state_id;
1758  switch (part) {
1759    case kCheckbox:
1760      switch (state) {
1761        case kNormal:
1762          state_id = CBS_UNCHECKEDNORMAL;
1763          break;
1764        case kHovered:
1765          state_id = CBS_UNCHECKEDHOT;
1766          break;
1767        case kPressed:
1768          state_id = CBS_UNCHECKEDPRESSED;
1769          break;
1770        case kDisabled:
1771          state_id = CBS_UNCHECKEDDISABLED;
1772          break;
1773        default:
1774          NOTREACHED() << "Invalid state: " << state;
1775          break;
1776      }
1777      break;
1778    case kMenuCheck:
1779      switch (state) {
1780        case kNormal:
1781        case kHovered:
1782        case kPressed:
1783          state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
1784                                               : MC_CHECKMARKNORMAL;
1785          break;
1786        case kDisabled:
1787          state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
1788                                               : MC_CHECKMARKDISABLED;
1789          break;
1790        default:
1791          NOTREACHED() << "Invalid state: " << state;
1792          break;
1793      }
1794      break;
1795    case kMenuPopupArrow:
1796    case kMenuPopupGutter:
1797    case kMenuPopupSeparator:
1798      switch (state) {
1799        case kNormal:
1800          state_id = MBI_NORMAL;
1801          break;
1802        case kHovered:
1803          state_id = MBI_HOT;
1804          break;
1805        case kPressed:
1806          state_id = MBI_PUSHED;
1807          break;
1808        case kDisabled:
1809          state_id = MBI_DISABLED;
1810          break;
1811        default:
1812          NOTREACHED() << "Invalid state: " << state;
1813          break;
1814      }
1815      break;
1816    case kPushButton:
1817      switch (state) {
1818        case kNormal:
1819          state_id = PBS_NORMAL;
1820          break;
1821        case kHovered:
1822          state_id = PBS_HOT;
1823          break;
1824        case kPressed:
1825          state_id = PBS_PRESSED;
1826          break;
1827        case kDisabled:
1828          state_id = PBS_DISABLED;
1829          break;
1830        default:
1831          NOTREACHED() << "Invalid state: " << state;
1832          break;
1833      }
1834      break;
1835    case kRadio:
1836      switch (state) {
1837        case kNormal:
1838          state_id = RBS_UNCHECKEDNORMAL;
1839          break;
1840        case kHovered:
1841          state_id = RBS_UNCHECKEDHOT;
1842          break;
1843        case kPressed:
1844          state_id = RBS_UNCHECKEDPRESSED;
1845          break;
1846        case kDisabled:
1847          state_id = RBS_UNCHECKEDDISABLED;
1848          break;
1849        default:
1850          NOTREACHED() << "Invalid state: " << state;
1851          break;
1852      }
1853      break;
1854    case kWindowResizeGripper:
1855      switch (state) {
1856        case kNormal:
1857        case kHovered:
1858        case kPressed:
1859        case kDisabled:
1860          state_id = 1;  // gripper has no windows state
1861          break;
1862        default:
1863          NOTREACHED() << "Invalid state: " << state;
1864          break;
1865      }
1866      break;
1867    case kScrollbarDownArrow:
1868      switch (state) {
1869        case kNormal:
1870          state_id = ABS_DOWNNORMAL;
1871          break;
1872        case kHovered:
1873          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1874          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1875              ABS_DOWNHOT : ABS_DOWNHOVER;
1876          break;
1877        case kPressed:
1878          state_id = ABS_DOWNPRESSED;
1879          break;
1880        case kDisabled:
1881          state_id = ABS_DOWNDISABLED;
1882          break;
1883        default:
1884          NOTREACHED() << "Invalid state: " << state;
1885          break;
1886      }
1887      break;
1888    case kScrollbarLeftArrow:
1889      switch (state) {
1890        case kNormal:
1891          state_id = ABS_LEFTNORMAL;
1892          break;
1893        case kHovered:
1894          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1895          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1896              ABS_LEFTHOT : ABS_LEFTHOVER;
1897          break;
1898        case kPressed:
1899          state_id = ABS_LEFTPRESSED;
1900          break;
1901        case kDisabled:
1902          state_id = ABS_LEFTDISABLED;
1903          break;
1904        default:
1905          NOTREACHED() << "Invalid state: " << state;
1906          break;
1907      }
1908      break;
1909    case kScrollbarRightArrow:
1910      switch (state) {
1911        case kNormal:
1912          state_id = ABS_RIGHTNORMAL;
1913          break;
1914        case kHovered:
1915          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1916          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1917              ABS_RIGHTHOT : ABS_RIGHTHOVER;
1918          break;
1919        case kPressed:
1920          state_id = ABS_RIGHTPRESSED;
1921          break;
1922        case kDisabled:
1923          state_id = ABS_RIGHTDISABLED;
1924          break;
1925        default:
1926          NOTREACHED() << "Invalid state: " << state;
1927          break;
1928      }
1929      break;
1930    case kScrollbarUpArrow:
1931      switch (state) {
1932        case kNormal:
1933          state_id = ABS_UPNORMAL;
1934          break;
1935        case kHovered:
1936          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1937          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1938              ABS_UPHOT : ABS_UPHOVER;
1939          break;
1940        case kPressed:
1941          state_id = ABS_UPPRESSED;
1942          break;
1943        case kDisabled:
1944          state_id = ABS_UPDISABLED;
1945          break;
1946        default:
1947          NOTREACHED() << "Invalid state: " << state;
1948          break;
1949      }
1950      break;
1951    case kScrollbarHorizontalThumb:
1952    case kScrollbarVerticalThumb:
1953      switch (state) {
1954        case kNormal:
1955          state_id = SCRBS_NORMAL;
1956          break;
1957        case kHovered:
1958          // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1959          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
1960              SCRBS_HOT : SCRBS_HOVER;
1961          break;
1962        case kPressed:
1963          state_id = SCRBS_PRESSED;
1964          break;
1965        case kDisabled:
1966          state_id = SCRBS_DISABLED;
1967          break;
1968        default:
1969          NOTREACHED() << "Invalid state: " << state;
1970          break;
1971      }
1972      break;
1973    default:
1974      NOTREACHED() << "Invalid part: " << part;
1975      break;
1976  }
1977  return state_id;
1978}
1979
1980HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
1981                                    int part_id,
1982                                    int state_id,
1983                                    int prop_id,
1984                                    int *value) const {
1985  HANDLE handle = GetThemeHandle(theme);
1986  if (handle && get_theme_int_)
1987    return get_theme_int_(handle, part_id, state_id, prop_id, value);
1988  return E_NOTIMPL;
1989}
1990
1991HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
1992                                          const gfx::Rect& rect,
1993                                          UINT type,
1994                                          UINT state,
1995                                          bool is_selected,
1996                                          State control_state) const {
1997  const int width = rect.width();
1998  const int height = rect.height();
1999
2000  // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
2001  base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
2002
2003  if (mask_bitmap == NULL)
2004    return E_OUTOFMEMORY;
2005
2006  base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
2007  base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
2008  RECT local_rect = { 0, 0, width, height };
2009  DrawFrameControl(bitmap_dc, &local_rect, type, state);
2010
2011  // We're going to use BitBlt with a b&w mask. This results in using the dest
2012  // dc's text color for the black bits in the mask, and the dest dc's
2013  // background color for the white bits in the mask. DrawFrameControl draws the
2014  // check in black, and the background in white.
2015  int bg_color_key;
2016  int text_color_key;
2017  switch (control_state) {
2018    case NativeTheme::kHovered:
2019      bg_color_key = COLOR_HIGHLIGHT;
2020      text_color_key = COLOR_HIGHLIGHTTEXT;
2021      break;
2022    case NativeTheme::kNormal:
2023      bg_color_key = COLOR_MENU;
2024      text_color_key = COLOR_MENUTEXT;
2025      break;
2026    case NativeTheme::kDisabled:
2027      bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
2028      text_color_key = COLOR_GRAYTEXT;
2029      break;
2030    default:
2031      NOTREACHED();
2032      bg_color_key = COLOR_MENU;
2033      text_color_key = COLOR_MENUTEXT;
2034      break;
2035  }
2036  COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
2037  COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
2038  BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
2039  SetBkColor(hdc, old_bg_color);
2040  SetTextColor(hdc, old_text_color);
2041
2042  return S_OK;
2043}
2044
2045HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
2046  if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
2047    return 0;
2048
2049  if (theme_handles_[theme_name])
2050    return theme_handles_[theme_name];
2051
2052  // Not found, try to load it.
2053  HANDLE handle = 0;
2054  switch (theme_name) {
2055  case BUTTON:
2056    handle = open_theme_(NULL, L"Button");
2057    break;
2058  case LIST:
2059    handle = open_theme_(NULL, L"Listview");
2060    break;
2061  case MENU:
2062    handle = open_theme_(NULL, L"Menu");
2063    break;
2064  case MENULIST:
2065    handle = open_theme_(NULL, L"Combobox");
2066    break;
2067  case SCROLLBAR:
2068    handle = open_theme_(NULL, L"Scrollbar");
2069    break;
2070  case STATUS:
2071    handle = open_theme_(NULL, L"Status");
2072    break;
2073  case TAB:
2074    handle = open_theme_(NULL, L"Tab");
2075    break;
2076  case TEXTFIELD:
2077    handle = open_theme_(NULL, L"Edit");
2078    break;
2079  case TRACKBAR:
2080    handle = open_theme_(NULL, L"Trackbar");
2081    break;
2082  case WINDOW:
2083    handle = open_theme_(NULL, L"Window");
2084    break;
2085  case PROGRESS:
2086    handle = open_theme_(NULL, L"Progress");
2087    break;
2088  case SPIN:
2089    handle = open_theme_(NULL, L"Spin");
2090    break;
2091  default:
2092    NOTREACHED();
2093  }
2094  theme_handles_[theme_name] = handle;
2095  return handle;
2096}
2097
2098}  // namespace ui
2099