<commit_before>/* MS-Windows Engine (aka GTK-Wimp)
 *
 * Copyright (C) 2003, 2004 <NAME> <<EMAIL>>
 * Copyright (C) 2006 <NAME> (PCMan) <<EMAIL>>
 * Includes code adapted from redmond95 by <NAME>, and
 * gtk-nativewin by Evan Martin
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Useful resources:
 *
 *  http://lxr.mozilla.org/seamonkey/source/widget/src/windows/nsNativeThemeWin.cpp
 *  http://lxr.mozilla.org/seamonkey/source/widget/src/windows/nsLookAndFeel.cpp
 *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/functions/drawthemebackground.asp
 *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/pantdraw_4b3g.asp
 */

/* Include first, else we get redefinition warnings about STRICT */
#include "pango/pangowin32.h"

#include "msw_style.h"
#include "xp_theme.h"

#include <windows.h>
#include <math.h>
#include <string.h>
#include <stdio.h>

#include "gdk/gdk.h"
#include "gtk/gtk.h"

#ifdef BUILDING_STANDALONE
#include "gdk/gdkwin32.h"
#else
#include "gdk/win32/gdkwin32.h"
#endif


#define DETAIL(xx)   ((detail) && (!strcmp(xx, detail)))


/* Default values, not normally used
 */
static const GtkRequisition default_option_indicator_size = { 9, 8 };
static const GtkBorder default_option_indicator_spacing = { 7, 5, 2, 2 };

static GtkStyleClass *parent_class;
static HBRUSH g_dither_brush = NULL;

static HPEN g_light_pen = NULL;
static HPEN g_dark_pen = NULL;

typedef enum
{
  CHECK_AA,
  CHECK_BASE,
  CHECK_BLACK,
  CHECK_DARK,
  CHECK_LIGHT,
  CHECK_MID,
  CHECK_TEXT,
  CHECK_INCONSISTENT,
  RADIO_BASE,
  RADIO_BLACK,
  RADIO_DARK,
  RADIO_LIGHT,
  RADIO_MID,
  RADIO_TEXT
} Part;

#define PART_SIZE 13

static const unsigned char check_aa_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_base_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_black_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_dark_bits[] = {
  0xff, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00
};
static const unsigned char check_light_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0xfe, 0x1f, 0x00, 0x00
};
static const unsigned char check_mid_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_text_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
  0x00, 0x03, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00,
  0xd8, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
  0x70, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_inconsistent_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xf0, 0x03, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_base_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_black_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0x0c, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_dark_bits[] = {
  0xf0, 0x01, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_light_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00
};
static const unsigned char radio_mid_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
  0x0c, 0x06, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_text_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xe0, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};

static struct
{
  const unsigned char *bits;
  cairo_surface_t *bmap;
} parts[] = {
  { check_aa_bits, NULL           },
  { check_base_bits, NULL         },
  { check_black_bits, NULL        },
  { check_dark_bits, NULL         },
  { check_light_bits, NULL        },
  { check_mid_bits, NULL          },
  { check_text_bits, NULL         },
  { check_inconsistent_bits, NULL },
  { radio_base_bits, NULL         },
  { radio_black_bits, NULL        },
  { radio_dark_bits, NULL         },
  { radio_light_bits, NULL        },
  { radio_mid_bits, NULL          },
  { radio_text_bits, NULL         }
};

static void
_cairo_draw_line (cairo_t  *cr,
                  GdkColor *color,
                  gint      x1,
                  gint      y1,
                  gint      x2,
                  gint      y2)
{
  cairo_save (cr);

  gdk_cairo_set_source_color (cr, color);
  cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
  cairo_set_line_width (cr, 1.0);

  cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
  cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
  cairo_stroke (cr);

  cairo_restore (cr);
}

static void
_cairo_draw_rectangle (cairo_t *cr,
                       GdkColor *color,
                       gboolean filled,
                       gint x,
                       gint y,
                       gint width,
                       gint height)
{
  gdk_cairo_set_source_color (cr, color);

  if (filled)
    {
      cairo_rectangle (cr, x, y, width, height);
      cairo_fill (cr);
    }
  else
    {
      cairo_rectangle (cr, x + 0.5, y + 0.5, width, height);
      cairo_stroke (cr);
    }
}

static gboolean
get_system_font (XpThemeClass klazz, XpThemeFont type, LOGFONTW *out_lf)
{
  if (xp_theme_get_system_font (klazz, type, out_lf))
    {
      return TRUE;
    }
  else
    {
      /* Use wide char versions here, as the theming functions only support
       * wide chars versions of the structures. */
      NONCLIENTMETRICSW ncm;

      ncm.cbSize = sizeof (NONCLIENTMETRICSW);

      if (SystemParametersInfoW (SPI_GETNONCLIENTMETRICS,
				sizeof (NONCLIENTMETRICSW), &ncm, 0))
	{
	  if (type == XP_THEME_FONT_CAPTION)
	    *out_lf = ncm.lfCaptionFont;
	  else if (type == XP_THEME_FONT_MENU)
	    *out_lf = ncm.lfMenuFont;
	  else if (type == XP_THEME_FONT_STATUS)
	    *out_lf = ncm.lfStatusFont;
	  else
	    *out_lf = ncm.lfMessageFont;

	  return TRUE;
	}
    }

  return FALSE;
}

static char *
sys_font_to_pango_font (XpThemeClass klazz, XpThemeFont type, char *buf,
			size_t bufsiz)
{
  LOGFONTW lf;

  if (get_system_font (klazz, type, &lf))
    {
      PangoFontDescription *desc = NULL;
      int pt_size;
      const char *font;

      desc = pango_win32_font_description_from_logfontw (&lf);
      if (!desc)
	return NULL;

      font = pango_font_description_to_string (desc);
      pt_size = pango_font_description_get_size (desc);

      if (!(font && *font))
	{
	  pango_font_description_free (desc);
	  return NULL;
	}

      if (pt_size == 0)
	{
	  HDC hDC;
	  HWND hwnd;

	  hwnd = GetDesktopWindow ();
	  hDC = GetDC (hwnd);

	  if (hDC)
	    pt_size = -MulDiv (lf.lfHeight, 72, GetDeviceCaps (hDC, LOGPIXELSY));
	  else
	    pt_size = 10;

	  if (hDC)
	    ReleaseDC (hwnd, hDC);

	  g_snprintf (buf, bufsiz, "%s %d", font, pt_size);
	}
      else
	{
	  g_snprintf (buf, bufsiz, "%s", font);
	}

      if (desc)
	pango_font_description_free (desc);

      return buf;
    }

  return NULL;
}

/* missing from ms's header files */
#ifndef SPI_GETMENUSHOWDELAY
#define SPI_GETMENUSHOWDELAY 106
#endif

/* I don't know the proper XP theme class for things like
   HIGHLIGHTTEXT, so we'll just define it to be "BUTTON"
   for now */
#define XP_THEME_CLASS_TEXT XP_THEME_CLASS_BUTTON

#define WIN95_VERSION   0x400
#define WIN2K_VERSION   0x500
#define WINXP_VERSION   0x501
#define WIN2K3_VERSION  0x502
#define VISTA_VERSION   0x600

static gint32
get_windows_version ()
{
  static gint32 version = 0;
  static gboolean have_version = FALSE;

  if (!have_version)
    {
      OSVERSIONINFOEX osvi;
      have_version = TRUE;

      ZeroMemory (&osvi, sizeof (OSVERSIONINFOEX));
      osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);

      GetVersionEx((OSVERSIONINFO*) &osvi);

      version = (osvi.dwMajorVersion & 0xff) << 8 | (osvi.dwMinorVersion & 0xff);
    }

  return version;
}

static void
setup_menu_settings (GtkSettings *settings)
{
  int menu_delay;
  GObjectClass *klazz = G_OBJECT_GET_CLASS (G_OBJECT (settings));

  if (get_windows_version () > WIN95_VERSION)
    {
      if (SystemParametersInfo (SPI_GETMENUSHOWDELAY, 0, &menu_delay, 0))
	{
	  if (klazz)
	    {
	      if (g_object_class_find_property
		  (klazz, "gtk-menu-bar-popup-delay"))
		{
		  g_object_set (settings,
				"gtk-menu-bar-popup-delay", 0, NULL);
		}
	      if (g_object_class_find_property
		  (klazz, "gtk-menu-popup-delay"))
		{
		  g_object_set (settings,
				"gtk-menu-popup-delay", menu_delay, NULL);
		}
	      if (g_object_class_find_property
		  (klazz, "gtk-menu-popdown-delay"))
		{
		  g_object_set (settings,
				"gtk-menu-popdown-delay", menu_delay, NULL);
		}
	    }
	}
    }
}

void
msw_style_setup_system_settings (void)
{
  GtkSettings *settings;
  int cursor_blink_time;

  settings = gtk_settings_get_default ();
  if (!settings)
    return;

  cursor_blink_time = GetCaretBlinkTime ();
  g_object_set (settings, "gtk-cursor-blink", cursor_blink_time > 0, NULL);

  if (cursor_blink_time > 0)
    {
      g_object_set (settings, "gtk-cursor-blink-time",
		    2 * cursor_blink_time, NULL);
    }

  g_object_set (settings, "gtk-double-click-distance",
		GetSystemMetrics (SM_CXDOUBLECLK), NULL);
  g_object_set (settings, "gtk-double-click-time", GetDoubleClickTime (),
		NULL);
  g_object_set (settings, "gtk-dnd-drag-threshold",
		GetSystemMetrics (SM_CXDRAG), NULL);

  setup_menu_settings (settings);

  /*
     http://developer.gnome.org/doc/API/2.0/gtk/GtkSettings.html
     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/systemparametersinfo.asp
     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getsystemmetrics.asp */
}

