diff options
Diffstat (limited to 'matchbox2/mb-wm-theme.c')
-rw-r--r-- | matchbox2/mb-wm-theme.c | 2099 |
1 files changed, 2099 insertions, 0 deletions
diff --git a/matchbox2/mb-wm-theme.c b/matchbox2/mb-wm-theme.c new file mode 100644 index 0000000..93e8fe7 --- /dev/null +++ b/matchbox2/mb-wm-theme.c @@ -0,0 +1,2099 @@ +/* + * Matchbox Window Manager II - A lightweight window manager not for the + * desktop. + * + * Authored By Tomas Frydrych <tf@o-hand.com> + * + * Copyright (c) 2005 OpenedHand Ltd - http://o-hand.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + */ + +#include "mb-wm-theme.h" +#include "mb-wm-theme-xml.h" + +#include <sys/stat.h> +#include <expat.h> +#include <X11/Xft/Xft.h> + +#define SIMPLE_FRAME_TITLEBAR_HEIGHT 40 +#define SIMPLE_FRAME_EDGE_SIZE 3 + +MBWMThemeCustomClientTypeFunc custom_client_type_func = NULL; +void *custom_client_type_func_data = NULL; + +MBWMThemeCustomButtonTypeFunc custom_button_type_func = NULL; +void *custom_button_type_func_data = NULL; + +MBWMThemeCustomThemeTypeFunc custom_theme_type_func = NULL; +void *custom_theme_type_func_data = NULL; + +MBWMThemeCustomThemeAllocFunc custom_theme_alloc_func = NULL; + +static void +xml_element_start_cb (void *data, const char *tag, const char **expat_attr); + +static void +xml_element_end_cb (void *data, const char *tag); + +static void +xml_stack_free (MBWMList *stack); + +static void +mb_wm_theme_simple_paint_decor (MBWMTheme *theme, MBWMDecor *decor); +static void +mb_wm_theme_simple_paint_button (MBWMTheme *theme, MBWMDecorButton *button); +static void +mb_wm_theme_simple_get_decor_dimensions (MBWMTheme *, MBWindowManagerClient *, + int *, int *, int *, int *); +static void +mb_wm_theme_simple_get_button_size (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int *, int *); +static void +mb_wm_theme_simple_get_button_position (MBWMTheme *, MBWMDecor *, + MBWMDecorButtonType, int*, int*); +static MBWMDecor * +mb_wm_theme_simple_create_decor (MBWMTheme *, MBWindowManagerClient *, + MBWMDecorType); + +static void +mb_wm_theme_class_init (MBWMObjectClass *klass) +{ + MBWMThemeClass *t_class = MB_WM_THEME_CLASS (klass); + + t_class->paint_decor = mb_wm_theme_simple_paint_decor; + t_class->paint_button = mb_wm_theme_simple_paint_button; + t_class->decor_dimensions = mb_wm_theme_simple_get_decor_dimensions; + t_class->button_size = mb_wm_theme_simple_get_button_size; + t_class->button_position = mb_wm_theme_simple_get_button_position; + t_class->create_decor = mb_wm_theme_simple_create_decor; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMTheme"; +#endif +} + +static void +mb_wm_theme_destroy (MBWMObject *obj) +{ + MBWMTheme *theme = MB_WM_THEME (obj); + + if (theme->path) + free (theme->path); + + MBWMList *l = theme->xml_clients; + + while (l) + { + MBWMXmlClient * c = l->data; + MBWMList * n = l->next; + mb_wm_xml_client_free (c); + free (l); + + l = n; + } +} + +static int +mb_wm_theme_init (MBWMObject *obj, va_list vap) +{ + MBWMTheme *theme = MB_WM_THEME (obj); + MBWindowManager *wm = NULL; + MBWMObjectProp prop; + MBWMList *xml_clients = NULL; + char *path = NULL; + MBWMColor *clr_lowlight = NULL; + MBWMColor *clr_shadow = NULL; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + case MBWMObjectPropThemePath: + path = va_arg(vap, char *); + break; + case MBWMObjectPropThemeXmlClients: + xml_clients = va_arg(vap, MBWMList *); + break; + case MBWMObjectPropThemeColorLowlight: + clr_lowlight = va_arg(vap, MBWMColor *); + break; + case MBWMObjectPropThemeColorShadow: + clr_shadow = va_arg(vap, MBWMColor *); + break; + case MBWMObjectPropThemeShadowType: + theme->shadow_type = va_arg(vap, int); + break; + case MBWMObjectPropThemeCompositing: + theme->compositing = va_arg(vap, int); + break; + case MBWMObjectPropThemeShaped: + theme->shaped = va_arg(vap, int); + break; + + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + theme->wm = wm; + theme->xml_clients = xml_clients; + + if (path) + theme->path = strdup (path); + + if (clr_shadow && clr_shadow->set) + { + theme->color_shadow.r = clr_shadow->r; + theme->color_shadow.g = clr_shadow->g; + theme->color_shadow.b = clr_shadow->b; + theme->color_shadow.a = clr_shadow->a; + } + else + { + theme->color_shadow.r = 0.0; + theme->color_shadow.g = 0.0; + theme->color_shadow.b = 0.0; + theme->color_shadow.a = 0.95; + } + + if (clr_lowlight && clr_lowlight->set) + { + theme->color_lowlight.r = clr_lowlight->r; + theme->color_lowlight.g = clr_lowlight->g; + theme->color_lowlight.b = clr_lowlight->b; + theme->color_lowlight.a = clr_lowlight->a; + } + else + { + theme->color_lowlight.r = 0.0; + theme->color_lowlight.g = 0.0; + theme->color_lowlight.b = 0.0; + theme->color_lowlight.a = 0.55; + } + + return 1; +} + +int +mb_wm_theme_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMThemeClass), + sizeof (MBWMTheme), + mb_wm_theme_init, + mb_wm_theme_destroy, + mb_wm_theme_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +Bool +mb_wm_theme_is_button_press_activated (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type) +{ + MBWindowManagerClient * client; + MBWMXmlClient * c; + MBWMXmlDecor * d; + MBWMXmlButton * b; + MBWMClientType c_type; + + if (!theme || !theme->xml_clients || !decor || !decor->parent_client) + return False; + + client = decor->parent_client; + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) && + (b = mb_wm_xml_button_find_by_type (d->buttons, type))) + { + return b->press_activated; + } + + return False; +} + +void +mb_wm_theme_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (decor && decor->parent_client); + + if (!theme || !decor || !decor->parent_client) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->button_size) + klass->button_size (theme, decor, type, width, height); +} + +/* + * If the parent decor uses absolute postioning, the returned values + * are absolute. If the decor does packing, these values are added to + * calculated button position. + */ +void +mb_wm_theme_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (decor && decor->parent_client); + + if (!theme || !decor || !decor->parent_client) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->button_position) + klass->button_position (theme, decor, type, x, y); + else + { + if (x) + *x = 2; + + if (y) + *y = 2; + } +} + +void +mb_wm_theme_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (client); + + if (!theme || !client) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->decor_dimensions) + klass->decor_dimensions (theme, client, north, south, west, east); +} + +void +mb_wm_theme_paint_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMThemeClass *klass; + + if (!theme) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->paint_decor) + klass->paint_decor (theme, decor); +} + +void +mb_wm_theme_paint_button (MBWMTheme *theme, MBWMDecorButton *button) +{ + MBWMThemeClass *klass; + + if (!theme) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->paint_button) + klass->paint_button (theme, button); +} + +Bool +mb_wm_theme_supports (MBWMTheme *theme, MBWMThemeCaps capability) +{ + if (theme) + return False; + + return ((capability & theme->caps) != False); +} + +typedef enum +{ + XML_CTX_UNKNOWN = 0, + XML_CTX_THEME, + XML_CTX_CLIENT, + XML_CTX_DECOR, + XML_CTX_BUTTON, + XML_CTX_IMG, +} XmlCtx; + +struct stack_data +{ + XmlCtx ctx; + void *data; +}; + +struct expat_data +{ + XML_Parser par; + int theme_type; + int version; + MBWMList *xml_clients; + char *img; + MBWMList *stack; + MBWMColor color_lowlight; + MBWMColor color_shadow; + MBWMCompMgrShadowType shadow_type; + Bool compositing; + Bool shaped; +}; + +MBWMTheme * +mb_wm_theme_new (MBWindowManager * wm, const char * theme_path) +{ + MBWMTheme *theme = NULL; + int theme_type = 0; + char *path = NULL; + char buf[256]; + XML_Parser par = NULL; + FILE *file = NULL; + MBWMList *xml_clients = NULL; + char *img = NULL; + MBWMColor clr_lowlight; + MBWMColor clr_shadow; + MBWMCompMgrShadowType shadow_type; + Bool compositing; + Bool shaped; + struct stat st; + + /* + * If no theme specified, we try to load the default one, if that fails, + * we automatically fallback on the built-in defaults. + */ + if (!theme_path) + theme_path = "Default"; + + /* Attempt to parse the xml theme, if any, retrieving the theme type + * + * NB: We cannot do this in the _init function, since we need to know the + * type *before* we can create the underlying object on which the + * init method operates. + */ + + if (*theme_path == '/') + { + if (!stat (theme_path, &st)) + path = (char *) theme_path; + } + else + { + const char *home = getenv("HOME"); + int size; + + if (home) + { + const char *fmt = "%s/.themes/%s/matchbox2/theme.xml"; + + size = strlen (theme_path) + strlen (fmt) + strlen (home); + path = alloca (size); + snprintf (path, size, fmt, home, theme_path); + + if (stat (path, &st)) + path = NULL; + } + + if (!path) + { + const char * fmt = "%s/themes/%s/matchbox2/theme.xml"; + + size = strlen (theme_path) + strlen (fmt) + strlen (DATADIR); + path = alloca (size); + snprintf (path, size, fmt, DATADIR, theme_path); + + if (stat (path, &st)) + path = NULL; + } + } + + if (path) + { + struct expat_data udata; + + if (!(file = fopen (path, "r")) || + !(par = XML_ParserCreate(NULL))) + { + goto default_theme; + } + + memset (&udata, 0, sizeof (struct expat_data)); + udata.compositing = True; + udata.par = par; + + XML_SetElementHandler (par, + xml_element_start_cb, + xml_element_end_cb); + + XML_SetUserData(par, (void *)&udata); + + while (fgets (buf, sizeof (buf), file) && + XML_Parse(par, buf, strlen(buf), 0)); + + XML_Parse(par, NULL, 0, 1); + + if (udata.version == 2) + { + theme_type = udata.theme_type; + xml_clients = udata.xml_clients; + + if (udata.img) + { + if (*udata.img == '/') + img = udata.img; + else + { + int len = strlen (path) + strlen (udata.img); + char * s; + char * p = malloc (len + 1); + strncpy (p, path, len); + + s = strrchr (p, '/'); + + if (s) + { + *(s+1) = 0; + strcat (p, udata.img); + } + else + { + strncpy (p, udata.img, len); + } + + img = p; + free (udata.img); + } + } + } + + clr_lowlight.r = udata.color_lowlight.r; + clr_lowlight.g = udata.color_lowlight.g; + clr_lowlight.b = udata.color_lowlight.b; + clr_lowlight.a = udata.color_lowlight.a; + clr_lowlight.set = udata.color_lowlight.set; + + clr_shadow.r = udata.color_shadow.r; + clr_shadow.g = udata.color_shadow.g; + clr_shadow.b = udata.color_shadow.b; + clr_shadow.a = udata.color_shadow.a; + clr_shadow.set = udata.color_shadow.set; + + shadow_type = udata.shadow_type; + compositing = udata.compositing; + shaped = udata.shaped; + + xml_stack_free (udata.stack); + } + + if (custom_theme_alloc_func) + { + theme = + custom_theme_alloc_func (theme_type, + MBWMObjectPropWm, wm, + MBWMObjectPropThemePath, path, + MBWMObjectPropThemeImg, img, + MBWMObjectPropThemeXmlClients, xml_clients, + MBWMObjectPropThemeColorLowlight, &clr_lowlight, + MBWMObjectPropThemeColorShadow, &clr_shadow, + MBWMObjectPropThemeShadowType, shadow_type, + MBWMObjectPropThemeCompositing, compositing, + MBWMObjectPropThemeShaped, shaped, + NULL); + } + else if (theme_type) + { + theme = + MB_WM_THEME (mb_wm_object_new (theme_type, + MBWMObjectPropWm, wm, + MBWMObjectPropThemePath, path, + MBWMObjectPropThemeImg, img, + MBWMObjectPropThemeXmlClients, xml_clients, + MBWMObjectPropThemeColorLowlight, &clr_lowlight, + MBWMObjectPropThemeColorShadow, &clr_shadow, + MBWMObjectPropThemeShadowType, shadow_type, + MBWMObjectPropThemeCompositing, compositing, + MBWMObjectPropThemeShaped, shaped, + NULL)); + } + + default_theme: + + if (!theme) + { + theme = MB_WM_THEME (mb_wm_object_new ( + MB_WM_TYPE_THEME, + MBWMObjectPropWm, wm, + MBWMObjectPropThemeXmlClients, xml_clients, + MBWMObjectPropThemeColorLowlight, &clr_lowlight, + MBWMObjectPropThemeColorShadow, &clr_shadow, + MBWMObjectPropThemeShadowType, shadow_type, + MBWMObjectPropThemeCompositing, compositing, + MBWMObjectPropThemeShaped, shaped, + NULL)); + } + + if (par) + XML_ParserFree (par); + + if (file) + fclose (file); + + if (img) + free (img); + + return theme; +} + +MBWMDecor * +mb_wm_theme_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (client); + + if (!theme || !client) + return NULL; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->create_decor) + return klass->create_decor (theme, client, type); + + return NULL; +} + +void +mb_wm_theme_resize_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMThemeClass *klass; + + MBWM_ASSERT (decor); + + if (!theme || !decor) + return; + + klass = MB_WM_THEME_CLASS(MB_WM_OBJECT_GET_CLASS (theme)); + + if (klass->resize_decor) + klass->resize_decor (theme, decor); +} + +MBWMClientLayoutHints +mb_wm_theme_get_client_layout_hints (MBWMTheme * theme, + MBWindowManagerClient * client) +{ + MBWMXmlClient * c; + MBWMClientType c_type; + + if (!client || !theme) + return 0; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if (!theme->xml_clients || + !(c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + return 0; + } + + return c->layout_hints; +} + +/* + * Returns True if the theme prescribes at least one value for the geometry + */ +Bool +mb_wm_theme_get_client_geometry (MBWMTheme * theme, + MBWindowManagerClient * client, + MBGeometry * geom) +{ + MBWMXmlClient * c; + MBWMClientType c_type; + + if (!geom || !client || !theme) + return False; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if (!theme || !theme->xml_clients || + !(c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) || + (c->x < 0 && c->y < 0 && c->width < 0 && c->height < 0)) + { + return False; + } + + geom->x = c->x; + geom->y = c->y; + geom->width = c->width; + geom->height = c->height; + + return True; +} + +Bool +mb_wm_theme_is_client_shaped (MBWMTheme * theme, + MBWindowManagerClient * client) +{ +#ifdef HAVE_XEXT + MBWMXmlClient * c; + MBWMClientType c_type; + + if (!client || !theme || !theme->shaped || client->is_argb32) + return False; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if (theme->xml_clients && + (c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + return c->shaped; + } + + return False; +#else + return False; +#endif +} + +/* + * Retrieves color to be used for lowlighting (16-bit rgba) + */ +void +mb_wm_theme_get_lowlight_color (MBWMTheme * theme, + unsigned int * red, + unsigned int * green, + unsigned int * blue, + unsigned int * alpha) +{ + if (theme) + { + if (red) + *red = (unsigned int)(theme->color_lowlight.r * (double)0xffff); + + if (green) + *green = (unsigned int)(theme->color_lowlight.g * (double)0xffff); + + if (blue) + *blue = (unsigned int)(theme->color_lowlight.b * (double)0xffff); + + if (alpha) + *alpha = (unsigned int)(theme->color_lowlight.a * (double)0xffff); + + return; + } + + if (red) + *red = 0; + + if (green) + *green = 0; + + if (blue) + *blue = 0; + + if (*alpha) + *alpha = 0x8d8d; +} + +/* + * Retrieves color to be used for lowlighting (16-bit rgba) + */ +void +mb_wm_theme_get_shadow_color (MBWMTheme * theme, + unsigned int * red, + unsigned int * green, + unsigned int * blue, + unsigned int * alpha) +{ + if (theme) + { + if (red) + *red = (unsigned int)(theme->color_shadow.r * (double)0xffff); + + if (green) + *green = (unsigned int)(theme->color_shadow.g * (double)0xffff); + + if (blue) + *blue = (unsigned int)(theme->color_shadow.b * (double)0xffff); + + if (alpha) + *alpha = (unsigned int)(theme->color_shadow.a * (double)0xffff); + + return; + } + + if (red) + *red = 0; + + if (green) + *green = 0; + + if (blue) + *blue = 0; + + if (*alpha) + *alpha = 0xff00; +} + +MBWMCompMgrShadowType +mb_wm_theme_get_shadow_type (MBWMTheme * theme) +{ + if (!theme) + return MBWM_COMP_MGR_SHADOW_NONE; + + return theme->shadow_type; +} + +Bool +mb_wm_theme_use_compositing_mgr (MBWMTheme * theme) +{ + if (!theme) + return False; + + return theme->compositing; +} + +/* + * Expat callback stuff + */ + +static void +xml_stack_push (MBWMList ** stack, XmlCtx ctx) +{ + struct stack_data * s = malloc (sizeof (struct stack_data)); + + s->ctx = ctx; + s->data = NULL; + + *stack = mb_wm_util_list_prepend (*stack, s); +} + +static XmlCtx +xml_stack_top_ctx (MBWMList *stack) +{ + struct stack_data * s = stack->data; + + return s->ctx; +} + +static void * +xml_stack_top_data (MBWMList *stack) +{ + struct stack_data * s = stack->data; + + return s->data; +} + +static void +xml_stack_top_set_data (MBWMList *stack, void * data) +{ + struct stack_data * s = stack->data; + + s->data = data; +} + +static void +xml_stack_pop (MBWMList ** stack) +{ + MBWMList * top = *stack; + struct stack_data * s = top->data; + + *stack = top->next; + free (s); + free (top); +} + +static void +xml_stack_free (MBWMList *stack) +{ + MBWMList * l = stack; + while (l) + { + MBWMList * n = l->next; + free (l->data); + free (l); + + l = n; + } +} + +static void +xml_element_start_cb (void *data, const char *tag, const char **expat_attr) +{ + struct expat_data * exd = data; + + MBWM_DBG ("tag <%s>\n", tag); + + if (!strcmp (tag, "theme")) + { + MBWMColor clr; + const char ** p = expat_attr; + + xml_stack_push (&exd->stack, XML_CTX_THEME); + + while (*p) + { + if (!strcmp (*p, "engine-version")) + exd->version = atoi (*(p+1)); + else if (!strcmp (*p, "engine-type")) + { + if (!strcmp (*(p+1), "default")) + exd->theme_type = MB_WM_TYPE_THEME; +#if THEME_PNG + else if (!strcmp (*(p+1), "png")) + exd->theme_type = MB_WM_TYPE_THEME_PNG; +#endif + else if (custom_theme_type_func) + exd->theme_type = custom_theme_type_func (*(p+1), + custom_theme_type_func_data); + } + else if (!strcmp (*p, "shaped")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + exd->shaped = 1; + } + else if (!strcmp (*p, "color-shadow")) + { + mb_wm_xml_clr_from_string (&clr, *(p+1)); + + if (clr.set) + { + exd->color_shadow.r = clr.r; + exd->color_shadow.g = clr.g; + exd->color_shadow.b = clr.b; + exd->color_shadow.a = clr.a; + exd->color_shadow.set = True; + } + } + else if (!strcmp (*p, "color-lowlight")) + { + mb_wm_xml_clr_from_string (&clr, *(p+1)); + + if (clr.set) + { + exd->color_lowlight.r = clr.r; + exd->color_lowlight.g = clr.g; + exd->color_lowlight.b = clr.b; + exd->color_lowlight.a = clr.a; + exd->color_lowlight.set = True; + } + } + else if (!strcmp (*p, "shadow-type")) + { + if (!strcmp (*(p+1), "simple")) + exd->shadow_type = MBWM_COMP_MGR_SHADOW_SIMPLE; + else if (!strcmp (*(p+1), "gaussian")) + exd->shadow_type = MBWM_COMP_MGR_SHADOW_GAUSSIAN; + } + else if (!strcmp (*p, "compositing")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + exd->compositing = True; + else + exd->compositing = False; + } + + p += 2; + } + } + + if (!strcmp (tag, "client")) + { + MBWMXmlClient * c = mb_wm_xml_client_new (); + const char **p = expat_attr; + + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_CLIENT); + + if (ctx != XML_CTX_THEME) + { + MBWM_DBG ("Expected context theme"); + return; + } + + while (*p) + { + if (!strcmp (*p, "type")) + { + if (!strcmp (*(p+1), "app")) + c->type = MBWMClientTypeApp; + else if (!strcmp (*(p+1), "dialog")) + c->type = MBWMClientTypeDialog; + else if (!strcmp (*(p+1), "panel")) + c->type = MBWMClientTypePanel; + else if (!strcmp (*(p+1), "input")) + c->type = MBWMClientTypeInput; + else if (!strcmp (*(p+1), "desktop")) + c->type = MBWMClientTypeDesktop; + else if (!strcmp (*(p+1), "notification")) + c->type = MBWMClientTypeNote; + else if (custom_client_type_func) + c->type = custom_client_type_func (*(p+1), + custom_client_type_func_data); + } + else if (!strcmp (*p, "shaped")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + c->shaped = 1; + } + else if (!strcmp (*p, "width")) + { + c->width = atoi (*(p+1)); + } + else if (!strcmp (*p, "height")) + { + c->height = atoi (*(p+1)); + } + else if (!strcmp (*p, "x")) + { + c->x = atoi (*(p+1)); + } + else if (!strcmp (*p, "y")) + { + c->y = atoi (*(p+1)); + } + else if (!strcmp (*p, "layout-hints") && *(p+1)) + { + /* comma-separate list of hints */ + char * duph = strdup (*(p+1)); + char * comma; + char * h = duph; + + while (h) + { + comma = strchr (h, ','); + + if (comma) + *comma = 0; + + if (!strcmp (h, "reserve-edge-north")) + { + c->layout_hints |= LayoutPrefReserveEdgeNorth; + } + else if (!strcmp (h, "reserve-edge-south")) + { + c->layout_hints |= LayoutPrefReserveEdgeSouth; + } + else if (!strcmp (h, "reserve-edge-west")) + { + c->layout_hints |= LayoutPrefReserveEdgeWest; + } + else if (!strcmp (h, "reserve-edge-east")) + { + c->layout_hints |= LayoutPrefReserveEdgeEast; + } + if (!strcmp (h, "reserve-north")) + { + c->layout_hints |= LayoutPrefReserveNorth; + } + else if (!strcmp (h, "reserve-south")) + { + c->layout_hints |= LayoutPrefReserveSouth; + } + else if (!strcmp (h, "reserve-west")) + { + c->layout_hints |= LayoutPrefReserveWest; + } + else if (!strcmp (h, "reserve-east")) + { + c->layout_hints |= LayoutPrefReserveEast; + } + else if (!strcmp (h, "grow")) + { + c->layout_hints |= LayoutPrefGrowToFreeSpace; + } + else if (!strcmp (h, "free")) + { + c->layout_hints |= LayoutPrefPositionFree; + } + else if (!strcmp (h, "full-screen")) + { + c->layout_hints |= LayoutPrefFullscreen; + } + else if (!strcmp (h, "fixed-x")) + { + c->layout_hints |= LayoutPrefFixedX; + } + else if (!strcmp (h, "fixed-y")) + { + c->layout_hints |= LayoutPrefFixedY; + } + else if (!strcmp (h, "overlaps")) + { + c->layout_hints |= LayoutPrefOverlaps; + } + + if (comma) + h = comma + 1; + else + break; + } + + free (duph); + } + + p += 2; + } + + if (!c->type) + mb_wm_xml_client_free (c); + else + { + exd->xml_clients = mb_wm_util_list_prepend (exd->xml_clients, c); + xml_stack_top_set_data (exd->stack, c); + } + + + return; + } + + if (!strcmp (tag, "decor")) + { + MBWMXmlDecor * d = mb_wm_xml_decor_new (); + const char **p = expat_attr; + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + MBWMXmlClient * c = xml_stack_top_data (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_DECOR); + + if (ctx != XML_CTX_CLIENT || !c) + { + MBWM_DBG ("Expected context client"); + return; + } + + while (*p) + { + if (!strcmp (*p, "color-fg")) + mb_wm_xml_clr_from_string (&d->clr_fg, *(p+1)); + else if (!strcmp (*p, "color-bg")) + mb_wm_xml_clr_from_string (&d->clr_bg, *(p+1)); + else if (!strcmp (*p, "type")) + { + if (!strcmp (*(p+1), "north")) + d->type = MBWMDecorTypeNorth; + else if (!strcmp (*(p+1), "south")) + d->type = MBWMDecorTypeSouth; + else if (!strcmp (*(p+1), "east")) + d->type = MBWMDecorTypeEast; + else if (!strcmp (*(p+1), "west")) + d->type = MBWMDecorTypeWest; + } + else if (!strcmp (*p, "template-width")) + { + d->width = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-height")) + { + d->height = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-x")) + { + d->x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-y")) + { + d->y = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-pad-offset")) + { + d->pad_offset = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-pad-length")) + { + d->pad_length = atoi (*(p+1)); + } + else if (!strcmp (*p, "font-size")) + { + char * end_size = NULL; + + d->font_units = MBWMXmlFontUnitsPixels; + d->font_size = strtol (*(p+1), &end_size, 0); + + if (end_size && *end_size) + { + if (*end_size == 'p') + { + switch (*(end_size+1)) + { + case 't': + d->font_units = MBWMXmlFontUnitsPoints; + break; + case 'x': + default: + ; + } + } + } + } + else if (!strcmp (*p, "font-family")) + { + d->font_family = strdup (*(p+1)); + } + else if (!strcmp (*p, "show-title")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + d->show_title = 1; + } + + p += 2; + } + + if (!d->type) + mb_wm_xml_decor_free (d); + else + { + c->decors = mb_wm_util_list_prepend (c->decors, d); + xml_stack_top_set_data (exd->stack, d); + } + + return; + } + + if (!strcmp (tag, "button")) + { + MBWMXmlButton * b = mb_wm_xml_button_new (); + const char **p = expat_attr; + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + MBWMXmlDecor * d = xml_stack_top_data (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_BUTTON); + + if (ctx != XML_CTX_DECOR || !d) + { + MBWM_DBG ("Expected context decor"); + return; + } + + while (*p) + { + if (!strcmp (*p, "color-fg")) + mb_wm_xml_clr_from_string (&b->clr_fg, *(p+1)); + else if (!strcmp (*p, "color-bg")) + mb_wm_xml_clr_from_string (&b->clr_bg, *(p+1)); + else if (!strcmp (*p, "type")) + { + if (!strcmp (*(p+1), "minimize")) + b->type = MBWMDecorButtonMinimize; + else if (!strcmp (*(p+1), "close")) + b->type = MBWMDecorButtonClose; + else if (!strcmp (*(p+1), "menu")) + b->type = MBWMDecorButtonMenu; + else if (!strcmp (*(p+1), "accept")) + b->type = MBWMDecorButtonAccept; + else if (!strcmp (*(p+1), "fullscreen")) + b->type = MBWMDecorButtonFullscreen; + else if (!strcmp (*(p+1), "help")) + b->type = MBWMDecorButtonHelp; + else if (custom_button_type_func) + b->type = custom_button_type_func (*(p+1), + custom_button_type_func_data); + } + else if (!strcmp (*p, "packing")) + { + if (!strcmp (*(p+1), "end")) + b->packing = MBWMDecorButtonPackEnd; + else if (!strcmp (*(p+1), "start")) + b->packing = MBWMDecorButtonPackStart; + } + else if (!strcmp (*p, "template-x")) + { + b->x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-y")) + { + b->y = atoi (*(p+1)); + } + else if (!strcmp (*p, "width")) + { + b->width = atoi (*(p+1)); + } + else if (!strcmp (*p, "height")) + { + b->height = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-active-x")) + { + b->active_x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-active-y")) + { + b->active_y = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-inactive-x")) + { + b->inactive_x = atoi (*(p+1)); + } + else if (!strcmp (*p, "template-inactive-y")) + { + b->inactive_y = atoi (*(p+1)); + } + else if (!strcmp (*p, "press-activated")) + { + if (!strcmp (*(p+1), "yes") || !strcmp (*(p+1), "1")) + b->press_activated = 1; + } + + p += 2; + } + + if (!b->type) + { + mb_wm_xml_button_free (b); + return; + } + + d->buttons = mb_wm_util_list_append (d->buttons, b); + + xml_stack_top_set_data (exd->stack, b); + + return; + } + + if (!strcmp (tag, "img")) + { + const char **p = expat_attr; + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + + xml_stack_push (&exd->stack, XML_CTX_IMG); + + if (ctx != XML_CTX_THEME) + { + MBWM_DBG ("Expected context theme"); + return; + } + + while (*p) + { + if (!strcmp (*p, "src")) + { + exd->img = strdup (*(p+1)); + return; + } + + p += 2; + } + + return; + } + +} + +static void +xml_element_end_cb (void *data, const char *tag) +{ + struct expat_data * exd = data; + + XmlCtx ctx = xml_stack_top_ctx (exd->stack); + + MBWM_DBG ("tag </%s>\n", tag); + + if (!strcmp (tag, "theme")) + { + XML_StopParser (exd->par, 0); + } + else if (!strcmp (tag, "client")) + { + if (ctx == XML_CTX_CLIENT) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected client on the top of the stack!"); + } + else if (!strcmp (tag, "decor")) + { + if (ctx == XML_CTX_DECOR) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected decor on the top of the stack!"); + } + else if (!strcmp (tag, "button")) + { + if (ctx == XML_CTX_BUTTON) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected button on the top of the stack!"); + } + else if (!strcmp (tag, "img")) + { + if (ctx == XML_CTX_IMG) + { + xml_stack_pop (&exd->stack); + } + else + MBWM_DBG ("Expected img on the top of the stack!"); + } +} + + + +static void +construct_buttons (MBWMTheme * theme, + MBWMDecor * decor, MBWMXmlDecor *d) +{ + MBWindowManagerClient *client = decor->parent_client; + MBWindowManager *wm = client->wmref; + MBWMDecorButton *button; + + if (d) + { + MBWMList * l = d->buttons; + while (l) + { + MBWMXmlButton * b = l->data; + + button = mb_wm_decor_button_stock_new (wm, + b->type, + b->packing, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + l = l->next; + } + + return; + } + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonClose, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + +#if 0 + /* + * We probably do not want this in the default client, but for now + * it is useful for testing purposes + */ + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonFullscreen, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonHelp, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonAccept, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonMinimize, + MBWMDecorButtonPackEnd, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); + + button = mb_wm_decor_button_stock_new (wm, + MBWMDecorButtonMenu, + MBWMDecorButtonPackStart, + decor, + 0); + + mb_wm_decor_button_show (button); + mb_wm_object_unref (MB_WM_OBJECT (button)); +#endif +} + +struct DecorData +{ + Pixmap xpix; + XftDraw *xftdraw; + XftColor clr; + XftFont *font; +}; + +static void +decordata_free (MBWMDecor * decor, void *data) +{ + struct DecorData * dd = data; + Display * xdpy = decor->parent_client->wmref->xdpy; + + XFreePixmap (xdpy, dd->xpix); + + XftDrawDestroy (dd->xftdraw); + + if (dd->font) + XftFontClose (xdpy, dd->font); + + free (dd); +} + +static MBWMDecor * +mb_wm_theme_simple_create_decor (MBWMTheme *theme, + MBWindowManagerClient *client, + MBWMDecorType type) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMDecor *decor = NULL; + MBWindowManager *wm = client->wmref; + MBWMXmlClient *c; + + if (MB_WM_THEME (theme)->xml_clients && + (c = mb_wm_xml_client_find_by_type (MB_WM_THEME (theme)->xml_clients, + c_type))) + { + MBWMXmlDecor *d; + + d = mb_wm_xml_decor_find_by_type (c->decors, type); + + if (d) + { + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + construct_buttons (theme, decor, d); + } + + return decor; + } + + switch (c_type) + { + case MBWMClientTypeApp: + switch (type) + { + case MBWMDecorTypeNorth: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + construct_buttons (theme, decor, NULL); + break; + default: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + } + break; + + case MBWMClientTypeDialog: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + break; + + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + decor = mb_wm_decor_new (wm, type); + mb_wm_decor_attach (decor, client); + } + + return decor; +} + +static void +mb_wm_theme_simple_get_button_size (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *width, + int *height) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (width) + *width = b->width; + + if (height) + *height = b->height; + + return; + } + } + + /* + * These are defaults when no theme description was loaded + */ + switch (c_type) + { + case MBWMClientTypeApp: + case MBWMClientTypeDialog: + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + if (width) + *width = SIMPLE_FRAME_TITLEBAR_HEIGHT-4; + + if (height) + *height = SIMPLE_FRAME_TITLEBAR_HEIGHT-4; + } +} + +static void +mb_wm_theme_simple_get_button_position (MBWMTheme *theme, + MBWMDecor *decor, + MBWMDecorButtonType type, + int *x, + int *y) +{ + MBWindowManagerClient * client = decor->parent_client; + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + /* FIXME -- assumes button on the north decor only */ + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type); + + if (b) + { + if (x) + if (b->x >= 0) + *x = b->x; + else + *x = 2; + + if (y) + if (b->y >= 0) + *y = b->y; + else + *y = 2; + + return; + } + } + + if (x) + *x = 2; + + if (y) + *y = 2; +} + +static void +mb_wm_theme_simple_get_decor_dimensions (MBWMTheme *theme, + MBWindowManagerClient *client, + int *north, + int *south, + int *west, + int *east) +{ + MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + MBWMXmlClient * c; + MBWMXmlDecor * d; + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type))) + { + if (north) + if ((d = mb_wm_xml_decor_find_by_type (c->decors,MBWMDecorTypeNorth))) + *north = d->height; + else + *north = SIMPLE_FRAME_TITLEBAR_HEIGHT; + + if (south) + if ((d = mb_wm_xml_decor_find_by_type (c->decors,MBWMDecorTypeSouth))) + *south = d->height; + else + *south = SIMPLE_FRAME_EDGE_SIZE; + + if (west) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeWest))) + *west = d->width; + else + *west = SIMPLE_FRAME_EDGE_SIZE; + + if (east) + if ((d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeEast))) + *east = d->width; + else + *east = SIMPLE_FRAME_EDGE_SIZE; + + return; + } + + /* + * These are defaults when no theme description was loaded + */ + switch (c_type) + { + case MBWMClientTypeDialog: + case MBWMClientTypeApp: + if (north) + *north = SIMPLE_FRAME_TITLEBAR_HEIGHT; + + if (south) + *south = SIMPLE_FRAME_EDGE_SIZE; + + if (west) + *west = SIMPLE_FRAME_EDGE_SIZE; + + if (east) + *east = SIMPLE_FRAME_EDGE_SIZE; + break; + + case MBWMClientTypePanel: + case MBWMClientTypeDesktop: + case MBWMClientTypeInput: + default: + if (north) + *north = 0; + + if (south) + *south = 0; + + if (west) + *west = 0; + + if (east) + *east = 0; + } +} + +static unsigned long +pixel_from_clr (Display * dpy, int screen, MBWMColor * clr) +{ + XColor xcol; + + xcol.red = (int)(clr->r * (double)0xffff); + xcol.green = (int)(clr->g * (double)0xffff); + xcol.blue = (int)(clr->b * (double)0xffff); + xcol.flags = DoRed|DoGreen|DoBlue; + + XAllocColor (dpy, DefaultColormap (dpy, screen), &xcol); + + return xcol.pixel; +} + +static XftFont * +xft_load_font (MBWMDecor * decor, MBWMXmlDecor *d) +{ + char desc[512]; + XftFont * font; + Display * xdpy = decor->parent_client->wmref->xdpy; + int xscreen = decor->parent_client->wmref->xscreen; + int font_size; + + font_size = d && d->font_size ? d->font_size : SIMPLE_FRAME_TITLEBAR_HEIGHT / 2; + + if (!d || d->font_units == MBWMXmlFontUnitsPixels) + { + font_size = mb_wm_util_pixels_to_points (decor->parent_client->wmref, + font_size); + } + + snprintf (desc, sizeof (desc), "%s-%i", + d && d->font_family ? d->font_family : "Sans", + font_size); + + font = XftFontOpenName (xdpy, xscreen, desc); + + return font; +} + +static void +mb_wm_theme_simple_paint_decor (MBWMTheme *theme, MBWMDecor *decor) +{ + MBWMDecorType type; + const MBGeometry *geom; + MBWindowManagerClient *client; + Window xwin; + MBWindowManager *wm = theme->wm; + MBWMColor clr_bg; + MBWMColor clr_fg; + MBWMClientType c_type; + MBWMXmlClient *c = NULL; + MBWMXmlDecor *d = NULL; + struct DecorData *dd; + int x, y, w, h; + GC gc; + Display *xdpy = wm->xdpy; + int xscreen = wm->xscreen; + const char *title; + + clr_fg.r = 1.0; + clr_fg.g = 1.0; + clr_fg.b = 1.0; + + clr_bg.r = 0.5; + clr_bg.g = 0.5; + clr_bg.b = 0.5; + + client = mb_wm_decor_get_parent (decor); + xwin = mb_wm_decor_get_x_window (decor); + + if (client == NULL || xwin == None) + return; + + dd = mb_wm_decor_get_theme_data (decor); + + type = mb_wm_decor_get_type (decor); + geom = mb_wm_decor_get_geometry (decor); + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))) + { + if (d->clr_fg.set) + { + clr_fg.r = d->clr_fg.r; + clr_fg.g = d->clr_fg.g; + clr_fg.b = d->clr_fg.b; + } + + if (d->clr_bg.set) + { + clr_bg.r = d->clr_bg.r; + clr_bg.g = d->clr_bg.g; + clr_bg.b = d->clr_bg.b; + } + } + + if (!dd) + { + XRenderColor rclr; + + dd = malloc (sizeof (struct DecorData)); + dd->xpix = XCreatePixmap(xdpy, xwin, + decor->geom.width, decor->geom.height, + DefaultDepth(xdpy, xscreen)); + + dd->xftdraw = XftDrawCreate (xdpy, dd->xpix, + DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen)); + + rclr.red = (int)(clr_fg.r * (double)0xffff); + rclr.green = (int)(clr_fg.g * (double)0xffff); + rclr.blue = (int)(clr_fg.b * (double)0xffff); + rclr.alpha = 0xffff; + + XftColorAllocValue (xdpy, DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen), + &rclr, &dd->clr); + + dd->font = xft_load_font (decor, d); + + XSetWindowBackgroundPixmap(xdpy, xwin, dd->xpix); + + mb_wm_decor_set_theme_data (decor, dd, decordata_free); + } + + gc = XCreateGC (xdpy, dd->xpix, 0, NULL); + + XSetLineAttributes (xdpy, gc, 1, LineSolid, CapProjecting, JoinMiter); + XSetBackground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_bg)); + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_bg)); + + w = geom->width; h = geom->height; x = geom->x; y = geom->y; + + XFillRectangle (xdpy, dd->xpix, gc, 0, 0, w, h); + + if (mb_wm_decor_get_type(decor) == MBWMDecorTypeNorth && + (title = mb_wm_client_get_name (client))) + { + XRectangle rec; + + int pack_start_x = mb_wm_decor_get_pack_start_x (decor); + int pack_end_x = mb_wm_decor_get_pack_end_x (decor); + int west_width = mb_wm_client_frame_west_width (client); + int y = (decor->geom.height - + (dd->font->ascent + dd->font->descent)) / 2 + + dd->font->ascent; + + rec.x = 0; + rec.y = 0; + rec.width = pack_end_x - 2; + rec.height = d ? d->height : SIMPLE_FRAME_TITLEBAR_HEIGHT; + + XftDrawSetClipRectangles (dd->xftdraw, 0, 0, &rec, 1); + + XftDrawStringUtf8(dd->xftdraw, + &dd->clr, + dd->font, + west_width + pack_start_x + (h / 5), y, + title, strlen (title)); + } + + XFreeGC (xdpy, gc); + + XClearWindow (xdpy, xwin); +} + +static void +mb_wm_theme_simple_paint_button (MBWMTheme *theme, MBWMDecorButton *button) +{ + MBWMDecor *decor; + MBWindowManagerClient *client; + Window xwin; + MBWindowManager *wm = theme->wm; + int x, y, w, h; + MBWMColor clr_bg; + MBWMColor clr_fg; + MBWMClientType c_type; + MBWMXmlClient *c = NULL; + MBWMXmlDecor *d = NULL; + MBWMXmlButton *b = NULL; + struct DecorData * dd; + GC gc; + Display *xdpy = wm->xdpy; + int xscreen = wm->xscreen; + + clr_fg.r = 1.0; + clr_fg.g = 1.0; + clr_fg.b = 1.0; + + clr_bg.r = 0.0; + clr_bg.g = 0.0; + clr_bg.b = 0.0; + + decor = button->decor; + client = mb_wm_decor_get_parent (decor); + xwin = decor->xwin; + dd = mb_wm_decor_get_theme_data (decor); + + if (client == NULL || xwin == None || dd->xpix == None) + return; + + c_type = MB_WM_CLIENT_CLIENT_TYPE (client); + + if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) && + (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) && + (b = mb_wm_xml_button_find_by_type (d->buttons, button->type))) + { + clr_fg.r = b->clr_fg.r; + clr_fg.g = b->clr_fg.g; + clr_fg.b = b->clr_fg.b; + + clr_bg.r = b->clr_bg.r; + clr_bg.g = b->clr_bg.g; + clr_bg.b = b->clr_bg.b; + } + + w = button->geom.width; + h = button->geom.height; + x = button->geom.x; + y = button->geom.y; + + gc = XCreateGC (xdpy, dd->xpix, 0, NULL); + + XSetLineAttributes (xdpy, gc, 1, LineSolid, CapRound, JoinRound); + + + + if (button->state == MBWMDecorButtonStateInactive) + { + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_bg)); + } + else + { + /* FIXME -- think of a better way of doing this */ + MBWMColor clr; + clr.r = clr_bg.r + 0.2; + clr.g = clr_bg.g + 0.2; + clr.b = clr_bg.b + 0.2; + + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr)); + } + + XFillRectangle (xdpy, dd->xpix, gc, x, y, w+1, h+1); + + XSetLineAttributes (xdpy, gc, 3, LineSolid, CapRound, JoinRound); + XSetForeground (xdpy, gc, pixel_from_clr (xdpy, xscreen, &clr_fg)); + + if (button->type == MBWMDecorButtonClose) + { + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + 3, x + w - 3, y + h - 3); + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + h - 3, x + w - 3, y + 3); + } + else if (button->type == MBWMDecorButtonFullscreen) + { + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + 3, x + 3, y + h - 3); + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + h - 3, x + w - 3, y + h - 3); + XDrawLine (xdpy, dd->xpix, gc, x + w - 3, y + h - 3, x + w - 3, y + 3); + XDrawLine (xdpy, dd->xpix, gc, x + w - 3, y + 3, x + 3, y + 3); + } + else if (button->type == MBWMDecorButtonMinimize) + { + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + h - 5, x + w - 3, y + h - 5); + } + else if (button->type == MBWMDecorButtonHelp) + { + char desc[512]; + XftFont *font; + XRenderColor rclr; + XftColor clr; + XRectangle rec; + + snprintf (desc, sizeof (desc), "%s-%i:bold", + d && d->font_family ? d->font_family : "Sans", h*3/4); + + font = XftFontOpenName (xdpy, xscreen, desc); + + rclr.red = (int)(clr_fg.r * (double)0xffff); + rclr.green = (int)(clr_fg.g * (double)0xffff); + rclr.blue = (int)(clr_fg.b * (double)0xffff); + rclr.alpha = 0xffff; + + XftColorAllocValue (xdpy, DefaultVisual (xdpy, xscreen), + DefaultColormap (xdpy, xscreen), + &rclr, &clr); + + rec.x = x; + rec.y = y; + rec.width = w; + rec.height = h; + + XftDrawSetClipRectangles (dd->xftdraw, 0, 0, &rec, 1); + + XftDrawStringUtf8 (dd->xftdraw, &clr, font, + x + 4, + y + (h - (font->ascent + font->descent))/2 + + font->ascent, + "?", 1); + + XftFontClose (xdpy, font); + } + else if (button->type == MBWMDecorButtonMenu) + { + XSetLineAttributes (xdpy, gc, 3, LineSolid, CapRound, JoinMiter); + XDrawLine (xdpy, dd->xpix, gc, x + 3, y + 5, x + w/2, y + h - 5); + XDrawLine (xdpy, dd->xpix, gc, x + w/2, y + h - 5, x + w - 3, y + 5); + } + else if (button->type == MBWMDecorButtonAccept) + { + XDrawArc (xdpy, dd->xpix, gc, x + 4, y + 4, w - 8, h - 8, 0, 64 * 360); + } + + XFreeGC (xdpy, gc); + + XClearWindow (wm->xdpy, xwin); +} + +/* + * Installs a global handler that can be used to translate custom client type + * names to their numerical values. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_client_type_func (MBWMThemeCustomClientTypeFunc func, + void *user_data) +{ + custom_client_type_func = func; + custom_client_type_func_data = user_data; +} + +/* + * Installs a global handler that can be used to translate custom theme names + * to their numerical (MBWMObject) values. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_theme_type_func (MBWMThemeCustomThemeTypeFunc func, + void *user_data) +{ + custom_theme_type_func = func; + custom_theme_type_func_data = user_data; +} + +/* + * Installs a global handler that can be used to translate custom button names + * to their numerical values. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_button_type_func (MBWMThemeCustomButtonTypeFunc func, + void *user_data) +{ + custom_button_type_func = func; + custom_button_type_func_data = user_data; +} + +/* + * Installs a global handler that can be used to allocate a custom + * MBWMThemeSubclass. + * + * NB: this is not an object function, since we need it before we allocate the + * actual MBWMTheme object in the XML parser. + */ +void +mb_wm_theme_set_custom_theme_alloc_func (MBWMThemeCustomThemeAllocFunc func) +{ + custom_theme_alloc_func = func; +} |