/* MS-Windows Engine (aka GTK-Wimp)
 *
 * Copyright (C) 2003, 2004 Raymond Penners <raymond@dotsphinx.com>
 * Copyright (C) 2006 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
 * Includes code adapted from redmond95 by Owen Taylor, 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;
    }

  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,
			      GtkArrowType arrow_type,
			      gint *x, gint *y, gint *width, gint *height)
{
  gint slider_width = 14, stepper_size = 14;
  gint box_width;
  gint box_height;

  if (range)
    {
      gtk_widget_style_get (range,
			    "slider_width", &slider_width,
			    "stepper_size", &stepper_size, NULL);
    }

  if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
    {
      box_width = slider_width;
      box_height = stepper_size;
    }
  else
    {
      box_width = stepper_size;
      box_height = slider_width;
    }

  *x = *x - (box_width - *width) / 2;
  *y = *y - (box_height - *height) / 2;
  *width = box_width;
  *height = box_height;
}

static XpThemeElement
to_xp_arrow (GtkArrowType arrow_type)
{
  XpThemeElement xp_arrow;

  switch (arrow_type)
    {
    case GTK_ARROW_UP:
      xp_arrow = XP_THEME_ELEMENT_ARROW_UP;
      break;

    case GTK_ARROW_DOWN:
      xp_arrow = XP_THEME_ELEMENT_ARROW_DOWN;
      break;

    case GTK_ARROW_LEFT:
      xp_arrow = XP_THEME_ELEMENT_ARROW_LEFT;
      break;

    default:
      xp_arrow = XP_THEME_ELEMENT_ARROW_RIGHT;
      break;
    }

  return xp_arrow;
}

static void
draw_arrow (GtkStyle *style,
	    GdkWindow *window,
	    GtkStateType state,
	    GtkShadowType shadow,
	    GdkRectangle *area,
	    GtkWidget *widget,
	    const gchar *detail,
	    GtkArrowType arrow_type,
	    gboolean fill, gint x, gint y, gint width, gint height)
{
  const gchar *name;
  HDC dc;
  RECT rect;
  XpDCInfo dc_info;

  name = gtk_widget_get_name (widget);

  sanitize_size (window, &width, &height);

  if (GTK_IS_ARROW (widget) && is_combo_box_child (widget) && xp_theme_is_active ())
    return;

  if (DETAIL("spinbutton"))
    {
      if (xp_theme_is_drawable (XP_THEME_ELEMENT_SPIN_BUTTON_UP))
	{
	  return;
	}

      width -= 2;
      --height;
      if (arrow_type == GTK_ARROW_DOWN)
	++y;
      ++x;

      if (state == GTK_STATE_ACTIVE)
	{
	  ++x;
	  ++y;
	}

      draw_varrow (window, &style->fg[state], shadow, area,
		   arrow_type, x, y, width, height);

      return;
    }
  else if (DETAIL("vscrollbar") || DETAIL("hscrollbar"))
    {
      gboolean is_disabled = FALSE;
      UINT btn_type = 0;
      GtkScrollbar *scrollbar = GTK_SCROLLBAR (widget);

      gint box_x = x;
      gint box_y = y;
      gint box_width = width;
      gint box_height = height;

      reverse_engineer_stepper_box (widget, arrow_type,
				    &box_x, &box_y, &box_width, &box_height);

      if (gtk_range_get_adjustment(&scrollbar->range)->page_size >=
          (gtk_range_get_adjustment(&scrollbar->range)->upper -
           gtk_range_get_adjustment(&scrollbar->range)->lower))
	{
	  is_disabled = TRUE;
	}

      if (xp_theme_draw (window, to_xp_arrow (arrow_type), style, box_x, box_y,
			 box_width, box_height, state, area))
	{
	}
      else
	{
	  switch (arrow_type)
	    {
	    case GTK_ARROW_UP:
	      btn_type = DFCS_SCROLLUP;
	      break;

	    case GTK_ARROW_DOWN:
	      btn_type = DFCS_SCROLLDOWN;
	      break;

	    case GTK_ARROW_LEFT:
	      btn_type = DFCS_SCROLLLEFT;
	      break;

	    case GTK_ARROW_RIGHT:
	      btn_type = DFCS_SCROLLRIGHT;
	      break;

	    case GTK_ARROW_NONE:
	      break;
	    }

	  if (state == GTK_STATE_INSENSITIVE)
	    {
	      btn_type |= DFCS_INACTIVE;
	    }

	  if (widget)
	    {
	      sanitize_size (window, &width, &height);

	      dc = get_window_dc (style, window, state, &dc_info,
				  box_x, box_y, box_width, box_height, &rect);
	      DrawFrameControl (dc, &rect, DFC_SCROLL,
				btn_type | (shadow ==
					    GTK_SHADOW_IN ? (DFCS_PUSHED |
							     DFCS_FLAT) : 0));
	      release_window_dc (&dc_info);
	    }
	}
    }
  else
    {
      /* draw the toolbar chevrons - waiting for GTK 2.4 */
      if (name && !strcmp (name, "gtk-toolbar-arrow"))
	{
	  if (xp_theme_draw
	      (window, XP_THEME_ELEMENT_REBAR_CHEVRON, style, x, y,
	       width, height, state, area))
	    {
	      return;
	    }
	}
      /* probably a gtk combo box on a toolbar */
      else if (0		/* widget->parent && GTK_IS_BUTTON
				   (widget->parent) */ )
	{
	  if (xp_theme_draw
	      (window, XP_THEME_ELEMENT_COMBOBUTTON, style, x - 3,
	       widget->allocation.y + 1, width + 5,
	       widget->allocation.height - 4, state, area))
	    {
	      return;
	    }
	}

      if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
	{
	  x += (width - 7) / 2;
	  y += (height - 5) / 2;

          draw_varrow (window, &style->fg[state], shadow, area,
		       arrow_type, x, y, 7, 5);
	}
      else
	{
	  x += (width - 5) / 2;
	  y += (height - 7) / 2;

          draw_harrow (window, &style->fg[state], shadow, area,
		       arrow_type, x, y, 5, 7);
	}
    }
}

static void
option_menu_get_props (GtkWidget *widget,
		       GtkRequisition *indicator_size,
		       GtkBorder *indicator_spacing)
{
  GtkRequisition *tmp_size = NULL;
  GtkBorder *tmp_spacing = NULL;

  if (widget)
    gtk_widget_style_get (widget,
			  "indicator_size", &tmp_size,
			  "indicator_spacing", &tmp_spacing, NULL);

  if (tmp_size)
    {
      *indicator_size = *tmp_size;
      gtk_requisition_free (tmp_size);
    }
  else
    {
      *indicator_size = default_option_indicator_size;
    }

  if (tmp_spacing)
    {
      *indicator_spacing = *tmp_spacing;
      gtk_border_free (tmp_spacing);
    }
  else
    {
      *indicator_spacing = default_option_indicator_spacing;
    }
}

static gboolean
is_toolbar_child (GtkWidget *wid)
{
  while (wid)
    {
      if (GTK_IS_TOOLBAR (wid) || GTK_IS_HANDLE_BOX (wid))
	return TRUE;
      else
	wid = wid->parent;
    }

  return FALSE;
}

static gboolean
is_menu_tool_button_child (GtkWidget *wid)
{
  while (wid)
    {
      if (GTK_IS_MENU_TOOL_BUTTON (wid))
	return TRUE;
      else
	wid = wid->parent;
    }
  return FALSE;
}

static HPEN
get_light_pen ()
{
  if (!g_light_pen)
    {
      g_light_pen = CreatePen (PS_SOLID | PS_INSIDEFRAME, 1,
			       GetSysColor (COLOR_BTNHIGHLIGHT));
    }

  return g_light_pen;
}