static void
setup_system_font (GtkStyle *style)
{
  char buf[256], *font;		/* It's okay, lfFaceName is smaller than 32
				   chars */

  if ((font = sys_font_to_pango_font (XP_THEME_CLASS_TEXT,
				      XP_THEME_FONT_MESSAGE,
				      buf, sizeof (buf))) != NULL)
    {
      if (style->font_desc)
	{
	  pango_font_description_free (style->font_desc);
	}

      style->font_desc = pango_font_description_from_string (font);
    }
}

static void
sys_color_to_gtk_color (XpThemeClass klazz, int id, GdkColor * pcolor)
{
  DWORD color;

  if (!xp_theme_get_system_color (klazz, id, &color))
    color = GetSysColor (id);

  pcolor->pixel = color;
  pcolor->red = (GetRValue (color) << 8) | GetRValue (color);
  pcolor->green = (GetGValue (color) << 8) | GetGValue (color);
  pcolor->blue = (GetBValue (color) << 8) | GetBValue (color);
}

static int
get_system_metric (XpThemeClass klazz, int id)
{
  int rval;

  if (!xp_theme_get_system_metric (klazz, id, &rval))
    rval = GetSystemMetrics (id);

  return rval;
}

static void
setup_msw_rc_style (void)
{
  char buf[1024], font_buf[256], *font_ptr;
  char menu_bar_prelight_str[128];

  GdkColor menu_color;
  GdkColor menu_text_color;
  GdkColor tooltip_back;
  GdkColor tooltip_fore;
  GdkColor btn_fore;
  GdkColor btn_face;
  GdkColor progress_back;

  GdkColor fg_prelight;
  GdkColor bg_prelight;
  GdkColor base_prelight;
  GdkColor text_prelight;

  /* Prelight */
  sys_color_to_gtk_color (get_windows_version () >= VISTA_VERSION ? XP_THEME_CLASS_MENU : XP_THEME_CLASS_TEXT,
			  get_windows_version () >= VISTA_VERSION ? COLOR_MENUTEXT : COLOR_HIGHLIGHTTEXT,
			  &fg_prelight);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT, &bg_prelight);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT,
			  &base_prelight);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT,
			  &text_prelight);

  sys_color_to_gtk_color (XP_THEME_CLASS_MENU, COLOR_MENUTEXT,
			  &menu_text_color);
  sys_color_to_gtk_color (XP_THEME_CLASS_MENU, COLOR_MENU, &menu_color);

  /* tooltips */
  sys_color_to_gtk_color (XP_THEME_CLASS_TOOLTIP, COLOR_INFOTEXT,
			  &tooltip_fore);
  sys_color_to_gtk_color (XP_THEME_CLASS_TOOLTIP, COLOR_INFOBK,
			  &tooltip_back);

  /* text on push buttons. TODO: button shadows, backgrounds, and
     highlights */
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT, &btn_fore);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE, &btn_face);

  /* progress bar background color */
  sys_color_to_gtk_color (XP_THEME_CLASS_PROGRESS, COLOR_HIGHLIGHT,
			  &progress_back);

  /* Enable coloring for menus. */
  font_ptr =
    sys_font_to_pango_font (XP_THEME_CLASS_MENU, XP_THEME_FONT_MENU,
			    font_buf, sizeof (font_buf));
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-menu\" = \"msw-default\"\n" "{\n"
	      "GtkMenuItem::toggle-spacing = 8\n"
	      "fg[PRELIGHT] = { %d, %d, %d }\n"
	      "bg[PRELIGHT] = { %d, %d, %d }\n"
	      "text[PRELIGHT] = { %d, %d, %d }\n"
	      "base[PRELIGHT] = { %d, %d, %d }\n"
	      "fg[NORMAL] = { %d, %d, %d }\n"
	      "bg[NORMAL] = { %d, %d, %d }\n" "%s = \"%s\"\n"
	      "}widget_class \"*MenuItem*\" style \"msw-menu\"\n"
	      "widget_class \"*GtkMenu\" style \"msw-menu\"\n"
	      "widget_class \"*GtkMenuShell*\" style \"msw-menu\"\n",
	      fg_prelight.red, fg_prelight.green, fg_prelight.blue,
	      bg_prelight.red, bg_prelight.green, bg_prelight.blue,
	      text_prelight.red, text_prelight.green, text_prelight.blue,
	      base_prelight.red, base_prelight.green, base_prelight.blue,
	      menu_text_color.red, menu_text_color.green,
	      menu_text_color.blue, menu_color.red, menu_color.green,
	      menu_color.blue, (font_ptr ? "font_name" : "#"),
	      (font_ptr ? font_ptr : " font name should go here"));
  gtk_rc_parse_string (buf);

  if (xp_theme_is_active ())
    {
      *menu_bar_prelight_str = '\0';
    }
  else
    {
      g_snprintf (menu_bar_prelight_str, sizeof (menu_bar_prelight_str),
		  "fg[PRELIGHT] = { %d, %d, %d }\n",
		  menu_text_color.red, menu_text_color.green,
		  menu_text_color.blue);
    }

  /* Enable coloring for menu bars. */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-menu-bar\" = \"msw-menu\"\n"
	      "{\n"
	      "bg[NORMAL] = { %d, %d, %d }\n"
	      "%s" "GtkMenuBar::shadow-type = %d\n"
	      /*
	         FIXME: This should be enabled once gtk+ support
	         GtkMenuBar::prelight-item style property.
	       */
	      /* "GtkMenuBar::prelight-item = 1\n" */
	      "}widget_class \"*MenuBar*\" style \"msw-menu-bar\"\n",
	      btn_face.red, btn_face.green, btn_face.blue,
	      menu_bar_prelight_str, xp_theme_is_active ()? 0 : 2);
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-toolbar\" = \"msw-default\"\n"
	      "{\n"
	      "GtkHandleBox::shadow-type = %s\n"
	      "GtkToolbar::shadow-type = %s\n"
	      "}widget_class \"*HandleBox*\" style \"msw-toolbar\"\n",
	      "etched-in", "etched-in");
  gtk_rc_parse_string (buf);

  /* enable tooltip fonts */
  font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_STATUS, XP_THEME_FONT_STATUS,
				     font_buf, sizeof (font_buf));
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-tooltips-caption\" = \"msw-default\"\n"
	      "{fg[NORMAL] = { %d, %d, %d }\n" "%s = \"%s\"\n"
	      "}widget \"gtk-tooltips.GtkLabel\" style \"msw-tooltips-caption\"\n"
	      "widget \"gtk-tooltip.GtkLabel\" style \"msw-tooltips-caption\"\n",
	      tooltip_fore.red, tooltip_fore.green, tooltip_fore.blue,
	      (font_ptr ? "font_name" : "#"),
	      (font_ptr ? font_ptr : " font name should go here"));
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-tooltips\" = \"msw-default\"\n"
	      "{bg[NORMAL] = { %d, %d, %d }\n"
	      "}widget \"gtk-tooltips*\" style \"msw-tooltips\"\n"
	      "widget \"gtk-tooltip*\" style \"msw-tooltips\"\n",
	      tooltip_back.red, tooltip_back.green, tooltip_back.blue);
  gtk_rc_parse_string (buf);

  /* enable font theming for status bars */
  font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_STATUS, XP_THEME_FONT_STATUS,
				     font_buf, sizeof (font_buf));
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-status\" = \"msw-default\"\n" "{%s = \"%s\"\n"
	      "bg[NORMAL] = { %d, %d, %d }\n"
	      "}widget_class \"*Status*\" style \"msw-status\"\n",
	      (font_ptr ? "font_name" : "#"),
	      (font_ptr ? font_ptr : " font name should go here"),
	      btn_face.red, btn_face.green, btn_face.blue);
  gtk_rc_parse_string (buf);

  /* enable coloring for text on buttons
   * TODO: use GetThemeMetric for the border and outside border */
  g_snprintf (buf, sizeof (buf),
              "style \"msw-button\" = \"msw-default\"\n"
              "{\n"
              "bg[NORMAL] = { %d, %d, %d }\n"
              "bg[PRELIGHT] = { %d, %d, %d }\n"
              "bg[INSENSITIVE] = { %d, %d, %d }\n"
              "fg[PRELIGHT] = { %d, %d, %d }\n"
              "GtkButton::default-border = { 0, 0, 0, 0 }\n"
              "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
              "GtkButton::child-displacement-x = %d\n"
              "GtkButton::child-displacement-y = %d\n"
              "GtkWidget::focus-padding = %d\n"
              "}widget_class \"*Button*\" style \"msw-button\"\n",
              btn_face.red, btn_face.green, btn_face.blue,
              btn_face.red, btn_face.green, btn_face.blue,
              btn_face.red, btn_face.green, btn_face.blue,
              btn_fore.red, btn_fore.green, btn_fore.blue,
              xp_theme_is_active ()? 0 : 1,
              xp_theme_is_active ()? 0 : 1,
              xp_theme_is_active ()? 1 : 2);
  gtk_rc_parse_string (buf);

  /* enable coloring for progress bars */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-progress\" = \"msw-default\"\n"
	      "{bg[PRELIGHT] = { %d, %d, %d }\n"
	      "bg[NORMAL] = { %d, %d, %d }\n"
	      "}widget_class \"*Progress*\" style \"msw-progress\"\n",
	      progress_back.red,
	      progress_back.green,
	      progress_back.blue,
	      btn_face.red, btn_face.green, btn_face.blue);
  gtk_rc_parse_string (buf);

  /* scrollbar thumb width and height */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-vscrollbar\" = \"msw-default\"\n"
	      "{GtkRange::slider-width = %d\n"
	      "GtkRange::stepper-size = %d\n"
	      "GtkRange::stepper-spacing = 0\n"
	      "GtkRange::trough_border = 0\n"
	      "GtkScale::slider-length = %d\n"
	      "GtkScrollbar::min-slider-length = 8\n"
	      "}widget_class \"*VScrollbar*\" style \"msw-vscrollbar\"\n"
	      "widget_class \"*VScale*\" style \"msw-vscrollbar\"\n",
	      GetSystemMetrics (SM_CYVTHUMB),
	      get_system_metric (XP_THEME_CLASS_SCROLLBAR, SM_CXVSCROLL), 11);
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-hscrollbar\" = \"msw-default\"\n"
	      "{GtkRange::slider-width = %d\n"
	      "GtkRange::stepper-size = %d\n"
	      "GtkRange::stepper-spacing = 0\n"
	      "GtkRange::trough_border = 0\n"
	      "GtkScale::slider-length = %d\n"
	      "GtkScrollbar::min-slider-length = 8\n"
	      "}widget_class \"*HScrollbar*\" style \"msw-hscrollbar\"\n"
	      "widget_class \"*HScale*\" style \"msw-hscrollbar\"\n",
	      GetSystemMetrics (SM_CXHTHUMB),
	      get_system_metric (XP_THEME_CLASS_SCROLLBAR, SM_CYHSCROLL), 11);
  gtk_rc_parse_string (buf);

  gtk_rc_parse_string ("style \"msw-scrolled-window\" = \"msw-default\"\n"
		       "{GtkScrolledWindow::scrollbars-within-bevel = 1}\n"
		       "class \"GtkScrolledWindow\" style \"msw-scrolled-window\"\n");

  /* radio/check button sizes */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-checkbutton\" = \"msw-button\"\n"
	      "{GtkCheckButton::indicator-size = 13\n"
	      "}widget_class \"*CheckButton*\" style \"msw-checkbutton\"\n"
	      "widget_class \"*RadioButton*\" style \"msw-checkbutton\"\n");
  gtk_rc_parse_string (buf);

  /* size of combo box toggle button */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-combobox-button\" = \"msw-default\"\n"
	      "{\n"
	      "xthickness = 0\n"
	      "ythickness = 0\n"
	      "GtkButton::default-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::child-displacement-x = 0\n"
	      "GtkButton::child-displacement-y = 0\n"
	      "GtkWidget::focus-padding = 0\n"
	      "GtkWidget::focus-line-width = 0\n"
	      "}\n"
	      "widget_class \"*ComboBox*ToggleButton*\" style \"msw-combobox-button\"\n");
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-combobox\" = \"msw-default\"\n"
	      "{\n"
	      "GtkComboBox::shadow-type = in\n"
	      "xthickness = %d\n"
	      "ythickness = %d\n"
	      "}\n"
	      "class \"GtkComboBox\" style \"msw-combobox\"\n",
        xp_theme_is_active()? 1 : GetSystemMetrics (SM_CXEDGE),
        xp_theme_is_active()? 1 : GetSystemMetrics (SM_CYEDGE));
  gtk_rc_parse_string (buf);

  /* size of tree view header */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-header-button\" = \"msw-default\"\n"
	      "{\n"
	      "xthickness = 0\n"
	      "ythickness = 0\n"
	      "GtkWidget::draw-border = {0, 0, 0, 0}\n"
        "GtkButton::default-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::child-displacement-x = 0\n"
	      "GtkButton::child-displacement-y = 0\n"
	      "GtkWidget::focus-padding = 0\n"
	      "GtkWidget::focus-line-width = 0\n"
	      "}\n"
	      "widget_class \"*TreeView*Button*\" style \"msw-header-button\"\n");
  gtk_rc_parse_string (buf);

  /* FIXME: This should be enabled once gtk+ support GtkNotebok::prelight-tab */
  /* enable prelight tab of GtkNotebook */
  /*
     g_snprintf (buf, sizeof (buf),
     "style \"msw-notebook\" = \"msw-default\"\n"
     "{GtkNotebook::prelight-tab=1\n"
     "}widget_class \"*Notebook*\" style \"msw-notebook\"\n");
     gtk_rc_parse_string (buf);
   */

  /* FIXME: This should be enabled once gtk+ support GtkTreeView::full-row-focus */
  /*
     g_snprintf (buf, sizeof (buf),
     "style \"msw-treeview\" = \"msw-default\"\n"
     "{GtkTreeView::full-row-focus=0\n"
     "}widget_class \"*TreeView*\" style \"msw-treeview\"\n");
     gtk_rc_parse_string (buf);
   */
}

