gtkwin32theme.c 14.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* 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
Javier Jardon's avatar
Javier Jardon committed
19
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 21 22 23 24 25
 */

#include <config.h>

#include "gtkwin32themeprivate.h"

26 27
#include "gtkwin32drawprivate.h"

28 29
#ifdef G_OS_WIN32

30
#include <windows.h>
31
#include <gdk/win32/gdkwin32.h>
32 33
#include <cairo-win32.h>

34 35
typedef HANDLE HTHEME;

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#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);
67 68 69 70 71 72
typedef HRESULT (FAR PASCAL *GetThemeBackgroundExtentFunc)  (HTHEME hTheme,
							     HDC hdc,
							     int iPartId,
							     int iStateId,
                                                             const RECT *pContentRect,
                                                             RECT *pExtentRect);
73 74

static GetThemeSysFontFunc get_theme_sys_font = NULL;
75
static GetThemeSysColorFunc GetThemeSysColor = NULL;
76
static GetThemeSysSizeFunc GetThemeSysSize = NULL;
77
static OpenThemeDataFunc OpenThemeData = NULL;
78
static CloseThemeDataFunc CloseThemeData = NULL;
79 80 81 82 83 84
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;
85
static GetThemePartSizeFunc GetThemePartSize = NULL;
86
static GetThemeBackgroundExtentFunc GetThemeBackgroundExtent = NULL;
87

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
#endif

static GHashTable *themes_by_class = NULL;

struct _GtkWin32Theme {
  char *class_name;
  gint ref_count;
#ifdef G_OS_WIN32
  HTHEME htheme;
#endif
};

GtkWin32Theme *
gtk_win32_theme_ref (GtkWin32Theme *theme)
{
  theme->ref_count++;

  return theme;
}

static gboolean
gtk_win32_theme_close (GtkWin32Theme *theme)
{
#ifdef G_OS_WIN32
  if (theme->htheme)
    {
      CloseThemeData (theme->htheme);
      theme->htheme = NULL;
      return TRUE;
    }
#endif
  return FALSE;
}

void
gtk_win32_theme_unref (GtkWin32Theme *theme)
{
  theme->ref_count--;

  if (theme->ref_count > 0)
    return;

  g_hash_table_remove (themes_by_class, theme->class_name);

  gtk_win32_theme_close (theme);
  g_free (theme->class_name);

  g_slice_free (GtkWin32Theme, theme);
}

138 139 140 141 142 143 144 145
gboolean
gtk_win32_theme_equal (GtkWin32Theme *theme1,
                       GtkWin32Theme *theme2)
{
  /* Themes are cached so they're guaranteed unique. */
  return theme1 == theme2;
}

146
#ifdef G_OS_WIN32
147

148 149 150 151 152
static GdkFilterReturn
invalidate_win32_themes (GdkXEvent *xevent,
                         GdkEvent  *event,
                         gpointer   unused)
{
153 154 155
  GHashTableIter iter;
  gboolean theme_was_open = FALSE;
  gpointer theme;
156 157 158 159 160 161 162 163 164
  MSG *msg;

  if (!GDK_IS_WIN32_WINDOW (event->any.window))
    return GDK_FILTER_CONTINUE;

  msg = (MSG *) xevent;
  if (msg->message != WM_THEMECHANGED)
    return GDK_FILTER_CONTINUE;

165 166 167 168 169 170
  g_hash_table_iter_init (&iter, themes_by_class);
  while (g_hash_table_iter_next (&iter, NULL, &theme))
    {
      theme_was_open |= gtk_win32_theme_close (theme);
    }
  if (theme_was_open)
171 172 173 174 175
    gtk_style_context_reset_widgets (gdk_display_get_default_screen (gdk_window_get_display (event->any.window)));

  return GDK_FILTER_CONTINUE;
}