static HPEN
get_dark_pen ()
{
  if (!g_dark_pen)
    {
      g_dark_pen = CreatePen (PS_SOLID | PS_INSIDEFRAME, 1,
			      GetSysColor (COLOR_BTNSHADOW));
    }

  return g_dark_pen;
}

static void
draw_3d_border (HDC hdc, RECT *rc, gboolean sunken)
{
  HPEN pen1, pen2;
  HGDIOBJ old_pen;

  if (sunken)
    {
      pen1 = get_dark_pen ();
      pen2 = get_light_pen ();
    }
  else
    {
      pen1 = get_light_pen ();
      pen2 = get_dark_pen ();
    }

  MoveToEx (hdc, rc->left, rc->bottom - 1, NULL);

  old_pen = SelectObject (hdc, pen1);
  LineTo (hdc, rc->left, rc->top);
  LineTo (hdc, rc->right - 1, rc->top);
  SelectObject (hdc, old_pen);

  old_pen = SelectObject (hdc, pen2);
  LineTo (hdc, rc->right - 1, rc->bottom - 1);
  LineTo (hdc, rc->left, rc->bottom - 1);
  SelectObject (hdc, old_pen);
}

static gboolean
draw_menu_item (GdkWindow *window, GtkWidget *widget, GtkStyle *style,
		gint x, gint y, gint width, gint height,
		GtkStateType state_type, GdkRectangle *area)
{
  GtkWidget *parent;
  GtkMenuShell *bar;
  HDC dc;
  RECT rect;
  XpDCInfo dc_info;

  if (xp_theme_is_active ())
    {
      return (xp_theme_draw (window, XP_THEME_ELEMENT_MENU_ITEM, style,
                             x, y, width, height, state_type, area));
    }

  if ((parent = gtk_widget_get_parent (widget))
      && GTK_IS_MENU_BAR (parent) && !xp_theme_is_active ())
    {
      bar = GTK_MENU_SHELL (parent);

      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

      if (state_type == GTK_STATE_PRELIGHT)
	{
	  draw_3d_border (dc, &rect, bar->active);
	}

      release_window_dc (&dc_info);

      return TRUE;
    }

  return FALSE;
}

static HBRUSH
get_dither_brush (void)
{
  WORD pattern[8];
  HBITMAP pattern_bmp;
  int i;

  if (g_dither_brush)
    return g_dither_brush;

  for (i = 0; i < 8; i++)
    {
      pattern[i] = (WORD) (0x5555 << (i & 1));
    }

  pattern_bmp = CreateBitmap (8, 8, 1, 1, &pattern);

  if (pattern_bmp)
    {
      g_dither_brush = CreatePatternBrush (pattern_bmp);
      DeleteObject (pattern_bmp);
    }

  return g_dither_brush;
}

static gboolean
draw_tool_button (GdkWindow *window, GtkWidget *widget, GtkStyle *style,
		  gint x, gint y, gint width, gint height,
		  GtkStateType state_type, GdkRectangle *area)
{
  HDC dc;
  RECT rect;
  XpDCInfo dc_info;
  gboolean is_toggled = FALSE;

  if (xp_theme_is_active ())
    {
      return (xp_theme_draw (window, XP_THEME_ELEMENT_TOOLBAR_BUTTON, style,
			     x, y, width, height, state_type, area));
    }

  if (GTK_IS_TOGGLE_BUTTON (widget))
    {
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
	{
	  is_toggled = TRUE;
	}
    }

  if (state_type != GTK_STATE_PRELIGHT
      && state_type != GTK_STATE_ACTIVE && !is_toggled)
    {
      return FALSE;
    }

  dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);
  if (state_type == GTK_STATE_PRELIGHT)
    {
      if (is_toggled)
	{
	  FillRect (dc, &rect, GetSysColorBrush (COLOR_BTNFACE));
	}

      draw_3d_border (dc, &rect, is_toggled);
    }
  else if (state_type == GTK_STATE_ACTIVE)
    {
      if (is_toggled && !is_menu_tool_button_child (widget->parent))
	{
	  SetTextColor (dc, GetSysColor (COLOR_3DHILIGHT));
	  SetBkColor (dc, GetSysColor (COLOR_BTNFACE));
	  FillRect (dc, &rect, get_dither_brush ());
	}

      draw_3d_border (dc, &rect, TRUE);
    }

  release_window_dc (&dc_info);

  return TRUE;
}