static void
setup_system_styles (GtkStyle *style)
{
  int i;

  /* Default background */
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT,
			  &style->bg[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_PRELIGHT]);

  /* Default base */
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOW,
			  &style->base[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT,
			  &style->base[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->base[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->base[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOW,
			  &style->base[GTK_STATE_PRELIGHT]);

  /* Default text */
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT,
			  &style->text[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT,
			  &style->text[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_GRAYTEXT,
			  &style->text[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT,
			  &style->text[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT,
			  &style->text[GTK_STATE_PRELIGHT]);

  /* Default foreground */
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT,
			  &style->fg[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT,
			  &style->fg[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_GRAYTEXT,
			  &style->fg[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT,
                          &style->fg[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT,
			  &style->fg[GTK_STATE_PRELIGHT]);

  for (i = 0; i < 5; i++)
    {
      sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_3DSHADOW,
			      &style->dark[i]);
      sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_3DHILIGHT,
			      &style->light[i]);

      style->mid[i].red = (style->light[i].red + style->dark[i].red) / 2;
      style->mid[i].green =
	(style->light[i].green + style->dark[i].green) / 2;
      style->mid[i].blue = (style->light[i].blue + style->dark[i].blue) / 2;

      style->text_aa[i].red = (style->text[i].red + style->base[i].red) / 2;
      style->text_aa[i].green =
	(style->text[i].green + style->base[i].green) / 2;
      style->text_aa[i].blue =
	(style->text[i].blue + style->base[i].blue) / 2;
    }
}

static gboolean
sanitize_size (GdkWindow *window, gint *width, gint *height)
{
  gboolean set_bg = FALSE;

  if ((*width == -1) && (*height == -1))
    {
      set_bg = GDK_IS_WINDOW (window);
      gdk_drawable_get_size (window, width, height);
    }
  else if (*width == -1)
    {
      gdk_drawable_get_size (window, width, NULL);
    }
  else if (*height == -1)
    {
      gdk_drawable_get_size (window, NULL, height);
    }

  return set_bg;
}

static XpThemeElement
map_gtk_progress_bar_to_xp (GtkProgressBar *progress_bar, gboolean trough)
{
  XpThemeElement ret;

  switch (gtk_progress_bar_get_orientation (progress_bar))
    {
    case GTK_PROGRESS_LEFT_TO_RIGHT:
    case GTK_PROGRESS_RIGHT_TO_LEFT:
      ret = trough
	? XP_THEME_ELEMENT_PROGRESS_TROUGH_H
	: XP_THEME_ELEMENT_PROGRESS_BAR_H;
      break;

    default:
      ret = trough
	? XP_THEME_ELEMENT_PROGRESS_TROUGH_V
	: XP_THEME_ELEMENT_PROGRESS_BAR_V;
      break;
    }

  return ret;
}

static gboolean
is_combo_box_child (GtkWidget *w)
{
  GtkWidget *tmp;

  if (w == NULL)
    return FALSE;

  for (tmp = w->parent; tmp; tmp = tmp->parent)
    {
      if (GTK_IS_COMBO_BOX (tmp))
	return TRUE;
    }

  return FALSE;
}

static void
draw_part (GdkDrawable *drawable,
           GdkColor *gc, GdkRectangle *area, gint x, gint y, Part part)
{
  cairo_t *cr = gdk_cairo_create (drawable);

  if (area)
    {
      gdk_cairo_rectangle (cr, area);
      cairo_clip (cr);
    }

  if (!parts[part].bmap)
    {
      parts[part].bmap = cairo_image_surface_create_for_data ((unsigned char *)parts[part].bits,
        					              CAIRO_FORMAT_A1,
        					              PART_SIZE, PART_SIZE, 4);
    }

  gdk_cairo_set_source_color (cr, gc);
  cairo_mask_surface (cr, parts[part].bmap, x, y);

  cairo_destroy(cr);
}

static void
draw_check (GtkStyle *style,
	    GdkWindow *window,
	    GtkStateType state,
	    GtkShadowType shadow,
	    GdkRectangle *area,
	    GtkWidget *widget,
	    const gchar *detail, gint x, gint y, gint width, gint height)
{
  x -= (1 + PART_SIZE - width) / 2;
  y -= (1 + PART_SIZE - height) / 2;

  if (DETAIL("check"))	/* Menu item */
    {
      if (shadow == GTK_SHADOW_IN)
	{
          draw_part (window, &style->black, area, x, y, CHECK_TEXT);
          draw_part (window, &style->dark[state], area, x, y, CHECK_AA);
	}
    }
  else
    {
      XpThemeElement theme_elt = XP_THEME_ELEMENT_CHECKBOX;
      switch (shadow)
	{
	case GTK_SHADOW_ETCHED_IN:
	  theme_elt = XP_THEME_ELEMENT_INCONSISTENT_CHECKBOX;
	  break;

	case GTK_SHADOW_IN:
	  theme_elt = XP_THEME_ELEMENT_PRESSED_CHECKBOX;
	  break;

	default:
	  break;
	}

      if (!xp_theme_draw (window, theme_elt,
			  style, x, y, width, height, state, area))
	{
	  if (DETAIL("cellcheck"))
	    state = GTK_STATE_NORMAL;

          draw_part (window, &style->black, area, x, y, CHECK_BLACK);
          draw_part (window, &style->dark[state], area, x, y, CHECK_DARK);
          draw_part (window, &style->mid[state], area, x, y, CHECK_MID);
          draw_part (window, &style->light[state], area, x, y, CHECK_LIGHT);
          draw_part (window, &style->base[state], area, x, y, CHECK_BASE);

	  if (shadow == GTK_SHADOW_IN)
	    {
              draw_part (window, &style->text[state], area, x,
			 y, CHECK_TEXT);
              draw_part (window, &style->text_aa[state], area,
			 x, y, CHECK_AA);
	    }
	  else if (shadow == GTK_SHADOW_ETCHED_IN)
	    {
              draw_part (window, &style->text[state], area, x, y,
			 CHECK_INCONSISTENT);
              draw_part (window, &style->text_aa[state], area, x, y,
			 CHECK_AA);
	    }
	}
    }
}

static void
draw_expander (GtkStyle        *style,
               GdkWindow       *window,
               GtkStateType     state,
               GdkRectangle    *area,
               GtkWidget       *widget,
               const gchar     *detail,
               gint             x,
               gint             y,
               GtkExpanderStyle expander_style)
{
  cairo_t *cr = gdk_cairo_create (window);

  gint expander_size;
  gint expander_semi_size;
  XpThemeElement xp_expander;
  GtkOrientation orientation;

  gtk_widget_style_get (widget, "expander_size", &expander_size, NULL);

  if (DETAIL("tool-palette-header"))
    {
      /* Expanders are usually drawn as little triangles and unfortunately
       * do not support rotated drawing modes. So a hack is applied (see
       * gtk_tool_item_group_header_expose_event_cb for details) when
       * drawing a GtkToolItemGroup's header for horizontal GtkToolShells,
       * forcing the triangle to point in the right direction. Except we
       * don't draw expanders as triangles on Windows. Usually, expanders
       * are represented as "+" and "-". It sucks for "+" to become "-" and
       * the inverse when we don't want to, so reverse the hack here. */

      orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (widget));

      if (orientation == GTK_ORIENTATION_HORIZONTAL)
          expander_style = GTK_EXPANDER_EXPANDED - expander_style;
    }

  switch (expander_style)
    {
    case GTK_EXPANDER_COLLAPSED:
    case GTK_EXPANDER_SEMI_COLLAPSED:
      xp_expander = XP_THEME_ELEMENT_TREEVIEW_EXPANDER_CLOSED;
      break;

    case GTK_EXPANDER_EXPANDED:
    case GTK_EXPANDER_SEMI_EXPANDED:
      xp_expander = XP_THEME_ELEMENT_TREEVIEW_EXPANDER_OPENED;
      break;

    default:
      g_assert_not_reached ();
    }

  if ((expander_size % 2) == 0)
    expander_size--;

  if (expander_size > 2)
    expander_size -= 2;

  if (area)
    {
      gdk_cairo_rectangle (cr, area);
      cairo_clip (cr);
      gdk_cairo_set_source_color (cr, &style->fg[state]);
    }

  expander_semi_size = expander_size / 2;
  x -= expander_semi_size;
  y -= expander_semi_size;

  if (!xp_theme_draw (window, xp_expander, style,
		      x, y, expander_size, expander_size, state, area))
    {
      HDC dc;
      RECT rect;
      HPEN pen;
      HGDIOBJ old_pen;
      XpDCInfo dc_info;

      dc = get_window_dc (style, window, state, &dc_info, x, y, expander_size,
			  expander_size, &rect);
      FrameRect (dc, &rect, GetSysColorBrush (COLOR_GRAYTEXT));
      InflateRect (&rect, -1, -1);
      FillRect (dc, &rect,
		GetSysColorBrush (state ==
				  GTK_STATE_INSENSITIVE ? COLOR_BTNFACE :
				  COLOR_WINDOW));

      InflateRect (&rect, -1, -1);

      pen = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_WINDOWTEXT));
      old_pen = SelectObject (dc, pen);

      MoveToEx (dc, rect.left, rect.top - 2 + expander_semi_size, NULL);
      LineTo (dc, rect.right, rect.top - 2 + expander_semi_size);

      if (expander_style == GTK_EXPANDER_COLLAPSED ||
	  expander_style == GTK_EXPANDER_SEMI_COLLAPSED)
	{
	  MoveToEx (dc, rect.left - 2 + expander_semi_size, rect.top, NULL);
	  LineTo (dc, rect.left - 2 + expander_semi_size, rect.bottom);
	}

      SelectObject (dc, old_pen);
      DeleteObject (pen);
      release_window_dc (&dc_info);
    }

  cairo_destroy(cr);
}

static void
draw_option (GtkStyle *style,
	     GdkWindow *window,
	     GtkStateType state,
	     GtkShadowType shadow,
	     GdkRectangle *area,
	     GtkWidget *widget,
	     const gchar *detail, gint x, gint y, gint width, gint height)
{
  x -= (1 + PART_SIZE - width) / 2;
  y -= (1 + PART_SIZE - height) / 2;

  if (DETAIL("option"))	/* Menu item */
    {
      if (shadow == GTK_SHADOW_IN)
	{
          draw_part (window, &style->fg[state], area, x, y, RADIO_TEXT);
	}
    }
  else
    {
      if (xp_theme_draw (window, shadow == GTK_SHADOW_IN
			 ? XP_THEME_ELEMENT_PRESSED_RADIO_BUTTON
			 : XP_THEME_ELEMENT_RADIO_BUTTON,
			 style, x, y, width, height, state, area))
	{
	}
      else
	{
	  if (DETAIL("cellradio"))
	    state = GTK_STATE_NORMAL;

          draw_part (window, &style->black, area, x, y, RADIO_BLACK);
          draw_part (window, &style->dark[state], area, x, y, RADIO_DARK);
          draw_part (window, &style->mid[state], area, x, y, RADIO_MID);
          draw_part (window, &style->light[state], area, x, y, RADIO_LIGHT);
          draw_part (window, &style->base[state], area, x, y, RADIO_BASE);

	  if (shadow == GTK_SHADOW_IN)
            draw_part (window, &style->text[state], area, x, y, RADIO_TEXT);
	}
    }
}

static void
draw_varrow (GdkWindow *window,
             GdkColor *gc,
	     GtkShadowType shadow_type,
	     GdkRectangle *area,
	     GtkArrowType arrow_type, gint x, gint y, gint width, gint height)
{
  gint steps, extra;
  gint y_start, y_increment;
  gint i;
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);

  if (area)
    {
       gdk_cairo_rectangle (cr, area);
       cairo_clip (cr);
    }

  width = width + width % 2 - 1;	/* Force odd */
  steps = 1 + width / 2;
  extra = height - steps;

  if (arrow_type == GTK_ARROW_DOWN)
    {
      y_start = y;
      y_increment = 1;
    }
  else
    {
      y_start = y + height - 1;
      y_increment = -1;
    }

  for (i = extra; i < height; i++)
    {
      _cairo_draw_line (cr, gc,
		     x + (i - extra), y_start + i * y_increment,
		     x + width - (i - extra) - 1, y_start + i * y_increment);
    }

  cairo_destroy(cr);
}

static void
draw_harrow (GdkWindow *window,
             GdkColor *gc,
	     GtkShadowType shadow_type,
	     GdkRectangle *area,
	     GtkArrowType arrow_type, gint x, gint y, gint width, gint height)
{
  gint steps, extra;
  gint x_start, x_increment;
  gint i;
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);

  if (area)
    {
       gdk_cairo_rectangle (cr, area);
       cairo_clip (cr);
    }

  height = height + height % 2 - 1;	/* Force odd */
  steps = 1 + height / 2;
  extra = width - steps;

  if (arrow_type == GTK_ARROW_RIGHT)
    {
      x_start = x;
      x_increment = 1;
    }
  else
    {
      x_start = x + width - 1;
      x_increment = -1;
    }

  for (i = extra; i < width; i++)
    {
      _cairo_draw_line (cr, gc,
		     x_start + i * x_increment, y + (i - extra),
		     x_start + i * x_increment, y + height - (i - extra) - 1);
    }

  cairo_destroy(cr);
}

/* This function makes up for some brokeness in gtkrange.c
 * where we never get the full arrow of the stepper button
 * and the type of button in a single drawing function.
 *
 * It doesn't work correctly when the scrollbar is squished
 * to the point we don't have room for full-sized steppers.
 */
static void
reverse_engineer_stepper_box (GtkWidget *range,<commit_msg>win32: Draw arrows as polygons, not as rows of pixels<commit_after>/* MS-Windows Engine (aka GTK-Wimp)
 *
 * Copyright (C) 2003, 2004 <NAME> <<EMAIL>>
 * Copyright (C) 2006 <NAME> (PCMan) <<EMAIL>>
 * Includes code adapted from redmond95 by <NAME>, and
 * gtk-nativewin by <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Useful resources:
 *
 *  http://lxr.mozilla.org/seamonkey/source/widget/src/windows/nsNativeThemeWin.cpp
 *  http://lxr.mozilla.org/seamonkey/source/widget/src/windows/nsLookAndFeel.cpp
 *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/functions/drawthemebackground.asp
 *  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/pantdraw_4b3g.asp
 */

/* Include first, else we get redefinition warnings about STRICT */
#include "pango/pangowin32.h"

#include "msw_style.h"
#include "xp_theme.h"

#include <windows.h>
#include <math.h>
#include <string.h>
#include <stdio.h>

#include "gdk/gdk.h"
#include "gtk/gtk.h"

#ifdef BUILDING_STANDALONE
#include "gdk/gdkwin32.h"
#else
#include "gdk/win32/gdkwin32.h"
#endif


#define DETAIL(xx)   ((detail) && (!strcmp(xx, detail)))


/* Default values, not normally used
 */
static const GtkRequisition default_option_indicator_size = { 9, 8 };
static const GtkBorder default_option_indicator_spacing = { 7, 5, 2, 2 };

static GtkStyleClass *parent_class;
static HBRUSH g_dither_brush = NULL;

static HPEN g_light_pen = NULL;
static HPEN g_dark_pen = NULL;

typedef enum
{
  CHECK_AA,
  CHECK_BASE,
  CHECK_BLACK,
  CHECK_DARK,
  CHECK_LIGHT,
  CHECK_MID,
  CHECK_TEXT,
  CHECK_INCONSISTENT,
  RADIO_BASE,
  RADIO_BLACK,
  RADIO_DARK,
  RADIO_LIGHT,
  RADIO_MID,
  RADIO_TEXT
} Part;

#define PART_SIZE 13

static const unsigned char check_aa_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_base_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_black_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_dark_bits[] = {
  0xff, 0x1f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00
};
static const unsigned char check_light_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0xfe, 0x1f, 0x00, 0x00
};
static const unsigned char check_mid_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_text_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
  0x00, 0x03, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00,
  0xd8, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
  0x70, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char check_inconsistent_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xf0, 0x03, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_base_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00,
  0xfc, 0x07, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_black_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0x0c, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_dark_bits[] = {
  0xf0, 0x01, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_light_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00
};
static const unsigned char radio_mid_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
  0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
  0x0c, 0x06, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};
