gtk2/modules/engines/ms-windows/msw_style.c

3506 lines
92 KiB
C
Executable File

/* 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);
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);
}
}