static void
draw_push_button (GdkWindow *window, GtkWidget *widget, GtkStyle *style,
		  gint x, gint y, gint width, gint height,
		  GtkStateType state_type, gboolean is_default)
{
  HDC dc;
  RECT rect;
  XpDCInfo dc_info;

  dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

  if (GTK_IS_TOGGLE_BUTTON (widget))
    {
      if (state_type == GTK_STATE_PRELIGHT &&
	  gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
	{
	  state_type = GTK_STATE_ACTIVE;
	}
    }

  if (state_type == GTK_STATE_ACTIVE)
    {
      if (GTK_IS_TOGGLE_BUTTON (widget))
	{
	  DrawEdge (dc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
	  SetTextColor (dc, GetSysColor (COLOR_3DHILIGHT));
	  SetBkColor (dc, GetSysColor (COLOR_BTNFACE));
	  FillRect (dc, &rect, get_dither_brush ());
	}
      else
	{
	  FrameRect (dc, &rect, GetSysColorBrush (COLOR_WINDOWFRAME));
	  InflateRect (&rect, -1, -1);
	  FrameRect (dc, &rect, GetSysColorBrush (COLOR_BTNSHADOW));
	  InflateRect (&rect, -1, -1);
	  FillRect (dc, &rect, GetSysColorBrush (COLOR_BTNFACE));
	}
    }
  else
    {
      if (is_default || gtk_widget_has_focus (widget))
	{
	  FrameRect (dc, &rect, GetSysColorBrush (COLOR_WINDOWFRAME));
	  InflateRect (&rect, -1, -1);
	}

      DrawFrameControl (dc, &rect, DFC_BUTTON, DFCS_BUTTONPUSH);
    }

  release_window_dc (&dc_info);
}

static void
draw_box (GtkStyle *style,
	  GdkWindow *window,
	  GtkStateType state_type,
	  GtkShadowType shadow_type,
	  GdkRectangle *area,
	  GtkWidget *widget,
	  const gchar *detail, gint x, gint y, gint width, gint height)
{
  if (is_combo_box_child (widget) && DETAIL("button"))
    {
      RECT rect;
      XpDCInfo dc_info;
      DWORD border;
      HDC dc;
      int cx;

      border = (GTK_TOGGLE_BUTTON (widget)->active ? DFCS_PUSHED | DFCS_FLAT : 0);

      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);
      DrawFrameControl (dc, &rect, DFC_SCROLL, DFCS_SCROLLDOWN | border);
      release_window_dc (&dc_info);

      if (xp_theme_is_active ()
	  && xp_theme_draw (window, XP_THEME_ELEMENT_COMBOBUTTON, style, x, y,
			    width, height, state_type, area))
	{
      cx = GetSystemMetrics(SM_CXVSCROLL);
      x += width - cx;
      width = cx;


      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width - cx, height, &rect);
      FillRect (dc, &rect, GetSysColorBrush (COLOR_WINDOW));
      release_window_dc (&dc_info);
      return;
	}
    }

  if (DETAIL("button") || DETAIL("buttondefault"))
    {
      if (GTK_IS_TREE_VIEW (widget->parent) || GTK_IS_CLIST (widget->parent))
      {
        if (xp_theme_draw
	      (window, XP_THEME_ELEMENT_LIST_HEADER, style, x, y,
	       width, height, state_type, area))
	    {
	      return;
	    }
	  else
	    {
	      HDC dc;
	      RECT rect;
	      XpDCInfo dc_info;
	      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

	      DrawFrameControl (dc, &rect, DFC_BUTTON, DFCS_BUTTONPUSH |
				(state_type ==
				 GTK_STATE_ACTIVE ? (DFCS_PUSHED | DFCS_FLAT)
				 : 0));
	      release_window_dc (&dc_info);
	    }
	}
      else if (is_toolbar_child (widget->parent)
	       || (!GTK_IS_BUTTON (widget) ||
		   (GTK_RELIEF_NONE == gtk_button_get_relief (GTK_BUTTON (widget)))))
	{
	  if (draw_tool_button (window, widget, style, x, y,
				width, height, state_type, area))
	    {
	      return;
	    }
	}
      else
	{
	  gboolean is_default = gtk_widget_has_default (widget);
	  if (xp_theme_draw
	      (window,
	       is_default ? XP_THEME_ELEMENT_DEFAULT_BUTTON :
	       XP_THEME_ELEMENT_BUTTON, style, x, y, width, height,
	       state_type, area))
	    {
	      return;
	    }

	  draw_push_button (window, widget, style,
			    x, y, width, height, state_type, is_default);

	  return;
	}

      return;
    }
  else if (DETAIL("spinbutton"))
    {
      if (xp_theme_is_drawable (XP_THEME_ELEMENT_SPIN_BUTTON_UP))
	{
	  return;
	}
    }
  else if (DETAIL("spinbutton_up") || DETAIL("spinbutton_down"))
    {
      if (!xp_theme_draw (window,
			  DETAIL("spinbutton_up")
			  ? XP_THEME_ELEMENT_SPIN_BUTTON_UP
			  : XP_THEME_ELEMENT_SPIN_BUTTON_DOWN,
			  style, x, y, width, height, state_type, area))
	{
	  RECT rect;
	  XpDCInfo dc_info;
	  HDC dc;

	  dc = get_window_dc (style, window, state_type, &dc_info,
			      x, y, width, height, &rect);
	  DrawEdge (dc, &rect,
		    state_type ==
		    GTK_STATE_ACTIVE ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT);
	  release_window_dc (&dc_info);
	}
      return;
    }
  else if (DETAIL("slider"))
    {
      if (GTK_IS_SCROLLBAR (widget))
	{
	  GtkScrollbar *scrollbar = GTK_SCROLLBAR (widget);
	  GtkOrientation orientation;
	  gboolean is_vertical;

          orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));

          if (orientation == GTK_ORIENTATION_VERTICAL)
            is_vertical = TRUE;
          else
            is_vertical = FALSE;

	  if (xp_theme_draw (window,
			     is_vertical
			     ? XP_THEME_ELEMENT_SCROLLBAR_V
			     : XP_THEME_ELEMENT_SCROLLBAR_H,
			     style, x, y, width, height, state_type, area))
	    {
	      XpThemeElement gripper =
		(is_vertical ? XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_V :
		 XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_H);

	      /* Do not display grippers on tiny scroll bars,
	         the limit imposed is rather arbitrary, perhaps
	         we can fetch the gripper geometry from
	         somewhere and use that... */
	      if ((gripper ==
		   XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_H
		   && width < 16)
		  || (gripper ==
		      XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_V && height < 16))
		{
		  return;
		}

	      xp_theme_draw (window, gripper, style, x, y,
			     width, height, state_type, area);
	      return;
	    }
	  else
	    {
              if (gtk_range_get_adjustment(&scrollbar->range)->page_size >=
        	  (gtk_range_get_adjustment(&scrollbar->range)->upper -
        	   gtk_range_get_adjustment(&scrollbar->range)->lower))
		{
		  return;
		}
	    }
	}
    }
  else if (DETAIL("bar"))
    {
      if (widget && GTK_IS_PROGRESS_BAR (widget))
	{
	  GtkProgressBar *progress_bar = GTK_PROGRESS_BAR (widget);
	  XpThemeElement xp_progress_bar =
	    map_gtk_progress_bar_to_xp (progress_bar, FALSE);

	  if (xp_theme_draw (window, xp_progress_bar, style, x, y,
			     width, height, state_type, area))
	    {
	      return;
	    }

	  shadow_type = GTK_SHADOW_NONE;
	}
    }
  else if (DETAIL("menuitem"))
    {
      shadow_type = GTK_SHADOW_NONE;
      if (draw_menu_item (window, widget, style,
			  x, y, width, height, state_type, area))
	{
	  return;
	}
    }
  else if (DETAIL("trough"))
    {
      if (widget && GTK_IS_PROGRESS_BAR (widget))
	{
	  GtkProgressBar *progress_bar = GTK_PROGRESS_BAR (widget);
	  XpThemeElement xp_progress_bar =
	    map_gtk_progress_bar_to_xp (progress_bar, TRUE);
	  if (xp_theme_draw
	      (window, xp_progress_bar, style, x, y, width, height,
	       state_type, area))
	    {
	      return;
	    }
	  else
	    {
	      /* Blank in classic Windows */
	    }
	}
      else if (widget && GTK_IS_SCROLLBAR (widget))
	{
          GtkOrientation orientation;
	  gboolean is_vertical;

          orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));

          if (orientation == GTK_ORIENTATION_VERTICAL)
            is_vertical = TRUE;
          else
            is_vertical = FALSE;

	  if (xp_theme_draw (window,
			     is_vertical
			     ? XP_THEME_ELEMENT_TROUGH_V
			     : XP_THEME_ELEMENT_TROUGH_H,
			     style, x, y, width, height, state_type, area))
	    {
	      return;
	    }
	  else
	    {
	      HDC dc;
	      RECT rect;
	      XpDCInfo dc_info;

	      sanitize_size (window, &width, &height);
	      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

	      SetTextColor (dc, GetSysColor (COLOR_3DHILIGHT));
	      SetBkColor (dc, GetSysColor (COLOR_BTNFACE));
	      FillRect (dc, &rect, get_dither_brush ());

	      release_window_dc (&dc_info);

	      return;
	    }
	}
      else if (widget && GTK_IS_SCALE (widget))
	{
          GtkOrientation orientation;

          orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));

	  if (!xp_theme_is_active ())
	    {
	      parent_class->draw_box (style, window, state_type,
				      GTK_SHADOW_NONE, area,
				      widget, detail, x, y, width, height);
	    }

	  if (orientation == GTK_ORIENTATION_VERTICAL)
	    {
	      if (xp_theme_draw
		  (window, XP_THEME_ELEMENT_SCALE_TROUGH_V,
		   style, (2 * x + width) / 2, y, 2, height,
		   state_type, area))
		{
		  return;
		}

	      parent_class->draw_box (style, window, state_type,
				      GTK_SHADOW_ETCHED_IN,
				      area, NULL, NULL,
				      (2 * x + width) / 2, y, 1, height);
	    }
	  else
	    {
	      if (xp_theme_draw
		  (window, XP_THEME_ELEMENT_SCALE_TROUGH_H,
		   style, x, (2 * y + height) / 2, width, 2,
		   state_type, area))
		{
		  return;
		}

	      parent_class->draw_box (style, window, state_type,
				      GTK_SHADOW_ETCHED_IN,
				      area, NULL, NULL, x,
				      (2 * y + height) / 2, width, 1);
	    }

	  return;
	}
    }
  else if (DETAIL("optionmenu"))
    {
      if (xp_theme_draw (window, XP_THEME_ELEMENT_EDIT_TEXT,
			 style, x, y, width, height, state_type, area))
	{
	  return;
	}
    }
  else if (DETAIL("vscrollbar") || DETAIL("hscrollbar"))
    {
      return;
    }
  else if (DETAIL("handlebox_bin") || DETAIL("toolbar") || DETAIL("menubar"))
    {
      sanitize_size (window, &width, &height);
      if (xp_theme_draw (window, XP_THEME_ELEMENT_REBAR,
			 style, x, y, width, height, state_type, area))
	{
	  return;
	}
    }
  else if (DETAIL("handlebox"))	/* grip */
    {
      if (!xp_theme_is_active ())
	{
	  return;
	}
    }
  else if (DETAIL("notebook") && GTK_IS_NOTEBOOK (widget))
    {
      if (xp_theme_draw (window, XP_THEME_ELEMENT_TAB_PANE, style,
			 x, y, width, height, state_type, area))
	{
	  return;
	}
    }

  else
    {
      const gchar *name = gtk_widget_get_name (widget);

      if (name && !strcmp (name, "gtk-tooltips"))
	{
	  if (xp_theme_draw
	      (window, XP_THEME_ELEMENT_TOOLTIP, style, x, y, width,
	       height, state_type, area))
	    {
	      return;
	    }
	  else
	    {
	      HBRUSH brush;
	      RECT rect;
	      XpDCInfo dc_info;
	      HDC hdc;

	      hdc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

	      brush = GetSysColorBrush (COLOR_3DDKSHADOW);

	      if (brush)
		{
		  FrameRect (hdc, &rect, brush);
		}

	      InflateRect (&rect, -1, -1);
	      FillRect (hdc, &rect, (HBRUSH) (COLOR_INFOBK + 1));

	      release_window_dc (&dc_info);

	      return;
	    }
	}
    }

  parent_class->draw_box (style, window, state_type, shadow_type, area,
			  widget, detail, x, y, width, height);

  if (DETAIL("optionmenu"))
    {
      GtkRequisition indicator_size;
      GtkBorder indicator_spacing;
      gint vline_x;

      option_menu_get_props (widget, &indicator_size, &indicator_spacing);

      sanitize_size (window, &width, &height);

      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
	{
	  vline_x =
	    x + indicator_size.width + indicator_spacing.left +
	    indicator_spacing.right;
	}
      else
	{
	  vline_x = x + width - (indicator_size.width +
				 indicator_spacing.left +
				 indicator_spacing.right) - style->xthickness;

	  parent_class->draw_vline (style, window, state_type, area, widget,
				    detail,
				    y + style->ythickness + 1,
				    y + height - style->ythickness - 3, vline_x);
	}
    }
}