static const unsigned char radio_text_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xe0, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0xf0, 0x01, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
  0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};

static struct
{
  const unsigned char *bits;
  cairo_surface_t *bmap;
} parts[] = {
  { check_aa_bits, NULL           },
  { check_base_bits, NULL         },
  { check_black_bits, NULL        },
  { check_dark_bits, NULL         },
  { check_light_bits, NULL        },
  { check_mid_bits, NULL          },
  { check_text_bits, NULL         },
  { check_inconsistent_bits, NULL },
  { radio_base_bits, NULL         },
  { radio_black_bits, NULL        },
  { radio_dark_bits, NULL         },
  { radio_light_bits, NULL        },
  { radio_mid_bits, NULL          },
  { radio_text_bits, NULL         }
};

static void
_cairo_draw_line (cairo_t  *cr,
                  GdkColor *color,
                  gint      x1,
                  gint      y1,
                  gint      x2,
                  gint      y2)
{
  cairo_save (cr);

  gdk_cairo_set_source_color (cr, color);
  cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
  cairo_set_line_width (cr, 1.0);

  cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
  cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
  cairo_stroke (cr);

  cairo_restore (cr);
}

static void
_cairo_draw_rectangle (cairo_t *cr,
                       GdkColor *color,
                       gboolean filled,
                       gint x,
                       gint y,
                       gint width,
                       gint height)
{
  gdk_cairo_set_source_color (cr, color);

  if (filled)
    {
      cairo_rectangle (cr, x, y, width, height);
      cairo_fill (cr);
    }
  else
    {
      cairo_rectangle (cr, x + 0.5, y + 0.5, width, height);
      cairo_stroke (cr);
    }
}

