blob: 75bf67bca8f999adcf86806318298d49edc00bbc [file] [log] [blame]
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/gfx/native_theme.h"
#include <windows.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#include "base/gfx/gdi_util.h"
#include "base/gfx/rect.h"
#include "base/logging.h"
#include "base/scoped_handle.h"
#include "skia/ext/platform_canvas.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkShader.h"
namespace {
void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
// Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
SkColor face = skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE));
SkColor highlight = skia::COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT));
SkColor buffer[] = { face, highlight, highlight, face };
// Confusing bit: we first create a temporary bitmap with our desired pattern,
// then copy it to another bitmap. The temporary bitmap doesn't take
// ownership of the pixel data, and so will point to garbage when this
// function returns. The copy will copy the pixel data into a place owned by
// the bitmap, which is in turn owned by the shader, etc., so it will live
// until we're done using it.
SkBitmap temp_bitmap;
temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
temp_bitmap.setPixels(buffer);
SkBitmap bitmap;
temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
SkShader* shader = SkShader::CreateBitmapShader(bitmap,
SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode);
// Align the pattern with the upper corner of |align_rect|.
SkMatrix matrix;
matrix.setTranslate(SkIntToScalar(align_rect.left),
SkIntToScalar(align_rect.top));
shader->setLocalMatrix(matrix);
paint->setShader(shader)->safeUnref();
}
} // namespace
namespace gfx {
/* static */
const NativeTheme* NativeTheme::instance() {
// The global NativeTheme instance.
static const NativeTheme s_native_theme;
return &s_native_theme;
}
NativeTheme::NativeTheme()
: theme_dll_(LoadLibrary(L"uxtheme.dll")),
draw_theme_(NULL),
draw_theme_ex_(NULL),
get_theme_color_(NULL),
get_theme_content_rect_(NULL),
get_theme_part_size_(NULL),
open_theme_(NULL),
close_theme_(NULL),
set_theme_properties_(NULL),
is_theme_active_(NULL),
get_theme_int_(NULL) {
if (theme_dll_) {
draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
GetProcAddress(theme_dll_, "DrawThemeBackground"));
draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
GetProcAddress(theme_dll_, "GetThemeColor"));
get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
GetProcAddress(theme_dll_, "GetThemePartSize"));
open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
GetProcAddress(theme_dll_, "OpenThemeData"));
close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
GetProcAddress(theme_dll_, "CloseThemeData"));
set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
GetProcAddress(theme_dll_, "SetThemeAppProperties"));
is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
GetProcAddress(theme_dll_, "IsThemeActive"));
get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
GetProcAddress(theme_dll_, "GetThemeInt"));
}
memset(theme_handles_, 0, sizeof(theme_handles_));
}
NativeTheme::~NativeTheme() {
if (theme_dll_) {
// todo (cpu): fix this soon.
// CloseHandles();
FreeLibrary(theme_dll_);
}
}
HRESULT NativeTheme::PaintButton(HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* rect) const {
HANDLE handle = GetThemeHandle(BUTTON);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
// Draw it manually.
// All pressed states have both low bits set, and no other states do.
const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
// BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
// button itself is shrunk by 1 pixel.
HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
if (brush) {
FrameRect(hdc, rect, brush);
InflateRect(rect, -1, -1);
}
}
DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
// Draw the focus rectangle (the dotted line box) only on buttons. For radio
// and checkboxes, we let webkit draw the focus rectangle (orange glow).
if ((BP_PUSHBUTTON == part_id) && focused) {
// The focus rect is inside the button. The exact number of pixels depends
// on whether we're in classic mode or using uxtheme.
if (handle && get_theme_content_rect_) {
get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
} else {
InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
-GetSystemMetrics(SM_CYEDGE));
}
DrawFocusRect(hdc, rect);
}
return S_OK;
}
HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active,
RECT* rect) const {
HANDLE handle = GetThemeHandle(WINDOW);
if (handle && draw_theme_) {
return draw_theme_(handle, hdc, WP_DIALOG,
active ? FS_ACTIVE : FS_INACTIVE, rect, NULL);
}
// Classic just renders a flat color background.
FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
return S_OK;
}
HRESULT NativeTheme::PaintListBackground(HDC hdc,
bool enabled,
RECT* rect) const {
HANDLE handle = GetThemeHandle(LIST);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL);
// Draw it manually.
HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW);
FillRect(hdc, rect, bg_brush);
DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
return S_OK;
}
HRESULT NativeTheme::PaintMenuArrow(ThemeName theme,
HDC hdc,
int part_id,
int state_id,
RECT* rect,
MenuArrowDirection arrow_direction,
bool is_highlighted) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_) {
if (arrow_direction == RIGHT_POINTING_ARROW) {
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
} else {
// There is no way to tell the uxtheme API to draw a left pointing arrow;
// it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
// are needed for RTL locales on Vista. So use a memory DC and mirror
// the region with GDI's StretchBlt.
Rect r(*rect);
ScopedHDC mem_dc(CreateCompatibleDC(hdc));
ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
r.height()));
HGDIOBJ old_bitmap = SelectObject(mem_dc, mem_bitmap);
// Copy and horizontally mirror the background from hdc into mem_dc. Use
// a negative-width source rect, starting at the rightmost pixel.
StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
// Draw the arrow.
RECT theme_rect = {0, 0, r.width(), r.height()};
HRESULT result = draw_theme_(handle, mem_dc, part_id,
state_id, &theme_rect, NULL);
// Copy and mirror the result back into mem_dc.
StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
SelectObject(mem_dc, old_bitmap);
return result;
}
}
// For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
// left pointing arrow. This makes the following 'if' statement slightly
// counterintuitive.
UINT state;
if (arrow_direction == RIGHT_POINTING_ARROW)
state = DFCS_MENUARROW;
else
state = DFCS_MENUARROWRIGHT;
return PaintFrameControl(hdc, rect, DFC_MENU, state, is_highlighted);
}
HRESULT NativeTheme::PaintMenuBackground(ThemeName theme,
HDC hdc,
int part_id,
int state_id,
RECT* rect) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_) {
HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW));
return result;
}
FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU));
DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT);
return S_OK;
}
HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme,
HDC hdc,
int part_id,
int state_id,
RECT* rect) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
// Nothing to do for background.
return S_OK;
}
HRESULT NativeTheme::PaintMenuCheck(ThemeName theme,
HDC hdc,
int part_id,
int state_id,
RECT* rect,
bool is_highlighted) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_) {
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
}
return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, is_highlighted);
}
HRESULT NativeTheme::PaintMenuGutter(HDC hdc,
int part_id,
int state_id,
RECT* rect) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
return E_NOTIMPL;
}
HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme,
HDC hdc,
int part_id,
int state_id,
bool selected,
RECT* rect) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
if (selected)
FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT));
return S_OK;
}
HRESULT NativeTheme::PaintMenuList(HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* rect) const {
HANDLE handle = GetThemeHandle(MENULIST);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
// Draw it manually.
DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state);
return S_OK;
}
HRESULT NativeTheme::PaintMenuSeparator(HDC hdc,
int part_id,
int state_id,
RECT* rect) const {
HANDLE handle = GetThemeHandle(MENU);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP);
return S_OK;
}
HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc,
int state_id,
int classic_state,
RECT* rect) const {
HANDLE handle = GetThemeHandle(SCROLLBAR);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL);
// Draw it manually.
DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state);
return S_OK;
}
HRESULT NativeTheme::PaintScrollbarTrack(
HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* target_rect,
RECT* align_rect,
skia::PlatformCanvas* canvas) const {
HANDLE handle = GetThemeHandle(SCROLLBAR);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL);
// Draw it manually.
const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR);
const DWORD color3DFace = GetSysColor(COLOR_3DFACE);
if ((colorScrollbar != color3DFace) &&
(colorScrollbar != GetSysColor(COLOR_WINDOW))) {
FillRect(hdc, target_rect, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
} else {
SkPaint paint;
SetCheckerboardShader(&paint, *align_rect);
canvas->drawIRect(skia::RECTToSkIRect(*target_rect), paint);
}
if (classic_state & DFCS_PUSHED)
InvertRect(hdc, target_rect);
return S_OK;
}
HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* rect) const {
HANDLE handle = GetThemeHandle(SCROLLBAR);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
// Draw it manually.
if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
// Classic mode doesn't have a gripper.
return S_OK;
}
HRESULT NativeTheme::PaintStatusGripper(HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* rect) const {
HANDLE handle = GetThemeHandle(STATUS);
if (handle && draw_theme_) {
// Paint the status bar gripper. There doesn't seem to be a
// standard gripper in Windows for the space between
// scrollbars. This is pretty close, but it's supposed to be
// painted over a status bar.
return draw_theme_(handle, hdc, SP_GRIPPER, 0, rect, NULL);
}
// Draw a windows classic scrollbar gripper.
DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
return S_OK;
}
HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const {
HANDLE handle = GetThemeHandle(TAB);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL);
// Classic just renders a flat color background.
FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
return S_OK;
}
HRESULT NativeTheme::PaintTrackbar(HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* rect,
skia::PlatformCanvas* canvas) const {
// Make the channel be 4 px thick in the center of the supplied rect. (4 px
// matches what XP does in various menus; GetThemePartSize() doesn't seem to
// return good values here.)
RECT channel_rect = *rect;
const int channel_thickness = 4;
if (part_id == TKP_TRACK) {
channel_rect.top +=
((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
channel_rect.bottom = channel_rect.top + channel_thickness;
} else if (part_id == TKP_TRACKVERT) {
channel_rect.left +=
((channel_rect.right - channel_rect.left - channel_thickness) / 2);
channel_rect.right = channel_rect.left + channel_thickness;
} // else this isn't actually a channel, so |channel_rect| == |rect|.
HANDLE handle = GetThemeHandle(TRACKBAR);
if (handle && draw_theme_)
return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
// Classic mode, draw it manually.
if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
} else if (part_id == TKP_THUMBVERT) {
DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
} else {
// Split rect into top and bottom pieces.
RECT top_section = *rect;
RECT bottom_section = *rect;
top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
bottom_section.top = top_section.bottom;
DrawEdge(hdc, &top_section, EDGE_RAISED,
BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
// Split triangular piece into two diagonals.
RECT& left_half = bottom_section;
RECT right_half = bottom_section;
right_half.left += ((bottom_section.right - bottom_section.left) / 2);
left_half.right = right_half.left;
DrawEdge(hdc, &left_half, EDGE_RAISED,
BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
DrawEdge(hdc, &right_half, EDGE_RAISED,
BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
// If the button is pressed, draw hatching.
if (classic_state & DFCS_PUSHED) {
SkPaint paint;
SetCheckerboardShader(&paint, *rect);
// Fill all three pieces with the pattern.
canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
SkScalar left_triangle_top = SkIntToScalar(left_half.top);
SkScalar left_triangle_right = SkIntToScalar(left_half.right);
SkPath left_triangle;
left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
left_triangle.lineTo(left_triangle_right, left_triangle_top);
left_triangle.lineTo(left_triangle_right,
SkIntToScalar(left_half.bottom));
left_triangle.close();
canvas->drawPath(left_triangle, paint);
SkScalar right_triangle_left = SkIntToScalar(right_half.left);
SkScalar right_triangle_top = SkIntToScalar(right_half.top);
SkPath right_triangle;
right_triangle.moveTo(right_triangle_left, right_triangle_top);
right_triangle.lineTo(SkIntToScalar(right_half.right),
right_triangle_top);
right_triangle.lineTo(right_triangle_left,
SkIntToScalar(right_half.bottom));
right_triangle.close();
canvas->drawPath(right_triangle, paint);
}
}
return S_OK;
}
HRESULT NativeTheme::PaintTextField(HDC hdc,
int part_id,
int state_id,
int classic_state,
RECT* rect,
COLORREF color,
bool fill_content_area,
bool draw_edges) const {
// TODO(ojan): http://b/1210017 Figure out how to give the ability to
// exclude individual edges from being drawn.
HANDLE handle = GetThemeHandle(TEXTFIELD);
// TODO(mpcomplete): can we detect if the color is specified by the user,
// and if not, just use the system color?
// CreateSolidBrush() accepts a RGB value but alpha must be 0.
HBRUSH bg_brush = CreateSolidBrush(color);
HRESULT hr;
// DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
// draw_theme_ex_ is NULL and draw_theme_ is non-null.
if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
if (draw_theme_ex_) {
static DTBGOPTS omit_border_options = {
sizeof(DTBGOPTS),
DTBG_OMITBORDER,
{0,0,0,0}
};
DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
} else {
hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
}
// TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
if (fill_content_area && get_theme_content_rect_) {
RECT content_rect;
hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
&content_rect);
FillRect(hdc, &content_rect, bg_brush);
}
} else {
// Draw it manually.
if (draw_edges)
DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
if (fill_content_area) {
FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
}
hr = S_OK;
}
DeleteObject(bg_brush);
return hr;
}
bool NativeTheme::IsThemingActive() const {
if (is_theme_active_)
return !!is_theme_active_();
return false;
}
HRESULT NativeTheme::GetThemePartSize(ThemeName theme_name,
HDC hdc,
int part_id,
int state_id,
RECT* rect,
int ts,
SIZE* size) const {
HANDLE handle = GetThemeHandle(theme_name);
if (handle && get_theme_part_size_)
return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
return E_NOTIMPL;
}
HRESULT NativeTheme::GetThemeColor(ThemeName theme,
int part_id,
int state_id,
int prop_id,
SkColor* color) const {
HANDLE handle = GetThemeHandle(theme);
if (handle && get_theme_color_) {
COLORREF color_ref;
if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
S_OK) {
*color = skia::COLORREFToSkColor(color_ref);
return S_OK;
}
}
return E_NOTIMPL;
}
SkColor NativeTheme::GetThemeColorWithDefault(ThemeName theme,
int part_id,
int state_id,
int prop_id,
int default_sys_color) const {
SkColor color;
if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
color = skia::COLORREFToSkColor(GetSysColor(default_sys_color));
return color;
}
HRESULT NativeTheme::GetThemeInt(ThemeName theme,
int part_id,
int state_id,
int prop_id,
int *value) const {
HANDLE handle = GetThemeHandle(theme);
if (handle && get_theme_int_)
return get_theme_int_(handle, part_id, state_id, prop_id, value);
return E_NOTIMPL;
}
Size NativeTheme::GetThemeBorderSize(ThemeName theme) const {
// For simplicity use the wildcard state==0, part==0, since it works
// for the cases we currently depend on.
int border;
if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
return Size(border, border);
else
return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
}
void NativeTheme::DisableTheming() const {
if (!set_theme_properties_)
return;
set_theme_properties_(0);
}
HRESULT NativeTheme::PaintFrameControl(HDC hdc,
RECT* rect,
UINT type,
UINT state,
bool is_highlighted) const {
const int width = rect->right - rect->left;
const int height = rect->bottom - rect->top;
// DrawFrameControl for menu arrow/check wants a monochrome bitmap.
ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
if (mask_bitmap == NULL)
return E_OUTOFMEMORY;
ScopedHDC bitmap_dc(CreateCompatibleDC(NULL));
HGDIOBJ org_bitmap = SelectObject(bitmap_dc, mask_bitmap);
RECT local_rect = { 0, 0, width, height };
DrawFrameControl(bitmap_dc, &local_rect, type, state);
// We're going to use BitBlt with a b&w mask. This results in using the dest
// dc's text color for the black bits in the mask, and the dest dc's
// background color for the white bits in the mask. DrawFrameControl draws the
// check in black, and the background in white.
COLORREF old_bg_color =
SetBkColor(hdc,
GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU));
COLORREF old_text_color =
SetTextColor(hdc,
GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT :
COLOR_MENUTEXT));
BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY);
SetBkColor(hdc, old_bg_color);
SetTextColor(hdc, old_text_color);
SelectObject(bitmap_dc, org_bitmap);
return S_OK;
}
void NativeTheme::CloseHandles() const
{
if (!close_theme_)
return;
for (int i = 0; i < LAST; ++i) {
if (theme_handles_[i])
close_theme_(theme_handles_[i]);
theme_handles_[i] = NULL;
}
}
HANDLE NativeTheme::GetThemeHandle(ThemeName theme_name) const
{
if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
return 0;
if (theme_handles_[theme_name])
return theme_handles_[theme_name];
// Not found, try to load it.
HANDLE handle = 0;
switch (theme_name) {
case BUTTON:
handle = open_theme_(NULL, L"Button");
break;
case LIST:
handle = open_theme_(NULL, L"Listview");
break;
case MENU:
handle = open_theme_(NULL, L"Menu");
break;
case MENULIST:
handle = open_theme_(NULL, L"Combobox");
break;
case SCROLLBAR:
handle = open_theme_(NULL, L"Scrollbar");
break;
case STATUS:
handle = open_theme_(NULL, L"Status");
break;
case TAB:
handle = open_theme_(NULL, L"Tab");
break;
case TEXTFIELD:
handle = open_theme_(NULL, L"Edit");
break;
case TRACKBAR:
handle = open_theme_(NULL, L"Trackbar");
break;
case WINDOW:
handle = open_theme_(NULL, L"Window");
break;
default:
NOTREACHED();
}
theme_handles_[theme_name] = handle;
return handle;
}
} // namespace gfx