static void
draw_tab (GtkStyle *style,
	  GdkWindow *window,
	  GtkStateType state,
	  GtkShadowType shadow,
	  GdkRectangle *area,
	  GtkWidget *widget,
	  const gchar *detail, gint x, gint y, gint width, gint height)
{
  GtkRequisition indicator_size;
  GtkBorder indicator_spacing;

  gint arrow_height;

  g_return_if_fail (style != NULL);
  g_return_if_fail (window != NULL);

  if (DETAIL("optionmenutab"))
    {
      if (xp_theme_draw (window, XP_THEME_ELEMENT_COMBOBUTTON,
			 style, x - 5, widget->allocation.y + 1,
			 width + 10, widget->allocation.height - 2,
			 state, area))
	{
	  return;
	}
    }

  option_menu_get_props (widget, &indicator_size, &indicator_spacing);

  x += (width - indicator_size.width) / 2;
  arrow_height = (indicator_size.width + 1) / 2;

  y += (height - arrow_height) / 2;

  draw_varrow (window, &style->black, shadow, area, GTK_ARROW_DOWN,
	       x, y, indicator_size.width, arrow_height);
}

/* Draw classic Windows tab - thanks Mozilla!
  (no system API for this, but DrawEdge can draw all the parts of a tab) */
static void
DrawTab (HDC hdc, const RECT R, gint32 aPosition, gboolean aSelected,
	 gboolean aDrawLeft, gboolean aDrawRight)
{
  gint32 leftFlag, topFlag, rightFlag, lightFlag, shadeFlag;
  RECT topRect, sideRect, bottomRect, lightRect, shadeRect;
  gint32 selectedOffset, lOffset, rOffset;

  selectedOffset = aSelected ? 1 : 0;
  lOffset = aDrawLeft ? 2 : 0;
  rOffset = aDrawRight ? 2 : 0;

  /* Get info for tab orientation/position (Left, Top, Right, Bottom) */
  switch (aPosition)
    {
    case BF_LEFT:
      leftFlag = BF_TOP;
      topFlag = BF_LEFT;
      rightFlag = BF_BOTTOM;
      lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
      shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;

      SetRect (&topRect, R.left, R.top + lOffset, R.right,
	       R.bottom - rOffset);
      SetRect (&sideRect, R.left + 2, R.top, R.right - 2 + selectedOffset,
	       R.bottom);
      SetRect (&bottomRect, R.right - 2, R.top, R.right, R.bottom);
      SetRect (&lightRect, R.left, R.top, R.left + 3, R.top + 3);
      SetRect (&shadeRect, R.left + 1, R.bottom - 2, R.left + 2,
	       R.bottom - 1);
      break;

    case BF_TOP:
      leftFlag = BF_LEFT;
      topFlag = BF_TOP;
      rightFlag = BF_RIGHT;
      lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
      shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;

      SetRect (&topRect, R.left + lOffset, R.top, R.right - rOffset,
	       R.bottom);
      SetRect (&sideRect, R.left, R.top + 2, R.right,
	       R.bottom - 1 + selectedOffset);
      SetRect (&bottomRect, R.left, R.bottom - 1, R.right, R.bottom);
      SetRect (&lightRect, R.left, R.top, R.left + 3, R.top + 3);
      SetRect (&shadeRect, R.right - 2, R.top + 1, R.right - 1, R.top + 2);
      break;

    case BF_RIGHT:
      leftFlag = BF_TOP;
      topFlag = BF_RIGHT;
      rightFlag = BF_BOTTOM;
      lightFlag = BF_DIAGONAL_ENDTOPLEFT;
      shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;

      SetRect (&topRect, R.left, R.top + lOffset, R.right,
	       R.bottom - rOffset);
      SetRect (&sideRect, R.left + 2 - selectedOffset, R.top, R.right - 2,
	       R.bottom);
      SetRect (&bottomRect, R.left, R.top, R.left + 2, R.bottom);
      SetRect (&lightRect, R.right - 3, R.top, R.right - 1, R.top + 2);
      SetRect (&shadeRect, R.right - 2, R.bottom - 3, R.right, R.bottom - 1);
      break;

    case BF_BOTTOM:
      leftFlag = BF_LEFT;
      topFlag = BF_BOTTOM;
      rightFlag = BF_RIGHT;
      lightFlag = BF_DIAGONAL_ENDTOPLEFT;
      shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;

      SetRect (&topRect, R.left + lOffset, R.top, R.right - rOffset,
	       R.bottom);
      SetRect (&sideRect, R.left, R.top + 2 - selectedOffset, R.right,
	       R.bottom - 2);
      SetRect (&bottomRect, R.left, R.top, R.right, R.top + 2);
      SetRect (&lightRect, R.left, R.bottom - 3, R.left + 2, R.bottom - 1);
      SetRect (&shadeRect, R.right - 2, R.bottom - 3, R.right, R.bottom - 1);
      break;

    default:
      g_return_if_reached ();
    }

  /* Background */
  FillRect (hdc, &R, (HBRUSH) (COLOR_3DFACE + 1));

  /* Tab "Top" */
  DrawEdge (hdc, &topRect, EDGE_RAISED, BF_SOFT | topFlag);

  /* Tab "Bottom" */
  if (!aSelected)
    DrawEdge (hdc, &bottomRect, EDGE_RAISED, BF_SOFT | topFlag);

  /* Tab "Sides" */
  if (!aDrawLeft)
    leftFlag = 0;
  if (!aDrawRight)
    rightFlag = 0;

  DrawEdge (hdc, &sideRect, EDGE_RAISED, BF_SOFT | leftFlag | rightFlag);

  /* Tab Diagonal Corners */
  if (aDrawLeft)
    DrawEdge (hdc, &lightRect, EDGE_RAISED, BF_SOFT | lightFlag);

  if (aDrawRight)
    DrawEdge (hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
}

static void
get_notebook_tab_position (GtkNotebook *notebook,
                           gboolean *start,
                           gboolean *end)
{
  gboolean found_start = FALSE, found_end = FALSE;
  gint i, n_pages;

  /* default value */
  *start = TRUE;
  *end = FALSE;

  n_pages = gtk_notebook_get_n_pages (notebook);
  for (i = 0; i < n_pages; i++)
    {
      GtkWidget *tab_child;
      GtkWidget *tab_label;
      gboolean expand;
      GtkPackType pack_type;
      gboolean is_selected;

      tab_child = gtk_notebook_get_nth_page (notebook, i);
      is_selected = gtk_notebook_get_current_page (notebook) == i;

      /* Skip invisible tabs */
      tab_label = gtk_notebook_get_tab_label (notebook, tab_child);
      if (!tab_label || !GTK_WIDGET_VISIBLE (tab_label))
        continue;

      /* Mimics what the notebook does internally. */
      if (tab_label && !gtk_widget_get_child_visible (tab_label))
        {
          /* One child is hidden because scroll arrows are present.
           * So both corners are rounded. */
          *start = FALSE;
          *end = FALSE;
          return;
        }

      gtk_notebook_query_tab_label_packing (notebook, tab_child, &expand,
                                            NULL, /* don't need fill */
                                            &pack_type);

      if (pack_type == GTK_PACK_START)
        {
          if (!found_start)
            {
              /* This is the first tab with PACK_START pack type */
              found_start = TRUE;

              if (is_selected)
                {
                  /* first PACK_START item is selected: set start to TRUE */
                  *start = TRUE;

                  if (expand && !found_end)
                    {
                      /* tentatively set end to TRUE: will be invalidated if we
                       * find other items */
                      *end = TRUE;
                    }
                }
              else
                {
                  *start = FALSE;
                }
            }
          else if (!found_end && !is_selected)
            {
              /* an unselected item exists, and no item with PACK_END pack type */
              *end = FALSE;
            }
        }

      if (pack_type == GTK_PACK_END)
        {
          if (!found_end)
            {
              /* This is the first tab with PACK_END pack type */
              found_end = TRUE;

              if (is_selected)
                {
                  /* first PACK_END item is selected: set end to TRUE */
                  *end = TRUE;

                  if (expand && !found_start)
                    {
                      /* tentatively set start to TRUE: will be invalidated if
                       * we find other items */
                      *start = TRUE;
                    }
                }
              else
                {
                *end = FALSE;
                }
            }
          else if (!found_start && !is_selected)
            {
              *start = FALSE;
            }
        }
    }
}

static gboolean
draw_themed_tab_button (GtkStyle *style,
			GdkWindow *window,
			GtkStateType state_type,
			GtkNotebook *notebook,
			gint x,
			gint y,
			gint width,
			gint height,
			gint gap_side)
{
  GdkPixmap *pixmap = NULL;
  GdkRectangle draw_rect, clip_rect;
  cairo_t *cr;
  gboolean start, stop;
  XpThemeElement element;
  gint d_w, d_h;

  get_notebook_tab_position (notebook, &start, &stop);

  if (state_type == GTK_STATE_NORMAL)
    {
      if (start && stop)
        {
          /* Both edges of the notebook are covered by the item */
          element = XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE;
        }
      else if (start)
        {
          /* The start edge is covered by the item */
          element = XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE;
        }
      else if (stop)
        {
          /* the stop edge is reached by the item */
          element = XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE;
        }
      else
        {
          /* no edge should be aligned with the tab */
          element = XP_THEME_ELEMENT_TAB_ITEM;
        }
    }
  else
    {
      /* Ideally, we should do the same here. Unfortunately, we don't have ways
       * to determine what tab widget is actually being drawn here, so we can't
       * determine its position relative to the borders */
      element = XP_THEME_ELEMENT_TAB_ITEM;
    }

  draw_rect.x = x;
  draw_rect.y = y;
  draw_rect.width = width;
  draw_rect.height = height;

  /* Perform adjustments required to have the theme perfectly aligned */
  if (state_type == GTK_STATE_ACTIVE)
    {
      switch (gap_side)
        {
        case GTK_POS_TOP:
          draw_rect.x += 2;
          draw_rect.width -= 2;
          draw_rect.height -= 1;
          break;
        case GTK_POS_BOTTOM:
          draw_rect.x += 2;
          draw_rect.width -= 2;
          draw_rect.y += 1;
          draw_rect.height -= 1;
          break;
        case GTK_POS_LEFT:
          draw_rect.y += 2;
          draw_rect.height -= 2;
          draw_rect.width -= 1;
          break;
        case GTK_POS_RIGHT:
          draw_rect.y += 2;
          draw_rect.height -= 2;
          draw_rect.x += 1;
          draw_rect.width -= 1;
          break;
        }
    }
  else
    {
      switch (gap_side)
        {
        case GTK_POS_TOP:
          draw_rect.height += 1;
          draw_rect.width += 2;
          break;
        case GTK_POS_BOTTOM:
          draw_rect.y -= 1;
          draw_rect.height += 1;
          draw_rect.width += 2;
          break;
        case GTK_POS_LEFT:
          draw_rect.width += 1;
          draw_rect.height += 2;
          break;
        case GTK_POS_RIGHT:
          draw_rect.x -= 1;
          draw_rect.width += 1;
          draw_rect.height += 2;
          break;
        }
    }

  clip_rect = draw_rect;

  /* Take care of obvious case where the clipping is an empty region */
  if (clip_rect.width <= 0 || clip_rect.height <= 0)
    return TRUE;

  /* Simple case: tabs on top are just drawn as is */
  if (gap_side == GTK_POS_TOP)
    {
       return xp_theme_draw (window, element, style,
	                     draw_rect.x, draw_rect.y,
	                     draw_rect.width, draw_rect.height,
	                     state_type, &clip_rect);
    }

  /* For other cases, we need to print the tab on a pixmap, and then rotate
   * it according to the gap side */
  if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT)
    {
      /* pixmap will have width/height inverted as we'll rotate +- PI / 2 */
      d_w = draw_rect.height;
      d_h = draw_rect.width;
    }
  else
    {
      d_w = draw_rect.width;
      d_h = draw_rect.height;
    }

  pixmap = gdk_pixmap_new (window, d_w, d_h, -1);

  /* First copy the previously saved window background */
  cr = gdk_cairo_create (pixmap);

  /* pixmaps unfortunately don't handle the alpha channel. We then
   * paint it first in white, hoping the actual background is clear */
  cairo_set_source_rgb (cr, 1, 1, 1);
  cairo_paint (cr);
  cairo_destroy (cr);

  if (!xp_theme_draw (pixmap, element, style, 0, 0, d_w, d_h, state_type, 0))
    {
      g_object_unref (pixmap);
      return FALSE;
    }

  /* Now we have the pixmap, we need to flip/rotate it according to its
   * final position. We'll do it using cairo on the dest window */
  cr = gdk_cairo_create (window);
  cairo_rectangle (cr, clip_rect.x, clip_rect.y,
                   clip_rect.width, clip_rect.height);
  cairo_clip (cr);
  cairo_translate(cr, draw_rect.x + draw_rect.width * 0.5,
                  draw_rect.y + draw_rect.height * 0.5);

  if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT) {
    cairo_rotate (cr, G_PI/2.0);
  }

  if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_BOTTOM) {
    cairo_scale (cr, 1, -1);
  }

  cairo_translate(cr, -d_w * 0.5, -d_h * 0.5);
  gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
  cairo_paint (cr);
  cairo_destroy (cr);

  g_object_unref (pixmap);

  return TRUE;
}