static gboolean
get_system_font (XpThemeClass klazz, XpThemeFont type, LOGFONTW *out_lf)
{
  if (xp_theme_get_system_font (klazz, type, out_lf))
    {
      return TRUE;
    }
  else
    {
      /* Use wide char versions here, as the theming functions only support
       * wide chars versions of the structures. */
      NONCLIENTMETRICSW ncm;

      ncm.cbSize = sizeof (NONCLIENTMETRICSW);

      if (SystemParametersInfoW (SPI_GETNONCLIENTMETRICS,
				sizeof (NONCLIENTMETRICSW), &ncm, 0))
	{
	  if (type == XP_THEME_FONT_CAPTION)
	    *out_lf = ncm.lfCaptionFont;
	  else if (type == XP_THEME_FONT_MENU)
	    *out_lf = ncm.lfMenuFont;
	  else if (type == XP_THEME_FONT_STATUS)
	    *out_lf = ncm.lfStatusFont;
	  else
	    *out_lf = ncm.lfMessageFont;

	  return TRUE;
	}
    }

  return FALSE;
}

static char *
sys_font_to_pango_font (XpThemeClass klazz, XpThemeFont type, char *buf,
			size_t bufsiz)
{
  LOGFONTW lf;

  if (get_system_font (klazz, type, &lf))
    {
      PangoFontDescription *desc = NULL;
      int pt_size;
      const char *font;

      desc = pango_win32_font_description_from_logfontw (&lf);
      if (!desc)
	return NULL;

      font = pango_font_description_to_string (desc);
      pt_size = pango_font_description_get_size (desc);

      if (!(font && *font))
	{
	  pango_font_description_free (desc);
	  return NULL;
	}

      if (pt_size == 0)
	{
	  HDC hDC;
	  HWND hwnd;

	  hwnd = GetDesktopWindow ();
	  hDC = GetDC (hwnd);

	  if (hDC)
	    pt_size = -MulDiv (lf.lfHeight, 72, GetDeviceCaps (hDC, LOGPIXELSY));
	  else
	    pt_size = 10;

	  if (hDC)
	    ReleaseDC (hwnd, hDC);

	  g_snprintf (buf, bufsiz, "%s %d", font, pt_size);
	}
      else
	{
	  g_snprintf (buf, bufsiz, "%s", font);
	}

      if (desc)
	pango_font_description_free (desc);

      return buf;
    }

  return NULL;
}

/* missing from ms's header files */
#ifndef SPI_GETMENUSHOWDELAY
#define SPI_GETMENUSHOWDELAY 106
#endif

/* I don't know the proper XP theme class for things like
   HIGHLIGHTTEXT, so we'll just define it to be "BUTTON"
   for now */
#define XP_THEME_CLASS_TEXT XP_THEME_CLASS_BUTTON

#define WIN95_VERSION   0x400
#define WIN2K_VERSION   0x500
#define WINXP_VERSION   0x501
#define WIN2K3_VERSION  0x502
#define VISTA_VERSION   0x600

static gint32
get_windows_version ()
{
  static gint32 version = 0;
  static gboolean have_version = FALSE;

  if (!have_version)
    {
      OSVERSIONINFOEX osvi;
      have_version = TRUE;

      ZeroMemory (&osvi, sizeof (OSVERSIONINFOEX));
      osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);

      GetVersionEx((OSVERSIONINFO*) &osvi);

      version = (osvi.dwMajorVersion & 0xff) << 8 | (osvi.dwMinorVersion & 0xff);
    }

  return version;
}

static void
setup_menu_settings (GtkSettings *settings)
{
  int menu_delay;
  GObjectClass *klazz = G_OBJECT_GET_CLASS (G_OBJECT (settings));

  if (get_windows_version () > WIN95_VERSION)
    {
      if (SystemParametersInfo (SPI_GETMENUSHOWDELAY, 0, &menu_delay, 0))
	{
	  if (klazz)
	    {
	      if (g_object_class_find_property
		  (klazz, "gtk-menu-bar-popup-delay"))
		{
		  g_object_set (settings,
				"gtk-menu-bar-popup-delay", 0, NULL);
		}
	      if (g_object_class_find_property
		  (klazz, "gtk-menu-popup-delay"))
		{
		  g_object_set (settings,
				"gtk-menu-popup-delay", menu_delay, NULL);
		}
	      if (g_object_class_find_property
		  (klazz, "gtk-menu-popdown-delay"))
		{
		  g_object_set (settings,
				"gtk-menu-popdown-delay", menu_delay, NULL);
		}
	    }
	}
    }
}

void
msw_style_setup_system_settings (void)
{
  GtkSettings *settings;
  int cursor_blink_time;

  settings = gtk_settings_get_default ();
  if (!settings)
    return;

  cursor_blink_time = GetCaretBlinkTime ();
  g_object_set (settings, "gtk-cursor-blink", cursor_blink_time > 0, NULL);

  if (cursor_blink_time > 0)
    {
      g_object_set (settings, "gtk-cursor-blink-time",
		    2 * cursor_blink_time, NULL);
    }

  g_object_set (settings, "gtk-double-click-distance",
		GetSystemMetrics (SM_CXDOUBLECLK), NULL);
  g_object_set (settings, "gtk-double-click-time", GetDoubleClickTime (),
		NULL);
  g_object_set (settings, "gtk-dnd-drag-threshold",
		GetSystemMetrics (SM_CXDRAG), NULL);

  setup_menu_settings (settings);

  /*
     http://developer.gnome.org/doc/API/2.0/gtk/GtkSettings.html
     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/systemparametersinfo.asp
     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getsystemmetrics.asp */
}

static void
setup_system_font (GtkStyle *style)
{
  char buf[256], *font;		/* It's okay, lfFaceName is smaller than 32
				   chars */

  if ((font = sys_font_to_pango_font (XP_THEME_CLASS_TEXT,
				      XP_THEME_FONT_MESSAGE,
				      buf, sizeof (buf))) != NULL)
    {
      if (style->font_desc)
	{
	  pango_font_description_free (style->font_desc);
	}

      style->font_desc = pango_font_description_from_string (font);
    }
}