176
static void
177
gtk_win32_theme_init (void)
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
{
  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");
212
      OpenThemeData = (OpenThemeDataFunc) GetProcAddress (uxtheme_dll, "OpenThemeData");
213
      CloseThemeData = (CloseThemeDataFunc) GetProcAddress (uxtheme_dll, "CloseThemeData");
214 215 216
      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");
217
      GetThemeSysColor = (GetThemeSysColorFunc) GetProcAddress (uxtheme_dll, "GetThemeSysColor");
218
      GetThemeSysSize = (GetThemeSysSizeFunc) GetProcAddress (uxtheme_dll, "GetThemeSysSize");
219 220
      is_theme_partially_transparent = (IsThemeBackgroundPartiallyTransparentFunc) GetProcAddress (uxtheme_dll, "IsThemeBackgroundPartiallyTransparent");
      draw_theme_parent_background = (DrawThemeParentBackgroundFunc) GetProcAddress (uxtheme_dll, "DrawThemeParentBackground");
221
      GetThemePartSize = (GetThemePartSizeFunc) GetProcAddress (uxtheme_dll, "GetThemePartSize");
222
      GetThemeBackgroundExtent = (GetThemeBackgroundExtentFunc) GetProcAddress (uxtheme_dll, "GetThemeBackgroundExtent");
223 224 225 226 227 228 229 230 231 232 233
    }

  if (is_app_themed && is_theme_active)
    {
      use_xp_theme = (is_app_themed () && is_theme_active ());
    }
  else
    {
      use_xp_theme = FALSE;
    }

234
  gdk_window_add_filter (NULL, invalidate_win32_themes, NULL);
235 236
}

237 238
static HTHEME
gtk_win32_theme_get_htheme (GtkWin32Theme *theme)
239 240 241
{
  guint16 *wclass;
  
242 243
  gtk_win32_theme_init ();

244 245
  if (theme->htheme)
    return theme->htheme;
246

247 248
  wclass = g_utf8_to_utf16 (theme->class_name, -1, NULL, NULL, NULL);
  theme->htheme  = OpenThemeData (NULL, wclass);
249 250
  g_free (wclass);

251
  return theme->htheme;
252 253
}

254
#endif /* G_OS_WIN32 */
255

256 257 258 259 260 261 262 263
static char *
canonicalize_class_name (const char *classname)
{
  /* Wine claims class names are case insensitive, so we convert them
     here to avoid multiple theme objects referencing the same HTHEME. */
  return g_ascii_strdown (classname, -1);
}

264
GtkWin32Theme *
265
gtk_win32_theme_lookup (const char *classname)
266
{
267
  GtkWin32Theme *theme;
268
  char *canonical_classname;
269

270 271 272
  if (G_UNLIKELY (themes_by_class == NULL))
    themes_by_class = g_hash_table_new (g_str_hash, g_str_equal);

273 274
  canonical_classname = canonicalize_class_name (classname);
  theme = g_hash_table_lookup (themes_by_class, canonical_classname);
275 276

  if (theme != NULL)
277 278 279 280
    {
      g_free (canonical_classname);
      return gtk_win32_theme_ref (theme);
    }
281 282 283

  theme = g_slice_new0 (GtkWin32Theme);
  theme->ref_count = 1;
284
  theme->class_name = canonical_classname;
285 286 287 288 289

  g_hash_table_insert (themes_by_class, theme->class_name, theme);

  return theme;
}
290

291 292 293 294
GtkWin32Theme *
gtk_win32_theme_parse (GtkCssParser *parser)
{
  GtkWin32Theme *theme;
295
  char *class_name;
296 297 298 299 300 301 302 303

  class_name = _gtk_css_parser_try_name (parser, TRUE);
  if (class_name == NULL)
    {
      _gtk_css_parser_error (parser, "Expected valid win32 theme name");
      return NULL;
    }

304
  theme = gtk_win32_theme_lookup (class_name);
305 306 307 308 309
  g_free (class_name);

  return theme;
}

310
cairo_surface_t *
311 312 313 314 315 316 317 318
gtk_win32_theme_create_surface (GtkWin32Theme *theme,
                                int            xp_part,
				int            state,
				int            margins[4],
				int            width,
                                int            height,
				int           *x_offs_out,
				int           *y_offs_out)
319 320
{
  cairo_surface_t *surface;
321
  cairo_t *cr;
322 323
  int x_offs;
  int y_offs;
324
#ifdef G_OS_WIN32
325
  gboolean has_alpha;
326 327
  HDC hdc;
  RECT rect;
328
  SIZE size;
329
  HRESULT res;
330
  HTHEME htheme;
331
#endif
332

333 334 335 336 337 338 339
  x_offs = margins[3];
  y_offs = margins[0];

  width -= margins[3] + margins[1];
  height -= margins[0] + margins[2];

#ifdef G_OS_WIN32
340 341
  htheme = gtk_win32_theme_get_htheme (theme);
  if (htheme)
342
    {
343 344
      rect.left = 0;
      rect.top = 0;
345 346 347
      rect.right = width;
      rect.bottom = height;

348
      hdc = GetDC (NULL);
349
      res = GetThemePartSize (htheme, hdc, xp_part, state, &rect, 2 /*TS_DRAW*/, &size);
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
      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;
        }

      has_alpha = is_theme_partially_transparent (htheme, xp_part, state);
      if (has_alpha)
        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);