static gboolean
draw_tab_button (GtkStyle *style,
		 GdkWindow *window,
		 GtkStateType state_type,
		 GtkShadowType shadow_type,
		 GdkRectangle *area,
		 GtkWidget *widget,
		 const gchar *detail,
		 gint x, gint y, gint width, gint height, gint gap_side)
{
  if (gap_side == GTK_POS_TOP || gap_side == GTK_POS_BOTTOM)
    {
      /* experimental tab-drawing code from mozilla */
      RECT rect;
      XpDCInfo dc_info;
      HDC dc;
      gint32 aPosition;
	  cairo_t *cr;

      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);
	  cr = gdk_cairo_create (window);

      if (gap_side == GTK_POS_TOP)
	aPosition = BF_TOP;
      else if (gap_side == GTK_POS_BOTTOM)
	aPosition = BF_BOTTOM;
      else if (gap_side == GTK_POS_LEFT)
	aPosition = BF_LEFT;
      else
	aPosition = BF_RIGHT;

      if (state_type == GTK_STATE_PRELIGHT)
	state_type = GTK_STATE_NORMAL;
      if (area)
        {
           gdk_cairo_rectangle (cr, area);
           cairo_clip (cr);
           gdk_cairo_set_source_color (cr, &style->dark[state_type]);
        }

      DrawTab (dc, rect, aPosition,
	       state_type != GTK_STATE_PRELIGHT,
	       (gap_side != GTK_POS_LEFT), (gap_side != GTK_POS_RIGHT));

      cairo_destroy (cr);

      release_window_dc (&dc_info);
      return TRUE;
    }

  return FALSE;
}