static void
sys_color_to_gtk_color (XpThemeClass klazz, int id, GdkColor * pcolor)
{
  DWORD color;

  if (!xp_theme_get_system_color (klazz, id, &color))
    color = GetSysColor (id);

  pcolor->pixel = color;
  pcolor->red = (GetRValue (color) << 8) | GetRValue (color);
  pcolor->green = (GetGValue (color) << 8) | GetGValue (color);
  pcolor->blue = (GetBValue (color) << 8) | GetBValue (color);
}

static int
get_system_metric (XpThemeClass klazz, int id)
{
  int rval;

  if (!xp_theme_get_system_metric (klazz, id, &rval))
    rval = GetSystemMetrics (id);

  return rval;
}

static void
setup_msw_rc_style (void)
{
  char buf[1024], font_buf[256], *font_ptr;
  char menu_bar_prelight_str[128];

  GdkColor menu_color;
  GdkColor menu_text_color;
  GdkColor tooltip_back;
  GdkColor tooltip_fore;
  GdkColor btn_fore;
  GdkColor btn_face;
  GdkColor progress_back;

  GdkColor fg_prelight;
  GdkColor bg_prelight;
  GdkColor base_prelight;
  GdkColor text_prelight;

  /* Prelight */
  sys_color_to_gtk_color (get_windows_version () >= VISTA_VERSION ? XP_THEME_CLASS_MENU : XP_THEME_CLASS_TEXT,
			  get_windows_version () >= VISTA_VERSION ? COLOR_MENUTEXT : COLOR_HIGHLIGHTTEXT,
			  &fg_prelight);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT, &bg_prelight);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT,
			  &base_prelight);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT,
			  &text_prelight);

  sys_color_to_gtk_color (XP_THEME_CLASS_MENU, COLOR_MENUTEXT,
			  &menu_text_color);
  sys_color_to_gtk_color (XP_THEME_CLASS_MENU, COLOR_MENU, &menu_color);

  /* tooltips */
  sys_color_to_gtk_color (XP_THEME_CLASS_TOOLTIP, COLOR_INFOTEXT,
			  &tooltip_fore);
  sys_color_to_gtk_color (XP_THEME_CLASS_TOOLTIP, COLOR_INFOBK,
			  &tooltip_back);

  /* text on push buttons. TODO: button shadows, backgrounds, and
     highlights */
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT, &btn_fore);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE, &btn_face);

  /* progress bar background color */
  sys_color_to_gtk_color (XP_THEME_CLASS_PROGRESS, COLOR_HIGHLIGHT,
			  &progress_back);

  /* Enable coloring for menus. */
  font_ptr =
    sys_font_to_pango_font (XP_THEME_CLASS_MENU, XP_THEME_FONT_MENU,
			    font_buf, sizeof (font_buf));
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-menu\" = \"msw-default\"\n" "{\n"
	      "GtkMenuItem::toggle-spacing = 8\n"
	      "fg[PRELIGHT] = { %d, %d, %d }\n"
	      "bg[PRELIGHT] = { %d, %d, %d }\n"
	      "text[PRELIGHT] = { %d, %d, %d }\n"
	      "base[PRELIGHT] = { %d, %d, %d }\n"
	      "fg[NORMAL] = { %d, %d, %d }\n"
	      "bg[NORMAL] = { %d, %d, %d }\n" "%s = \"%s\"\n"
	      "}widget_class \"*MenuItem*\" style \"msw-menu\"\n"
	      "widget_class \"*GtkMenu\" style \"msw-menu\"\n"
	      "widget_class \"*GtkMenuShell*\" style \"msw-menu\"\n",
	      fg_prelight.red, fg_prelight.green, fg_prelight.blue,
	      bg_prelight.red, bg_prelight.green, bg_prelight.blue,
	      text_prelight.red, text_prelight.green, text_prelight.blue,
	      base_prelight.red, base_prelight.green, base_prelight.blue,
	      menu_text_color.red, menu_text_color.green,
	      menu_text_color.blue, menu_color.red, menu_color.green,
	      menu_color.blue, (font_ptr ? "font_name" : "#"),
	      (font_ptr ? font_ptr : " font name should go here"));
  gtk_rc_parse_string (buf);

  if (xp_theme_is_active ())
    {
      *menu_bar_prelight_str = '\0';
    }
  else
    {
      g_snprintf (menu_bar_prelight_str, sizeof (menu_bar_prelight_str),
		  "fg[PRELIGHT] = { %d, %d, %d }\n",
		  menu_text_color.red, menu_text_color.green,
		  menu_text_color.blue);
    }

  /* Enable coloring for menu bars. */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-menu-bar\" = \"msw-menu\"\n"
	      "{\n"
	      "bg[NORMAL] = { %d, %d, %d }\n"
	      "%s" "GtkMenuBar::shadow-type = %d\n"
	      /*
	         FIXME: This should be enabled once gtk+ support
	         GtkMenuBar::prelight-item style property.
	       */
	      /* "GtkMenuBar::prelight-item = 1\n" */
	      "}widget_class \"*MenuBar*\" style \"msw-menu-bar\"\n",
	      btn_face.red, btn_face.green, btn_face.blue,
	      menu_bar_prelight_str, xp_theme_is_active ()? 0 : 2);
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-toolbar\" = \"msw-default\"\n"
	      "{\n"
	      "GtkHandleBox::shadow-type = %s\n"
	      "GtkToolbar::shadow-type = %s\n"
	      "}widget_class \"*HandleBox*\" style \"msw-toolbar\"\n",
	      "etched-in", "etched-in");
  gtk_rc_parse_string (buf);

  /* enable tooltip fonts */
  font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_STATUS, XP_THEME_FONT_STATUS,
				     font_buf, sizeof (font_buf));
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-tooltips-caption\" = \"msw-default\"\n"
	      "{fg[NORMAL] = { %d, %d, %d }\n" "%s = \"%s\"\n"
	      "}widget \"gtk-tooltips.GtkLabel\" style \"msw-tooltips-caption\"\n"
	      "widget \"gtk-tooltip.GtkLabel\" style \"msw-tooltips-caption\"\n",
	      tooltip_fore.red, tooltip_fore.green, tooltip_fore.blue,
	      (font_ptr ? "font_name" : "#"),
	      (font_ptr ? font_ptr : " font name should go here"));
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-tooltips\" = \"msw-default\"\n"
	      "{bg[NORMAL] = { %d, %d, %d }\n"
	      "}widget \"gtk-tooltips*\" style \"msw-tooltips\"\n"
	      "widget \"gtk-tooltip*\" style \"msw-tooltips\"\n",
	      tooltip_back.red, tooltip_back.green, tooltip_back.blue);
  gtk_rc_parse_string (buf);

  /* enable font theming for status bars */
  font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_STATUS, XP_THEME_FONT_STATUS,
				     font_buf, sizeof (font_buf));
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-status\" = \"msw-default\"\n" "{%s = \"%s\"\n"
	      "bg[NORMAL] = { %d, %d, %d }\n"
	      "}widget_class \"*Status*\" style \"msw-status\"\n",
	      (font_ptr ? "font_name" : "#"),
	      (font_ptr ? font_ptr : " font name should go here"),
	      btn_face.red, btn_face.green, btn_face.blue);
  gtk_rc_parse_string (buf);

  /* enable coloring for text on buttons
   * TODO: use GetThemeMetric for the border and outside border */
  g_snprintf (buf, sizeof (buf),
              "style \"msw-button\" = \"msw-default\"\n"
              "{\n"
              "bg[NORMAL] = { %d, %d, %d }\n"
              "bg[PRELIGHT] = { %d, %d, %d }\n"
              "bg[INSENSITIVE] = { %d, %d, %d }\n"
              "fg[PRELIGHT] = { %d, %d, %d }\n"
              "GtkButton::default-border = { 0, 0, 0, 0 }\n"
              "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
              "GtkButton::child-displacement-x = %d\n"
              "GtkButton::child-displacement-y = %d\n"
              "GtkWidget::focus-padding = %d\n"
              "}widget_class \"*Button*\" style \"msw-button\"\n",
              btn_face.red, btn_face.green, btn_face.blue,
              btn_face.red, btn_face.green, btn_face.blue,
              btn_face.red, btn_face.green, btn_face.blue,
              btn_fore.red, btn_fore.green, btn_fore.blue,
              xp_theme_is_active ()? 0 : 1,
              xp_theme_is_active ()? 0 : 1,
              xp_theme_is_active ()? 1 : 2);
  gtk_rc_parse_string (buf);

  /* enable coloring for progress bars */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-progress\" = \"msw-default\"\n"
	      "{bg[PRELIGHT] = { %d, %d, %d }\n"
	      "bg[NORMAL] = { %d, %d, %d }\n"
	      "}widget_class \"*Progress*\" style \"msw-progress\"\n",
	      progress_back.red,
	      progress_back.green,
	      progress_back.blue,
	      btn_face.red, btn_face.green, btn_face.blue);
  gtk_rc_parse_string (buf);

  /* scrollbar thumb width and height */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-vscrollbar\" = \"msw-default\"\n"
	      "{GtkRange::slider-width = %d\n"
	      "GtkRange::stepper-size = %d\n"
	      "GtkRange::stepper-spacing = 0\n"
	      "GtkRange::trough_border = 0\n"
	      "GtkScale::slider-length = %d\n"
	      "GtkScrollbar::min-slider-length = 8\n"
	      "}widget_class \"*VScrollbar*\" style \"msw-vscrollbar\"\n"
	      "widget_class \"*VScale*\" style \"msw-vscrollbar\"\n",
	      GetSystemMetrics (SM_CYVTHUMB),
	      get_system_metric (XP_THEME_CLASS_SCROLLBAR, SM_CXVSCROLL), 11);
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-hscrollbar\" = \"msw-default\"\n"
	      "{GtkRange::slider-width = %d\n"
	      "GtkRange::stepper-size = %d\n"
	      "GtkRange::stepper-spacing = 0\n"
	      "GtkRange::trough_border = 0\n"
	      "GtkScale::slider-length = %d\n"
	      "GtkScrollbar::min-slider-length = 8\n"
	      "}widget_class \"*HScrollbar*\" style \"msw-hscrollbar\"\n"
	      "widget_class \"*HScale*\" style \"msw-hscrollbar\"\n",
	      GetSystemMetrics (SM_CXHTHUMB),
	      get_system_metric (XP_THEME_CLASS_SCROLLBAR, SM_CYHSCROLL), 11);
  gtk_rc_parse_string (buf);

  gtk_rc_parse_string ("style \"msw-scrolled-window\" = \"msw-default\"\n"
		       "{GtkScrolledWindow::scrollbars-within-bevel = 1}\n"
		       "class \"GtkScrolledWindow\" style \"msw-scrolled-window\"\n");

  /* radio/check button sizes */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-checkbutton\" = \"msw-button\"\n"
	      "{GtkCheckButton::indicator-size = 13\n"
	      "}widget_class \"*CheckButton*\" style \"msw-checkbutton\"\n"
	      "widget_class \"*RadioButton*\" style \"msw-checkbutton\"\n");
  gtk_rc_parse_string (buf);

  /* size of combo box toggle button */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-combobox-button\" = \"msw-default\"\n"
	      "{\n"
	      "xthickness = 0\n"
	      "ythickness = 0\n"
	      "GtkButton::default-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::child-displacement-x = 0\n"
	      "GtkButton::child-displacement-y = 0\n"
	      "GtkWidget::focus-padding = 0\n"
	      "GtkWidget::focus-line-width = 0\n"
	      "}\n"
	      "widget_class \"*ComboBox*ToggleButton*\" style \"msw-combobox-button\"\n");
  gtk_rc_parse_string (buf);

  g_snprintf (buf, sizeof (buf),
	      "style \"msw-combobox\" = \"msw-default\"\n"
	      "{\n"
	      "GtkComboBox::shadow-type = in\n"
	      "xthickness = %d\n"
	      "ythickness = %d\n"
	      "}\n"
	      "class \"GtkComboBox\" style \"msw-combobox\"\n",
        xp_theme_is_active()? 1 : GetSystemMetrics (SM_CXEDGE),
        xp_theme_is_active()? 1 : GetSystemMetrics (SM_CYEDGE));
  gtk_rc_parse_string (buf);

  /* size of tree view header */
  g_snprintf (buf, sizeof (buf),
	      "style \"msw-header-button\" = \"msw-default\"\n"
	      "{\n"
	      "xthickness = 0\n"
	      "ythickness = 0\n"
	      "GtkWidget::draw-border = {0, 0, 0, 0}\n"
        "GtkButton::default-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
	      "GtkButton::child-displacement-x = 0\n"
	      "GtkButton::child-displacement-y = 0\n"
	      "GtkWidget::focus-padding = 0\n"
	      "GtkWidget::focus-line-width = 0\n"
	      "}\n"
	      "widget_class \"*TreeView*Button*\" style \"msw-header-button\"\n");
  gtk_rc_parse_string (buf);

  /* FIXME: This should be enabled once gtk+ support GtkNotebok::prelight-tab */
  /* enable prelight tab of GtkNotebook */
  /*
     g_snprintf (buf, sizeof (buf),
     "style \"msw-notebook\" = \"msw-default\"\n"
     "{GtkNotebook::prelight-tab=1\n"
     "}widget_class \"*Notebook*\" style \"msw-notebook\"\n");
     gtk_rc_parse_string (buf);
   */

  /* FIXME: This should be enabled once gtk+ support GtkTreeView::full-row-focus */
  /*
     g_snprintf (buf, sizeof (buf),
     "style \"msw-treeview\" = \"msw-default\"\n"
     "{GtkTreeView::full-row-focus=0\n"
     "}widget_class \"*TreeView*\" style \"msw-treeview\"\n");
     gtk_rc_parse_string (buf);
   */
}