369

370
      hdc = cairo_win32_surface_get_dc (surface);
371

372
      res = draw_theme_background (htheme, hdc, xp_part, state, &rect, &rect);
373

374 375
      *x_offs_out = x_offs;
      *y_offs_out = y_offs;
376

377 378 379 380
      if (res == S_OK)
        return surface;
    }
  else
381
#endif /* G_OS_WIN32 */
382 383 384
    {
      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
    }
385

386 387
  cr = cairo_create (surface);
  
388
  gtk_win32_draw_theme_background (cr, theme->class_name, xp_part, state, width, height);
389 390 391

  cairo_destroy (cr);
  
392 393 394
  *x_offs_out = x_offs;
  *y_offs_out = y_offs;

395 396 397
  return surface;
}

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
void
gtk_win32_theme_get_part_border (GtkWin32Theme  *theme,
                                 int             part,
                                 int             state,
                                 GtkBorder      *out_border)
{
#ifdef G_OS_WIN32
  HTHEME htheme = gtk_win32_theme_get_htheme (theme);
  RECT content, extent;
  HDC hdc;
  HRESULT res;

  if (use_xp_theme && GetThemeBackgroundExtent != NULL && htheme != NULL)
    {
      /* According to Wine source code, these values don't matter
       * because uxtheme.dll deals with margins internally. */
      content.left = content.top = 0;
      content.right = content.bottom = 100;

      hdc = GetDC (NULL);
      res = GetThemeBackgroundExtent (htheme, hdc, part, state, &content, &extent);
      ReleaseDC (NULL, hdc);

      if (SUCCEEDED (res))
        {
          out_border->top = content.top - extent.top;
          out_border->left = content.left - extent.left;
          out_border->bottom = extent.bottom - content.bottom;
          out_border->right = extent.right - content.right;
          return;
        }
    }
#endif

  gtk_win32_get_theme_margins (theme->class_name, part, state, out_border);
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448
void
gtk_win32_theme_get_part_size (GtkWin32Theme  *theme,
                               int             part,
                               int             state,
                               int            *width,
                               int            *height)
{
#ifdef G_OS_WIN32
  HTHEME htheme = gtk_win32_theme_get_htheme (theme);
  SIZE size;
  HRESULT res;

  if (use_xp_theme && GetThemePartSize != NULL && htheme != NULL)
    {
449
      res = GetThemePartSize (htheme, NULL, part, state, NULL, 1 /* TS_TRUE */, &size);
450 451 452 453 454 455 456 457 458 459 460

      if (SUCCEEDED (res))
        {
          if (width)
            *width = size.cx;
          if (height)
            *height = size.cy;
          return;
        }
    }
#endif
461
  gtk_win32_get_theme_part_size (theme->class_name, part, state, width, height);
462 463
}

464
int
465 466
gtk_win32_theme_get_size (GtkWin32Theme *theme,
			  int            id)
467
{
468
#ifdef G_OS_WIN32
469
  if (use_xp_theme && GetThemeSysSize != NULL)
470
    {
471
      HTHEME htheme = gtk_win32_theme_get_htheme (theme);
472
      int size;
473

474
      /* If htheme is NULL it will just return the GetSystemMetrics value */
475 476 477 478
      size = GetThemeSysSize (htheme, id);
      /* fall through on invalid parameter */
      if (GetLastError () == 0)
        return size;
479
    }
480 481

  return GetSystemMetrics (id);
482
#else
483
  return gtk_win32_get_sys_metric (id);
484
#endif
485
}
486

487 488 489 490
void
gtk_win32_theme_get_color (GtkWin32Theme *theme,
                           gint           id,
                           GdkRGBA       *color)
491 492
{
#ifdef G_OS_WIN32
493
  HTHEME htheme;
494 495
  DWORD dcolor;

496
  if (use_xp_theme && GetThemeSysColor != NULL)
497
    {
498
      htheme = gtk_win32_theme_get_htheme (theme);
499

500 501
      /* if htheme is NULL, it will just return the GetSysColor() value */
      dcolor = GetThemeSysColor (htheme, id);
502 503 504 505 506 507 508 509 510
    }
  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
511
  gtk_win32_get_sys_color (id, color);
512 513
#endif
}
514

515 516 517 518 519 520
void
gtk_win32_theme_print (GtkWin32Theme *theme,
                       GString       *string)
{
  g_string_append (string, theme->class_name);
}