static void
draw_extension (GtkStyle *style,
		GdkWindow *window,
		GtkStateType state_type,
		GtkShadowType shadow_type,
		GdkRectangle *area,
		GtkWidget *widget,
		const gchar *detail,
		gint x, gint y,
		gint width, gint height, GtkPositionType gap_side)
{
  if (widget && GTK_IS_NOTEBOOK (widget) && DETAIL("tab"))
    {
      GtkNotebook *notebook = GTK_NOTEBOOK (widget);

      /* draw_themed_tab_button and draw_tab_button expect to work with tab
       * position, instead of simply taking the "side of the gap" (gap_side).
       * The gap side, simply said, is the side of the tab that touches the notebook
       * frame and is always the exact opposite of the tab position... */
      int tab_pos = gtk_notebook_get_tab_pos (notebook);

      if (!draw_themed_tab_button (style, window, state_type,
				   notebook, x, y,
				   width, height, tab_pos))
	{
	  if (!draw_tab_button (style, window, state_type,
				shadow_type, area, widget,
				detail, x, y, width, height, tab_pos))
	    {
	      /* GtkStyle expects the usual gap_side */
	      parent_class->draw_extension (style, window, state_type,
					    shadow_type, area, widget, detail,
					    x, y, width, height,
					    gap_side);
	    }
	}
    }
}

static void
draw_box_gap (GtkStyle *style,
              GdkWindow *window,
              GtkStateType state_type,
	      GtkShadowType shadow_type,
	      GdkRectangle *area,
	      GtkWidget *widget,
	      const gchar *detail,
	      gint x,
	      gint y,
	      gint width,
	      gint height,
	      GtkPositionType gap_side,
	      gint gap_x,
	      gint gap_width)
{
  if (GTK_IS_NOTEBOOK (widget) && DETAIL("notebook"))
    {
      GtkNotebook *notebook = GTK_NOTEBOOK (widget);

      int side = gtk_notebook_get_tab_pos (notebook);
      int x2 = x, y2 = y;
      int w2 = width + style->xthickness, h2 = height + style->ythickness;

      switch (side)
        {
        case GTK_POS_TOP:
          y2 -= 1;
          break;
        case GTK_POS_BOTTOM:
          break;
        case GTK_POS_LEFT:
          x2 -= 1;
          break;
        case GTK_POS_RIGHT:
          w2 += 1;
          break;
        }

      if (xp_theme_draw (window, XP_THEME_ELEMENT_TAB_PANE, style,
			 x2, y2, w2, h2, state_type, area))
	{
	  return;
	}
    }

  parent_class->draw_box_gap (style, window, state_type, shadow_type,
			      area, widget, detail, x, y, width, height,
			      gap_side, gap_x, gap_width);
}

static gboolean
is_popup_window_child (GtkWidget *widget)
{
  GtkWidget *top;
  GtkWindowType type = -1;

  top = gtk_widget_get_toplevel (widget);

  if (top && GTK_IS_WINDOW (top))
    {
      g_object_get (top, "type", &type, NULL);

      if (type == GTK_WINDOW_POPUP)
	{			/* Hack for combo boxes */
	  return TRUE;
	}
    }

  return FALSE;
}

static void
draw_flat_box (GtkStyle *style, GdkWindow *window,
	       GtkStateType state_type, GtkShadowType shadow_type,
	       GdkRectangle *area, GtkWidget *widget,
	       const gchar *detail, gint x, gint y, gint width, gint height)
{
  if (detail)
    {
      if (state_type == GTK_STATE_SELECTED &&
	  (!strncmp ("cell_even", detail, 9) || !strncmp ("cell_odd", detail, 8)))
	{
          GdkColor *gc = gtk_widget_has_focus (widget) ? &style->base[state_type] : &style->base[GTK_STATE_ACTIVE];
          cairo_t *cr = gdk_cairo_create (window);

          _cairo_draw_rectangle (cr, gc, TRUE, x, y, width, height);

		  cairo_destroy (cr);

	  return;
	}
      else if (DETAIL("checkbutton"))
	{
	  if (state_type == GTK_STATE_PRELIGHT)
	    {
	      return;
	    }
	}
    }

  parent_class->draw_flat_box (style, window, state_type, shadow_type,
			       area, widget, detail, x, y, width, height);
}

static gboolean
draw_menu_border (GdkWindow *win, GtkStyle *style,
		  gint x, gint y, gint width, gint height)
{
  RECT rect;
  XpDCInfo dc_info;
  HDC dc;

  dc = get_window_dc (style, win, GTK_STATE_NORMAL, &dc_info, x, y, width, height, &rect);

  if (!dc)
    return FALSE;

  if (xp_theme_is_active ())
    {
      FrameRect (dc, &rect, GetSysColorBrush (COLOR_3DSHADOW));
    }
  else
    {
      DrawEdge (dc, &rect, EDGE_RAISED, BF_RECT);
    }

  release_window_dc (&dc_info);

  return TRUE;
}