static void
setup_system_styles (GtkStyle *style)
{
  int i;

  /* Default background */
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT,
			  &style->bg[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->bg[GTK_STATE_PRELIGHT]);

  /* Default base */
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOW,
			  &style->base[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT,
			  &style->base[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->base[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNFACE,
			  &style->base[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOW,
			  &style->base[GTK_STATE_PRELIGHT]);

  /* Default text */
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT,
			  &style->text[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT,
			  &style->text[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_GRAYTEXT,
			  &style->text[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT,
			  &style->text[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT,
			  &style->text[GTK_STATE_PRELIGHT]);

  /* Default foreground */
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT,
			  &style->fg[GTK_STATE_NORMAL]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT,
			  &style->fg[GTK_STATE_SELECTED]);
  sys_color_to_gtk_color (XP_THEME_CLASS_TEXT, COLOR_GRAYTEXT,
			  &style->fg[GTK_STATE_INSENSITIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT,
                          &style->fg[GTK_STATE_ACTIVE]);
  sys_color_to_gtk_color (XP_THEME_CLASS_WINDOW, COLOR_WINDOWTEXT,
			  &style->fg[GTK_STATE_PRELIGHT]);

  for (i = 0; i < 5; i++)
    {
      sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_3DSHADOW,
			      &style->dark[i]);
      sys_color_to_gtk_color (XP_THEME_CLASS_BUTTON, COLOR_3DHILIGHT,
			      &style->light[i]);

      style->mid[i].red = (style->light[i].red + style->dark[i].red) / 2;
      style->mid[i].green =
	(style->light[i].green + style->dark[i].green) / 2;
      style->mid[i].blue = (style->light[i].blue + style->dark[i].blue) / 2;

      style->text_aa[i].red = (style->text[i].red + style->base[i].red) / 2;
      style->text_aa[i].green =
	(style->text[i].green + style->base[i].green) / 2;
      style->text_aa[i].blue =
	(style->text[i].blue + style->base[i].blue) / 2;
    }
}

static gboolean
sanitize_size (GdkWindow *window, gint *width, gint *height)
{
  gboolean set_bg = FALSE;

  if ((*width == -1) && (*height == -1))
    {
      set_bg = GDK_IS_WINDOW (window);
      gdk_drawable_get_size (window, width, height);
    }
  else if (*width == -1)
    {
      gdk_drawable_get_size (window, width, NULL);
    }
  else if (*height == -1)
    {
      gdk_drawable_get_size (window, NULL, height);
    }

  return set_bg;
}

static XpThemeElement
map_gtk_progress_bar_to_xp (GtkProgressBar *progress_bar, gboolean trough)
{
  XpThemeElement ret;

  switch (gtk_progress_bar_get_orientation (progress_bar))
    {
    case GTK_PROGRESS_LEFT_TO_RIGHT:
    case GTK_PROGRESS_RIGHT_TO_LEFT:
      ret = trough
	? XP_THEME_ELEMENT_PROGRESS_TROUGH_H
	: XP_THEME_ELEMENT_PROGRESS_BAR_H;
      break;

    default:
      ret = trough
	? XP_THEME_ELEMENT_PROGRESS_TROUGH_V
	: XP_THEME_ELEMENT_PROGRESS_BAR_V;
      break;
    }

  return ret;
}

static gboolean
is_combo_box_child (GtkWidget *w)
{
  GtkWidget *tmp;

  if (w == NULL)
    return FALSE;

  for (tmp = w->parent; tmp; tmp = tmp->parent)
    {
      if (GTK_IS_COMBO_BOX (tmp))
	return TRUE;
    }

  return FALSE;
}

static void
draw_part (GdkDrawable *drawable,
           GdkColor *gc, GdkRectangle *area, gint x, gint y, Part part)
{
  cairo_t *cr = gdk_cairo_create (drawable);

  if (area)
    {
      gdk_cairo_rectangle (cr, area);
      cairo_clip (cr);
    }

  if (!parts[part].bmap)
    {
      parts[part].bmap = cairo_image_surface_create_for_data ((unsigned char *)parts[part].bits,
        					              CAIRO_FORMAT_A1,
        					              PART_SIZE, PART_SIZE, 4);
    }

  gdk_cairo_set_source_color (cr, gc);
  cairo_mask_surface (cr, parts[part].bmap, x, y);

  cairo_destroy(cr);
}

static void
draw_check (GtkStyle *style,
	    GdkWindow *window,
	    GtkStateType state,
	    GtkShadowType shadow,
	    GdkRectangle *area,
	    GtkWidget *widget,
	    const gchar *detail, gint x, gint y, gint width, gint height)
{
  x -= (1 + PART_SIZE - width) / 2;
  y -= (1 + PART_SIZE - height) / 2;

  if (DETAIL("check"))	/* Menu item */
    {
      if (shadow == GTK_SHADOW_IN)
	{
          draw_part (window, &style->black, area, x, y, CHECK_TEXT);
          draw_part (window, &style->dark[state], area, x, y, CHECK_AA);
	}
    }
  else
    {
      XpThemeElement theme_elt = XP_THEME_ELEMENT_CHECKBOX;
      switch (shadow)
	{
	case GTK_SHADOW_ETCHED_IN:
	  theme_elt = XP_THEME_ELEMENT_INCONSISTENT_CHECKBOX;
	  break;

	case GTK_SHADOW_IN:
	  theme_elt = XP_THEME_ELEMENT_PRESSED_CHECKBOX;
	  break;

	default:
	  break;
	}

      if (!xp_theme_draw (window, theme_elt,
			  style, x, y, width, height, state, area))
	{
	  if (DETAIL("cellcheck"))
	    state = GTK_STATE_NORMAL;

          draw_part (window, &style->black, area, x, y, CHECK_BLACK);
          draw_part (window, &style->dark[state], area, x, y, CHECK_DARK);
          draw_part (window, &style->mid[state], area, x, y, CHECK_MID);
          draw_part (window, &style->light[state], area, x, y, CHECK_LIGHT);
          draw_part (window, &style->base[state], area, x, y, CHECK_BASE);

	  if (shadow == GTK_SHADOW_IN)
	    {
              draw_part (window, &style->text[state], area, x,
			 y, CHECK_TEXT);
              draw_part (window, &style->text_aa[state], area,
			 x, y, CHECK_AA);
	    }
	  else if (shadow == GTK_SHADOW_ETCHED_IN)
	    {
              draw_part (window, &style->text[state], area, x, y,
			 CHECK_INCONSISTENT);
              draw_part (window, &style->text_aa[state], area, x, y,
			 CHECK_AA);
	    }
	}
    }
}

static void
draw_expander (GtkStyle        *style,
               GdkWindow       *window,
               GtkStateType     state,
               GdkRectangle    *area,
               GtkWidget       *widget,
               const gchar     *detail,
               gint             x,
               gint             y,
               GtkExpanderStyle expander_style)
{
  cairo_t *cr = gdk_cairo_create (window);

  gint expander_size;
  gint expander_semi_size;
  XpThemeElement xp_expander;
  GtkOrientation orientation;

  gtk_widget_style_get (widget, "expander_size", &expander_size, NULL);

  if (DETAIL("tool-palette-header"))
    {
      /* Expanders are usually drawn as little triangles and unfortunately
       * do not support rotated drawing modes. So a hack is applied (see
       * gtk_tool_item_group_header_expose_event_cb for details) when
       * drawing a GtkToolItemGroup's header for horizontal GtkToolShells,
       * forcing the triangle to point in the right direction. Except we
       * don't draw expanders as triangles on Windows. Usually, expanders
       * are represented as "+" and "-". It sucks for "+" to become "-" and
       * the inverse when we don't want to, so reverse the hack here. */

      orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (widget));

      if (orientation == GTK_ORIENTATION_HORIZONTAL)
          expander_style = GTK_EXPANDER_EXPANDED - expander_style;
    }

  switch (expander_style)
    {
    case GTK_EXPANDER_COLLAPSED:
    case GTK_EXPANDER_SEMI_COLLAPSED:
      xp_expander = XP_THEME_ELEMENT_TREEVIEW_EXPANDER_CLOSED;
      break;

    case GTK_EXPANDER_EXPANDED:
    case GTK_EXPANDER_SEMI_EXPANDED:
      xp_expander = XP_THEME_ELEMENT_TREEVIEW_EXPANDER_OPENED;
      break;

    default:
      g_assert_not_reached ();
    }

  if ((expander_size % 2) == 0)
    expander_size--;

  if (expander_size > 2)
    expander_size -= 2;

  if (area)
    {
      gdk_cairo_rectangle (cr, area);
      cairo_clip (cr);
      gdk_cairo_set_source_color (cr, &style->fg[state]);
    }

  expander_semi_size = expander_size / 2;
  x -= expander_semi_size;
  y -= expander_semi_size;

  if (!xp_theme_draw (window, xp_expander, style,
		      x, y, expander_size, expander_size, state, area))
    {
      HDC dc;
      RECT rect;
      HPEN pen;
      HGDIOBJ old_pen;
      XpDCInfo dc_info;

      dc = get_window_dc (style, window, state, &dc_info, x, y, expander_size,
			  expander_size, &rect);
      FrameRect (dc, &rect, GetSysColorBrush (COLOR_GRAYTEXT));
      InflateRect (&rect, -1, -1);
      FillRect (dc, &rect,
		GetSysColorBrush (state ==
				  GTK_STATE_INSENSITIVE ? COLOR_BTNFACE :
				  COLOR_WINDOW));

      InflateRect (&rect, -1, -1);

      pen = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_WINDOWTEXT));
      old_pen = SelectObject (dc, pen);

      MoveToEx (dc, rect.left, rect.top - 2 + expander_semi_size, NULL);
      LineTo (dc, rect.right, rect.top - 2 + expander_semi_size);

      if (expander_style == GTK_EXPANDER_COLLAPSED ||
	  expander_style == GTK_EXPANDER_SEMI_COLLAPSED)
	{
	  MoveToEx (dc, rect.left - 2 + expander_semi_size, rect.top, NULL);
	  LineTo (dc, rect.left - 2 + expander_semi_size, rect.bottom);
	}

      SelectObject (dc, old_pen);
      DeleteObject (pen);
      release_window_dc (&dc_info);
    }

  cairo_destroy(cr);
}

static void
draw_option (GtkStyle *style,
	     GdkWindow *window,
	     GtkStateType state,
	     GtkShadowType shadow,
	     GdkRectangle *area,
	     GtkWidget *widget,
	     const gchar *detail, gint x, gint y, gint width, gint height)
{
  x -= (1 + PART_SIZE - width) / 2;
  y -= (1 + PART_SIZE - height) / 2;

  if (DETAIL("option"))	/* Menu item */
    {
      if (shadow == GTK_SHADOW_IN)
	{
          draw_part (window, &style->fg[state], area, x, y, RADIO_TEXT);
	}
    }
  else
    {
      if (xp_theme_draw (window, shadow == GTK_SHADOW_IN
			 ? XP_THEME_ELEMENT_PRESSED_RADIO_BUTTON
			 : XP_THEME_ELEMENT_RADIO_BUTTON,
			 style, x, y, width, height, state, area))
	{
	}
      else
	{
	  if (DETAIL("cellradio"))
	    state = GTK_STATE_NORMAL;

          draw_part (window, &style->black, area, x, y, RADIO_BLACK);
          draw_part (window, &style->dark[state], area, x, y, RADIO_DARK);
          draw_part (window, &style->mid[state], area, x, y, RADIO_MID);
          draw_part (window, &style->light[state], area, x, y, RADIO_LIGHT);
          draw_part (window, &style->base[state], area, x, y, RADIO_BASE);

	  if (shadow == GTK_SHADOW_IN)
            draw_part (window, &style->text[state], area, x, y, RADIO_TEXT);
	}
    }
}

static void
draw_varrow (GdkWindow *window,
             GdkColor *gc,
	     GtkShadowType shadow_type,
	     GdkRectangle *area,
	     GtkArrowType arrow_type, gint x, gint y, gint width, gint height)
{
  gint steps, extra;
  gint y_start, y_increment;
  gint i;
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);

  if (area)
    {
       gdk_cairo_rectangle (cr, area);
       cairo_clip (cr);
    }

  width = width + width % 2 - 1;	/* Force odd */
  steps = 1 + width / 2;
  extra = height - steps;

  if (arrow_type == GTK_ARROW_DOWN)
    {
      y_start = y;
      y_increment = 1;
    }
  else
    {
      y_start = y + height - 1;
      y_increment = -1;
    }

  gdk_cairo_set_source_color (cr, gc);
  cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
  cairo_set_line_width (cr, 1.0);
  cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);

  cairo_move_to (cr, x + 0.5, y_start + extra * y_increment + 0.5);
  cairo_line_to (cr, x + width - 1 + 0.5, y_start + extra * y_increment + 0.5);
  cairo_line_to (cr, x + (height - 1 - extra) + 0.5, y_start + (height - 1) * y_increment + 0.5);
  cairo_close_path (cr);
  cairo_stroke_preserve (cr);
  cairo_fill (cr);

  cairo_destroy(cr);
}

