gtk3/gtk/gtkwin32theme.c
Alexander Larsson f4a68dff88 Fix rendering of theme parts on Windows XP
It seems XP doesn't handle drawing non-alpha theme parts
on alpha destinations. We fix this by using alpha bitmaps only when
needed.

However this means any non-drawn area by the theme part is now draw
black, so we must take more care to only draw where the theme part draws,
so we find the theme part size when available.
2012-03-09 16:43:00 +01:00

399 lines
11 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
* Copyright (C) 2011 Red Hat, Inc.
*
* Authors: Carlos Garnacho <carlosg@gnome.org>
* Cosimo Cecchi <cosimoc@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "gtkwin32themeprivate.h"
#ifdef G_OS_WIN32
#include <cairo-win32.h>
#define UXTHEME_DLL "uxtheme.dll"
static HINSTANCE uxtheme_dll = NULL;
static gboolean use_xp_theme = FALSE;
typedef HRESULT (FAR PASCAL *GetThemeSysFontFunc) (HTHEME hTheme, int iFontID, OUT LOGFONTW *plf);
typedef int (FAR PASCAL *GetThemeSysSizeFunc) (HTHEME hTheme, int iSizeId);
typedef COLORREF (FAR PASCAL *GetThemeSysColorFunc) (HTHEME hTheme,
int iColorID);
typedef HTHEME (FAR PASCAL *OpenThemeDataFunc) (HWND hwnd,
LPCWSTR pszClassList);
typedef HRESULT (FAR PASCAL *CloseThemeDataFunc) (HTHEME theme);
typedef HRESULT (FAR PASCAL *DrawThemeBackgroundFunc) (HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
const RECT *pRect, const RECT *pClipRect);
typedef HRESULT (FAR PASCAL *EnableThemeDialogTextureFunc) (HWND hwnd,
DWORD dwFlags);
typedef BOOL (FAR PASCAL *IsThemeActiveFunc) (VOID);
typedef BOOL (FAR PASCAL *IsAppThemedFunc) (VOID);
typedef BOOL (FAR PASCAL *IsThemeBackgroundPartiallyTransparentFunc) (HTHEME hTheme,
int iPartId,
int iStateId);
typedef HRESULT (FAR PASCAL *DrawThemeParentBackgroundFunc) (HWND hwnd,
HDC hdc,
RECT *prc);
typedef HRESULT (FAR PASCAL *GetThemePartSizeFunc) (HTHEME hTheme,
HDC hdc,
int iPartId,
int iStateId,
RECT *prc,
int eSize,
SIZE *psz);
static GetThemeSysFontFunc get_theme_sys_font = NULL;
static GetThemeSysColorFunc get_theme_sys_color = NULL;
static GetThemeSysSizeFunc get_theme_sys_metric = NULL;
static OpenThemeDataFunc open_theme_data = NULL;
static CloseThemeDataFunc close_theme_data = NULL;
static DrawThemeBackgroundFunc draw_theme_background = NULL;
static EnableThemeDialogTextureFunc enable_theme_dialog_texture = NULL;
static IsThemeActiveFunc is_theme_active = NULL;
static IsAppThemedFunc is_app_themed = NULL;
static IsThemeBackgroundPartiallyTransparentFunc is_theme_partially_transparent = NULL;
static DrawThemeParentBackgroundFunc draw_theme_parent_background = NULL;
static GetThemePartSizeFunc get_theme_part_size = NULL;
static GHashTable *hthemes_by_class = NULL;
static void
_gtk_win32_theme_init (void)
{
char *buf;
char dummy;
int n, k;
if (uxtheme_dll)
return;
n = GetSystemDirectory (&dummy, 0);
if (n <= 0)
return;
buf = g_malloc (n + 1 + strlen (UXTHEME_DLL));
k = GetSystemDirectory (buf, n);
if (k == 0 || k > n)
{
g_free (buf);
return;
}
if (!G_IS_DIR_SEPARATOR (buf[strlen (buf) -1]))
strcat (buf, G_DIR_SEPARATOR_S);
strcat (buf, UXTHEME_DLL);
uxtheme_dll = LoadLibrary (buf);
g_free (buf);
if (!uxtheme_dll)
return;
is_app_themed = (IsAppThemedFunc) GetProcAddress (uxtheme_dll, "IsAppThemed");
if (is_app_themed)
{
is_theme_active = (IsThemeActiveFunc) GetProcAddress (uxtheme_dll, "IsThemeActive");
open_theme_data = (OpenThemeDataFunc) GetProcAddress (uxtheme_dll, "OpenThemeData");
close_theme_data = (CloseThemeDataFunc) GetProcAddress (uxtheme_dll, "CloseThemeData");
draw_theme_background = (DrawThemeBackgroundFunc) GetProcAddress (uxtheme_dll, "DrawThemeBackground");
enable_theme_dialog_texture = (EnableThemeDialogTextureFunc) GetProcAddress (uxtheme_dll, "EnableThemeDialogTexture");
get_theme_sys_font = (GetThemeSysFontFunc) GetProcAddress (uxtheme_dll, "GetThemeSysFont");
get_theme_sys_color = (GetThemeSysColorFunc) GetProcAddress (uxtheme_dll, "GetThemeSysColor");
get_theme_sys_metric = (GetThemeSysSizeFunc) GetProcAddress (uxtheme_dll, "GetThemeSysSize");
is_theme_partially_transparent = (IsThemeBackgroundPartiallyTransparentFunc) GetProcAddress (uxtheme_dll, "IsThemeBackgroundPartiallyTransparent");
draw_theme_parent_background = (DrawThemeParentBackgroundFunc) GetProcAddress (uxtheme_dll, "DrawThemeParentBackground");
get_theme_part_size = (GetThemePartSizeFunc) GetProcAddress (uxtheme_dll, "GetThemePartSize");
}
if (is_app_themed && is_theme_active)
{
use_xp_theme = (is_app_themed () && is_theme_active ());
}
else
{
use_xp_theme = FALSE;
}
hthemes_by_class = g_hash_table_new (g_str_hash, g_str_equal);
}
HTHEME
_gtk_win32_lookup_htheme_by_classname (const char *class)
{
HTHEME theme;
guint16 *wclass;
char *lower;
_gtk_win32_theme_init ();
lower = g_ascii_strdown (class, -1);
theme = (HTHEME) g_hash_table_lookup (hthemes_by_class, lower);
if (theme)
{
g_free (lower);
return theme;
}
wclass = g_utf8_to_utf16 (lower, -1, NULL, NULL, NULL);
theme = open_theme_data (NULL, wclass);
g_free (wclass);
if (theme == NULL)
{
g_free (lower);
return NULL;
}
/* Takes ownership of lower: */
g_hash_table_insert (hthemes_by_class, lower, theme);
return theme;
}
#else
HTHEME
_gtk_win32_lookup_htheme_by_classname (const char *class)
{
return NULL;
}
#endif /* G_OS_WIN32 */
cairo_surface_t *
_gtk_win32_theme_part_create_surface (HTHEME theme,
int xp_part,
int state,
int margins[4],
int width,
int height,
int *x_offs_out,
int *y_offs_out)
{
cairo_surface_t *surface;
GdkRGBA color;
cairo_t *cr;
int x_offs;
int y_offs;
#ifdef G_OS_WIN32
HDC hdc;
RECT rect;
SIZE size;
HRESULT res;
#endif
x_offs = margins[3];
y_offs = margins[0];
width -= margins[3] + margins[1];
height -= margins[0] + margins[2];
#ifdef G_OS_WIN32
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
hdc = GetDC (NULL);
res = get_theme_part_size (theme, hdc, xp_part, state, &rect, 2, &size);
ReleaseDC (NULL, hdc);
if (res == S_OK)
{
x_offs += (width - size.cx) / 2;
y_offs += (height - size.cy) / 2;
width = size.cx;
height = size.cy;
rect.right = width;
rect.bottom = height;
}
if (is_theme_partially_transparent (theme, xp_part, state))
surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height);
else
surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height);
hdc = cairo_win32_surface_get_dc (surface);
res = draw_theme_background (theme, hdc, xp_part, state, &rect, &rect);
*x_offs_out = x_offs;
*y_offs_out = y_offs;
if (res == S_OK)
return surface;
#else /* !G_OS_WIN32 */
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
#endif /* G_OS_WIN32 */
cr = cairo_create (surface);
/* XXX: Do something better here (like printing the theme parts) */
gdk_rgba_parse (&color, "pink");
gdk_cairo_set_source_rgba (cr, &color);
cairo_paint (cr);
cairo_destroy (cr);
*x_offs_out = x_offs;
*y_offs_out = y_offs;
return surface;
}
int
_gtk_win32_theme_int_parse (GtkCssParser *parser,
GFile *base,
int *value)
{
char *class;
int arg;
if (_gtk_css_parser_try (parser,
"-gtk-win32-size",
TRUE))
{
if (!_gtk_css_parser_try (parser, "(", TRUE))
{
_gtk_css_parser_error (parser,
"Expected '(' after '-gtk-win32-size'");
return 0;
}
class = _gtk_css_parser_try_name (parser, TRUE);
if (class == NULL)
{
_gtk_css_parser_error (parser,
"Expected name as first argument to '-gtk-win32-size'");
return 0;
}
if (! _gtk_css_parser_try (parser, ",", TRUE))
{
g_free (class);
_gtk_css_parser_error (parser,
"Expected ','");
return 0;
}
if (!_gtk_css_parser_try_int (parser, &arg))
{
g_free (class);
_gtk_css_parser_error (parser, "Expected a valid integer value");
return 0;
}
if (!_gtk_css_parser_try (parser, ")", TRUE))
{
_gtk_css_parser_error (parser,
"Expected ')'");
return 0;
}
#ifdef G_OS_WIN32
if (use_xp_theme && get_theme_sys_metric != NULL)
{
HTHEME theme = _gtk_win32_lookup_htheme_by_classname (class);
/* If theme is NULL it will just return the GetSystemMetrics value */
*value = get_theme_sys_metric (theme, arg);
}
else
*value = GetSystemMetrics (arg);
#else
*value = 1;
#endif
g_free (class);
return 1;
}
return -1;
}
GtkSymbolicColor *
_gtk_win32_theme_color_parse (GtkCssParser *parser)
{
GtkSymbolicColor *color;
char *class;
int id;
class = _gtk_css_parser_try_name (parser, TRUE);
if (class == NULL)
{
_gtk_css_parser_error (parser,
"Expected name as first argument to '-gtk-win32-color'");
return NULL;
}
if (! _gtk_css_parser_try (parser, ",", TRUE))
{
g_free (class);
_gtk_css_parser_error (parser,
"Expected ','");
return NULL;
}
if (!_gtk_css_parser_try_int (parser, &id))
{
g_free (class);
_gtk_css_parser_error (parser, "Expected a valid integer value");
return NULL;
}
color = gtk_symbolic_color_new_win32 (class, id);
g_free (class);
return color;
}
gboolean
_gtk_win32_theme_color_resolve (const char *theme_class,
gint id,
GdkRGBA *color)
{
#ifdef G_OS_WIN32
DWORD dcolor;
if (use_xp_theme && get_theme_sys_color != NULL)
{
HTHEME theme = _gtk_win32_lookup_htheme_by_classname (theme_class);
/* if theme is NULL, it will just return the GetSystemColor()
value */
dcolor = get_theme_sys_color (theme, id);
}
else
dcolor = GetSysColor (id);
color->alpha = 1.0;
color->red = GetRValue (dcolor) / 255.0;
color->green = GetGValue (dcolor) / 255.0;
color->blue = GetBValue (dcolor) / 255.0;
#else
gdk_rgba_parse (color, "pink");
#endif
return TRUE;
}