static void
draw_shadow (GtkStyle *style,
	     GdkWindow *window,
	     GtkStateType state_type,
	     GtkShadowType shadow_type,
	     GdkRectangle *area,
	     GtkWidget *widget,
	     const gchar *detail, gint x, gint y, gint width, gint height)
{
  gboolean is_handlebox;
  gboolean is_toolbar;

  if (DETAIL("frame"))
    {

      HDC dc;
      RECT rect;
      XpDCInfo dc_info;



      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);
      if (is_combo_box_child (widget))
        {
          FillRect (dc, &rect, GetSysColorBrush (COLOR_WINDOW));
        }
      else if (is_popup_window_child (widget))
	{
	  FrameRect (dc, &rect, GetSysColorBrush (COLOR_WINDOWFRAME));
	}
      else
	{
	  switch (shadow_type)
	    {
	    case GTK_SHADOW_IN:
	      draw_3d_border (dc, &rect, TRUE);
	      break;

	    case GTK_SHADOW_OUT:
	      draw_3d_border (dc, &rect, FALSE);
	      break;

	    case GTK_SHADOW_ETCHED_IN:
	      draw_3d_border (dc, &rect, TRUE);
	      InflateRect (&rect, -1, -1);
	      draw_3d_border (dc, &rect, FALSE);
	      break;

	    case GTK_SHADOW_ETCHED_OUT:
	      draw_3d_border (dc, &rect, FALSE);
	      InflateRect (&rect, -1, -1);
	      draw_3d_border (dc, &rect, TRUE);
	      break;

	    case GTK_SHADOW_NONE:
	      break;
	    }
	}

      release_window_dc (&dc_info);

      return;
    }
  if (DETAIL("entry") || DETAIL("combobox"))
    {
      if (shadow_type != GTK_SHADOW_IN)
	return;

      if (!xp_theme_draw (window, XP_THEME_ELEMENT_EDIT_TEXT, style,
			  x, y, width, height, state_type, area))
	{
	  HDC dc;
	  RECT rect;
	  XpDCInfo dc_info;

	  dc = get_window_dc (style, window, state_type, &dc_info,
			      x, y, width, height, &rect);

	  DrawEdge (dc, &rect, EDGE_SUNKEN, BF_RECT);
	  release_window_dc (&dc_info);
	}

      return;
    }

  if (DETAIL("scrolled_window") &&
      xp_theme_draw (window, XP_THEME_ELEMENT_EDIT_TEXT, style,
		     x, y, width, height, state_type, area))
    {
      return;
    }

  if (DETAIL("spinbutton"))
    return;

  if (DETAIL("menu"))
    {
      if (draw_menu_border (window, style, x, y, width, height))
	{
	  return;
	}
    }

  if (DETAIL("handlebox"))
    return;

  is_handlebox = (DETAIL("handlebox_bin"));
  is_toolbar = (DETAIL("toolbar") || DETAIL("menubar"));

  if (is_toolbar || is_handlebox)
    {
      if (shadow_type == GTK_SHADOW_NONE)
	{
	  return;
	}

      if (widget)
	{
	  HDC dc;
	  RECT rect;
	  XpDCInfo dc_info;
	  HGDIOBJ old_pen = NULL;
	  GtkPositionType pos;

	  sanitize_size (window, &width, &height);

	  if (is_handlebox)
	    {
	      pos = gtk_handle_box_get_handle_position (GTK_HANDLE_BOX (widget));
	      /*
	         If the handle box is at left side,
	         we shouldn't draw its right border.
	         The same holds true for top, right, and bottom.
	       */
	      switch (pos)
		{
		case GTK_POS_LEFT:
		  pos = GTK_POS_RIGHT;
		  break;

		case GTK_POS_RIGHT:
		  pos = GTK_POS_LEFT;
		  break;

		case GTK_POS_TOP:
		  pos = GTK_POS_BOTTOM;
		  break;

		case GTK_POS_BOTTOM:
		  pos = GTK_POS_TOP;
		  break;
		}
	    }
	  else
	    {
	      GtkWidget *parent = gtk_widget_get_parent (widget);

	      /* Dirty hack for toolbars contained in handle boxes */
	      if (GTK_IS_HANDLE_BOX (parent))
		{
		  pos = gtk_handle_box_get_handle_position (GTK_HANDLE_BOX (parent));
		}
	      else
		{
		  /*
		     Dirty hack:
		     Make pos != all legal enum vaules of GtkPositionType.
		     So every border will be draw.
		   */
		  pos = (GtkPositionType) - 1;
		}
	    }

	  dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

	  if (pos != GTK_POS_LEFT)
	    {
	      old_pen = SelectObject (dc, get_light_pen ());
	      MoveToEx (dc, rect.left, rect.top, NULL);
	      LineTo (dc, rect.left, rect.bottom);
	    }
	  if (pos != GTK_POS_TOP)
	    {
	      old_pen = SelectObject (dc, get_light_pen ());
	      MoveToEx (dc, rect.left, rect.top, NULL);
	      LineTo (dc, rect.right, rect.top);
	    }
	  if (pos != GTK_POS_RIGHT)
	    {
	      old_pen = SelectObject (dc, get_dark_pen ());
	      MoveToEx (dc, rect.right - 1, rect.top, NULL);
	      LineTo (dc, rect.right - 1, rect.bottom);
	    }
	  if (pos != GTK_POS_BOTTOM)
	    {
	      old_pen = SelectObject (dc, get_dark_pen ());
	      MoveToEx (dc, rect.left, rect.bottom - 1, NULL);
	      LineTo (dc, rect.right, rect.bottom - 1);
	    }
	  if (old_pen)
	    SelectObject (dc, old_pen);
	  release_window_dc (&dc_info);
	}

      return;
    }

  if (DETAIL("statusbar"))
    {
      return;
    }

  parent_class->draw_shadow (style, window, state_type, shadow_type, area,
			     widget, detail, x, y, width, height);
}

static void
draw_hline (GtkStyle *style,
	    GdkWindow *window,
	    GtkStateType state_type,
	    GdkRectangle *area,
	    GtkWidget *widget,
	    const gchar *detail, gint x1, gint x2, gint y)
{
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);

  if (xp_theme_is_active () && DETAIL("menuitem"))
    {
      gint cx, cy;
      gint new_y, new_height;
      gint y_offset;

      xp_theme_get_element_dimensions (XP_THEME_ELEMENT_MENU_SEPARATOR,
				       state_type,
				       &cx, &cy);

      /* Center the separator */
      y_offset = (area->height / 2) - (cy / 2);
      new_y = y_offset >= 0 ? area->y + y_offset : area->y;
      new_height = cy;

      if (xp_theme_draw
	  (window, XP_THEME_ELEMENT_MENU_SEPARATOR, style, x1, new_y, x2, new_height,
	   state_type, area))
	{
	  return;
	}
      else
	{
	  if (area)
	    {
              gdk_cairo_rectangle (cr, area);
              cairo_clip (cr);
	    }

          _cairo_draw_line (cr, &style->dark[state_type], x1, y, x2, y);

	}
    }
  else
    {
      if (style->ythickness == 2)
	{
	  if (area)
	    {
              gdk_cairo_rectangle (cr, area);
              cairo_clip (cr);
	    }

          _cairo_draw_line (cr, &style->dark[state_type], x1, y, x2, y);
	  ++y;
          _cairo_draw_line (cr, &style->light[state_type], x1, y, x2, y);

	}
      else
	{
	  parent_class->draw_hline (style, window, state_type, area, widget,
				    detail, x1, x2, y);
	}
    }
  cairo_destroy (cr);
}

static void
draw_vline (GtkStyle *style,
	    GdkWindow *window,
	    GtkStateType state_type,
	    GdkRectangle *area,
	    GtkWidget *widget,
	    const gchar *detail, gint y1, gint y2, gint x)
{
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);

  if (style->xthickness == 2)
    {
      if (area)
	{
              gdk_cairo_rectangle (cr, area);
              cairo_clip (cr);
	}

      _cairo_draw_line (cr, &style->dark[state_type], x, y1, x, y2);
      ++x;
      _cairo_draw_line (cr, &style->light[state_type], x, y1, x, y2);

    }
  else
    {
      parent_class->draw_vline (style, window, state_type, area, widget,
				detail, y1, y2, x);
    }

  cairo_destroy (cr);
}

static void
draw_slider (GtkStyle *style,
	     GdkWindow *window,
	     GtkStateType state_type,
	     GtkShadowType shadow_type,
	     GdkRectangle *area,
	     GtkWidget *widget,
	     const gchar *detail,
	     gint x,
	     gint y, gint width, gint height, GtkOrientation orientation)
{
  if (GTK_IS_SCALE (widget) &&
      xp_theme_draw (window, ((orientation == GTK_ORIENTATION_VERTICAL) ?
			      XP_THEME_ELEMENT_SCALE_SLIDER_V :
			      XP_THEME_ELEMENT_SCALE_SLIDER_H), style, x, y, width,
		     height, state_type, area))
    {
      return;
    }

  parent_class->draw_slider (style, window, state_type, shadow_type, area,
			     widget, detail, x, y, width, height,
			     orientation);
}