static void
draw_harrow (GdkWindow *window,
             GdkColor *gc,
	     GtkShadowType shadow_type,
	     GdkRectangle *area,
	     GtkArrowType arrow_type, gint x, gint y, gint width, gint height)
{
  gint steps, extra;
  gint x_start, x_increment;
  gint i;
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);

  if (area)
    {
       gdk_cairo_rectangle (cr, area);
       cairo_clip (cr);
    }

  height = height + height % 2 - 1;	/* Force odd */
  steps = 1 + height / 2;
  extra = width - steps;

  if (arrow_type == GTK_ARROW_RIGHT)
    {
      x_start = x;
      x_increment = 1;
    }
  else
    {
      x_start = x + width - 1;
      x_increment = -1;
    }

  gdk_cairo_set_source_color (cr, gc);
  cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
  cairo_set_line_width (cr, 1.0);
  cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);

  cairo_move_to (cr, x_start + extra * x_increment + 0.5, y + 0.5);
  cairo_line_to (cr, x_start + extra * x_increment + 0.5, y + height - 1 + 0.5);
  cairo_line_to (cr, x_start + (width - 1) * x_increment + 0.5, y + height - (width - 1 - extra) - 1 + 0.5);
  cairo_close_path (cr);
  cairo_stroke_preserve (cr);
  cairo_fill (cr);

  cairo_destroy(cr);
}

/* This function makes up for some brokeness in gtkrange.c
 * where we never get the full arrow of the stepper button
 * and the type of button in a single drawing function.
 *
 * It doesn't work correctly when the scrollbar is squished
 * to the point we don't have room for full-sized steppers.
 */
static void
reverse_engineer_stepper_box (GtkWidget *range,
{'fraction_non_alphanumeric': 0.1338050255290856, 'fraction_numerical': 0.06030384002609361, 'mean_word_length': 3.0490679128358815, 'pattern_counts': {'":': 0, '<': 46, '<?xml version=': 0, '>': 174, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4109297', 'n_tokens_mistral': 37336, 'n_tokens_neox': 32750, 'n_words': 6983}