diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-19 03:23:15 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-19 03:23:15 +0000 |
commit | cd81841e8ec6c2d1ace04df2e04b651c7a902242 (patch) | |
tree | 8ba76e5b3f3daabe56115af51e301a84271ceb09 /gfx/native_theme_win.cc | |
parent | 015cfd316c32fb760bd3f92716f99fb5b3699624 (diff) | |
download | chromium_src-cd81841e8ec6c2d1ace04df2e04b651c7a902242.zip chromium_src-cd81841e8ec6c2d1ace04df2e04b651c7a902242.tar.gz chromium_src-cd81841e8ec6c2d1ace04df2e04b651c7a902242.tar.bz2 |
Move more files to toplevel gfx.
TBR=darin
BUG=none
TEST=none
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42071 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gfx/native_theme_win.cc')
-rw-r--r-- | gfx/native_theme_win.cc | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/gfx/native_theme_win.cc b/gfx/native_theme_win.cc new file mode 100644 index 0000000..87024d3 --- /dev/null +++ b/gfx/native_theme_win.cc @@ -0,0 +1,710 @@ +// Copyright (c) 2009 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 "gfx/native_theme_win.h" + +#include <windows.h> +#include <uxtheme.h> +#include <vsstyle.h> +#include <vssym32.h> + +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "gfx/gdi_util.h" +#include "gfx/rect.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 |