static void
draw_resize_grip (GtkStyle *style,
		  GdkWindow *window,
		  GtkStateType state_type,
		  GdkRectangle *area,
		  GtkWidget *widget,
		  const gchar *detail,
		  GdkWindowEdge edge, gint x, gint y, gint width, gint height)
{
  cairo_t *cr;
  
  cr = gdk_cairo_create (window);
  
  if (DETAIL("statusbar"))
    {
      if (xp_theme_draw
	  (window, XP_THEME_ELEMENT_STATUS_GRIPPER, style, x, y, width,
	   height, state_type, area))
	{
          cairo_destroy (cr);
	  return;
	}
      else
	{
	  RECT rect;
	  XpDCInfo dc_info;
	  HDC dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

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

	  DrawFrameControl (dc, &rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
	  release_window_dc (&dc_info);

          cairo_destroy (cr);
	  return;
	}
    }

  parent_class->draw_resize_grip (style, window, state_type, area,
				  widget, detail, edge, x, y, width, height);
}

static void
draw_handle (GtkStyle      *style,
             GdkWindow     *window,
             GtkStateType   state_type,
             GtkShadowType  shadow_type,
             GdkRectangle  *area,
             GtkWidget     *widget,
             const gchar   *detail,
             gint           x,
             gint           y,
             gint           width,
             gint           height,
             GtkOrientation orientation)
{
  if (is_toolbar_child (widget))
    {
      HDC dc;
      RECT rect;
      XpDCInfo dc_info;
      XpThemeElement hndl;

      sanitize_size (window, &width, &height);

      if (GTK_IS_HANDLE_BOX (widget))
	{
	  GtkPositionType pos;
	  pos = gtk_handle_box_get_handle_position (GTK_HANDLE_BOX (widget));

	  if (pos == GTK_POS_TOP || pos == GTK_POS_BOTTOM)
	      orientation = GTK_ORIENTATION_HORIZONTAL;
	  else
	      orientation = GTK_ORIENTATION_VERTICAL;
	}

      if (orientation == GTK_ORIENTATION_VERTICAL)
	hndl = XP_THEME_ELEMENT_REBAR_GRIPPER_V;
      else
	hndl = XP_THEME_ELEMENT_REBAR_GRIPPER_H;

      if (xp_theme_draw (window, hndl, style, x, y, width, height, state_type, area))
	return;

      dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);

      if (orientation == GTK_ORIENTATION_VERTICAL)
	{
	  rect.left += 3;
	  rect.right = rect.left + 3;
	  rect.bottom -= 3;
	  rect.top += 3;
	}
      else
	{
	  rect.top += 3;
	  rect.bottom = rect.top + 3;
	  rect.right -= 3;
	  rect.left += 3;
	}

      draw_3d_border (dc, &rect, FALSE);
      release_window_dc (&dc_info);
      return;
    }
}

static void
draw_focus (GtkStyle *style,
	    GdkWindow *window,
	    GtkStateType state_type,
	    GdkRectangle *area,
	    GtkWidget *widget,
	    const gchar *detail, gint x, gint y, gint width, gint height)
{
  HDC dc;
  RECT rect;
  XpDCInfo dc_info;

  if (!gtk_widget_get_can_focus (widget))
    {
      return;
    }

  if (is_combo_box_child (widget)
      && (GTK_IS_ARROW (widget) || GTK_IS_BUTTON (widget)))
    {
      return;
    }
  if (GTK_IS_TREE_VIEW (widget->parent)	/* list view bheader */
      || GTK_IS_CLIST (widget->parent))
    {
      return;
    }

  dc = get_window_dc (style, window, state_type, &dc_info, x, y, width, height, &rect);
  DrawFocusRect (dc, &rect);
  release_window_dc (&dc_info);
/*
    parent_class->draw_focus (style, window, state_type,
						     area, widget, detail, x, y, width, height);
*/
}

static void
draw_layout (GtkStyle *style,
	     GdkWindow *window,
	     GtkStateType state_type,
	     gboolean use_text,
	     GdkRectangle *area,
	     GtkWidget *widget,
	     const gchar *detail,
	     gint old_x, gint old_y, PangoLayout *layout)
{
  GtkNotebook *notebook = NULL;
  gint x = old_x;
  gint y = old_y;

  /* In the XP theme, labels don't appear correctly centered inside
   * notebook tabs, so we give them a gentle nudge two pixels to the
   * right.  A little hackish, but what are 'ya gonna do?  -- Cody
   */
  if (xp_theme_is_active () && DETAIL("label"))
    {
      if (widget->parent != NULL)
	{
	  if (GTK_IS_NOTEBOOK (widget->parent))
	    {
	      int side;
	      notebook = GTK_NOTEBOOK (widget->parent);
	      side = gtk_notebook_get_tab_pos (notebook);

	      if (side == GTK_POS_TOP || side == GTK_POS_BOTTOM)
		{
		  x += 2;
		}
	    }
	}
    }

  parent_class->draw_layout (style, window, state_type,
			     use_text, area, widget, detail, x, y, layout);
}

static void
msw_style_init_from_rc (GtkStyle *style, GtkRcStyle *rc_style)
{
  setup_system_font (style);
  setup_menu_settings (gtk_settings_get_default ());
  setup_system_styles (style);
  parent_class->init_from_rc (style, rc_style);
}

static void
msw_style_class_init (MswStyleClass *klass)
{
  GtkStyleClass *style_class = GTK_STYLE_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  style_class->init_from_rc = msw_style_init_from_rc;
  style_class->draw_arrow = draw_arrow;
  style_class->draw_box = draw_box;
  style_class->draw_check = draw_check;
  style_class->draw_option = draw_option;
  style_class->draw_tab = draw_tab;
  style_class->draw_flat_box = draw_flat_box;
  style_class->draw_expander = draw_expander;
  style_class->draw_extension = draw_extension;
  style_class->draw_box_gap = draw_box_gap;
  style_class->draw_shadow = draw_shadow;
  style_class->draw_hline = draw_hline;
  style_class->draw_vline = draw_vline;
  style_class->draw_handle = draw_handle;
  style_class->draw_resize_grip = draw_resize_grip;
  style_class->draw_slider = draw_slider;
  style_class->draw_focus = draw_focus;
  style_class->draw_layout = draw_layout;
}

GType msw_type_style = 0;

void
msw_style_register_type (GTypeModule *module)
{
  const GTypeInfo object_info = {
    sizeof (MswStyleClass),
    (GBaseInitFunc) NULL,
    (GBaseFinalizeFunc) NULL,
    (GClassInitFunc) msw_style_class_init,
    NULL,			/* class_finalize */
    NULL,			/* class_data */
    sizeof (MswStyle),
    0,				/* n_preallocs */
    (GInstanceInitFunc) NULL,
  };

  msw_type_style = g_type_module_register_type (module,
						GTK_TYPE_STYLE,
						"MswStyle", &object_info, 0);
}

void
msw_style_init (void)
{
  xp_theme_init ();
  msw_style_setup_system_settings ();
  setup_msw_rc_style ();

  if (g_light_pen)
    {
      DeleteObject (g_light_pen);
      g_light_pen = NULL;
    }

  if (g_dark_pen)
    {
      DeleteObject (g_dark_pen);
      g_dark_pen = NULL;
    }
}

void
msw_style_finalize (void)
{
  if (g_dither_brush)
    {
      DeleteObject (g_dither_brush);
    }

  if (g_light_pen)
    {
      DeleteObject (g_light_pen);
    }

  if (g_dark_pen)
    {
      DeleteObject (g_dark_pen);
    }
}
