diff --git a/eek/Makefile.am b/eek/Makefile.am index 31dee266..7c940e17 100644 --- a/eek/Makefile.am +++ b/eek/Makefile.am @@ -112,8 +112,11 @@ endif libeek_gtk_public_headers = \ $(srcdir)/eek-gtk-keyboard.h \ $(srcdir)/eek-gtk.h +libeek_gtk_private_headers = \ + $(srcdir)/eek-gtk-renderer.h libeek_gtk_sources = \ - $(srcdir)/eek-gtk-keyboard.c + $(srcdir)/eek-gtk-keyboard.c \ + $(srcdir)/eek-gtk-renderer.c libeek_gtk_la_SOURCES = $(libeek_gtk_sources) libeek_gtk_la_CFLAGS = $(GTK_CFLAGS) diff --git a/eek/eek-gtk-keyboard.c b/eek/eek-gtk-keyboard.c index 78c019f8..0b718232 100644 --- a/eek/eek-gtk-keyboard.c +++ b/eek/eek-gtk-keyboard.c @@ -29,7 +29,7 @@ #endif /* HAVE_CONFIG_H */ #include "eek-gtk-keyboard.h" -#include "eek-renderer.h" +#include "eek-gtk-renderer.h" #include "eek-keyboard.h" #include "eek-section.h" #include "eek-key.h" @@ -105,7 +105,7 @@ eek_gtk_keyboard_real_draw (GtkWidget *self, PangoContext *pcontext; pcontext = gtk_widget_get_pango_context (self); - priv->renderer = eek_renderer_new (priv->keyboard, pcontext); + priv->renderer = eek_gtk_renderer_new (priv->keyboard, pcontext, self); eek_renderer_set_allocation_size (priv->renderer, allocation.width, diff --git a/eek/eek-gtk-renderer.c b/eek/eek-gtk-renderer.c new file mode 100644 index 00000000..75531236 --- /dev/null +++ b/eek/eek-gtk-renderer.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2011 Daiki Ueno + * Copyright (C) 2011 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include "eek-gtk-renderer.h" +#include "eek-key.h" + +G_DEFINE_TYPE (EekGtkRenderer, eek_gtk_renderer, EEK_TYPE_RENDERER); + +static cairo_surface_t * +pixbuf_to_cairo_surface (GdkPixbuf *pixbuf) +{ + cairo_surface_t *dummy_surface; + cairo_pattern_t *pattern; + cairo_surface_t *surface; + cairo_t *cr; + + dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + + cr = cairo_create (dummy_surface); + gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); + pattern = cairo_get_source (cr); + cairo_pattern_get_surface (pattern, &surface); + cairo_surface_reference (surface); + cairo_destroy (cr); + cairo_surface_destroy (dummy_surface); + + return surface; +} + +static void +eek_gtk_renderer_real_render_key_icon (EekRenderer *self, + cairo_t *cr, + EekKey *key, + gdouble scale, + gboolean rotate) +{ + EekBounds bounds; + EekSymbol *symbol; + const gchar *icon_name; + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + GError *error; + gint width, height; + + symbol = eek_key_get_symbol_with_fallback (key, 0, 0); + g_return_if_fail (symbol); + + icon_name = eek_symbol_get_icon_name (symbol); + g_return_if_fail (icon_name); + + eek_element_get_bounds (EEK_ELEMENT(key), &bounds); + + error = NULL; + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + icon_name, + MIN(bounds.width, bounds.height) * scale, + 0, + &error); + g_return_if_fail (pixbuf); + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + if (bounds.width * scale * height > bounds.height * scale * width) + scale *= bounds.width * scale / width; + else + scale *= bounds.height * scale / height; + + cairo_save (cr); + eek_renderer_apply_transformation_for_key (self, cr, key, scale, rotate); + surface = pixbuf_to_cairo_surface (pixbuf); + cairo_set_source_surface (cr, surface, 0.0, 0.0); + cairo_paint (cr); + cairo_restore (cr); +} + +static void +eek_gtk_renderer_class_init (EekGtkRendererClass *klass) +{ + EekRendererClass *renderer_class = EEK_RENDERER_CLASS (klass); + + renderer_class->render_key_icon = eek_gtk_renderer_real_render_key_icon; +} + +static void +eek_gtk_renderer_init (EekGtkRenderer *self) +{ +} + +EekRenderer * +eek_gtk_renderer_new (EekKeyboard *keyboard, + PangoContext *pcontext, + GtkWidget *widget) +{ + EekRenderer *renderer; + + renderer = g_object_new (EEK_TYPE_GTK_RENDERER, + "keyboard", keyboard, + "pango-context", pcontext, + NULL); + + return renderer; +} diff --git a/eek/eek-gtk-renderer.h b/eek/eek-gtk-renderer.h new file mode 100644 index 00000000..5948477a --- /dev/null +++ b/eek/eek-gtk-renderer.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2011 Daiki Ueno + * Copyright (C) 2010-2011 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef EEK_GTK_RENDERER_H +#define EEK_GTK_RENDERER_H 1 + +#include +#include "eek-renderer.h" + +G_BEGIN_DECLS + +#define EEK_TYPE_GTK_RENDERER (eek_gtk_renderer_get_type()) +#define EEK_GTK_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEK_TYPE_GTK_RENDERER, EekGtkRenderer)) +#define EEK_GTK_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_GTK_RENDERER, EekGtkRendererClass)) +#define EEK_IS_GTK_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEK_TYPE_GTK_RENDERER)) +#define EEK_IS_GTK_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_GTK_RENDERER)) +#define EEK_GTK_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_GTK_RENDERER, EekGtkRendererClass)) + +typedef struct _EekGtkRenderer EekGtkRenderer; +typedef struct _EekGtkRendererClass EekGtkRendererClass; +typedef struct _EekGtkRendererPrivate EekGtkRendererPrivate; + +struct _EekGtkRenderer { + EekRenderer parent; + + EekGtkRendererPrivate *priv; +}; + +struct _EekGtkRendererClass +{ + EekRendererClass parent_class; + + /*< private >*/ + /* padding */ + gpointer pdummy[24]; +}; + +GType eek_gtk_renderer_get_type (void) G_GNUC_CONST; +EekRenderer *eek_gtk_renderer_new (EekKeyboard *keyboard, + PangoContext *pcontext, + GtkWidget *widget); + +G_END_DECLS +#endif /* EEK_GTK_RENDERER_H */ diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index e8bb601e..9a9f3a98 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -350,9 +350,8 @@ render_key (EekRenderer *self, EekOutline *outline; cairo_surface_t *outline_surface; EekBounds bounds; - PangoLayout *layout; - PangoRectangle extents = { 0, }; gulong oref; + EekSymbol *symbol; eek_element_get_bounds (EEK_ELEMENT(key), &bounds); oref = eek_key_get_oref (key); @@ -381,7 +380,7 @@ render_key (EekRenderer *self, bounds.height * priv->scale); cairo_fill (cr); - render_key_outline (self, cr, key); + eek_renderer_render_key_outline (self, cr, key, 1.0, 0); g_hash_table_insert (priv->outline_surface_cache, outline, @@ -391,31 +390,41 @@ render_key (EekRenderer *self, cairo_set_source_surface (cr, outline_surface, 0.0, 0.0); cairo_paint (cr); - layout = pango_cairo_create_layout (cr); - eek_renderer_real_render_key_label (self, layout, key); - pango_layout_get_extents (layout, NULL, &extents); + symbol = eek_key_get_symbol_with_fallback (key, 0, 0); + if (EEK_RENDERER_GET_CLASS(self)->render_key_icon && + symbol && eek_symbol_get_icon_name (symbol)) { + eek_renderer_render_key_icon (self, cr, key, 1.0, 0); + } else { + PangoLayout *layout; + PangoRectangle extents = { 0, }; - cairo_save (cr); - cairo_move_to - (cr, - (bounds.width * priv->scale - extents.width / PANGO_SCALE) / 2, - (bounds.height * priv->scale - extents.height / PANGO_SCALE) / 2); - cairo_set_source_rgba (cr, - priv->foreground->red, - priv->foreground->green, - priv->foreground->blue, - priv->foreground->alpha); - pango_cairo_show_layout (cr, layout); - cairo_restore (cr); - g_object_unref (layout); + layout = pango_cairo_create_layout (cr); + eek_renderer_render_key_label (self, layout, key); + pango_layout_get_extents (layout, NULL, &extents); + + cairo_save (cr); + cairo_move_to + (cr, + (bounds.width * priv->scale - extents.width / PANGO_SCALE) / 2, + (bounds.height * priv->scale - extents.height / PANGO_SCALE) / 2); + cairo_set_source_rgba (cr, + priv->foreground->red, + priv->foreground->green, + priv->foreground->blue, + priv->foreground->alpha); + + pango_cairo_show_layout (cr, layout); + cairo_restore (cr); + g_object_unref (layout); + } } -static void -prepare_render_key (EekRenderer *self, - cairo_t *cr, - EekKey *key, - gdouble scale, - gboolean rotate) +void +eek_renderer_apply_transformation_for_key (EekRenderer *self, + cairo_t *cr, + EekKey *key, + gdouble scale, + gboolean rotate) { EekElement *section; EekBounds bounds, rotated_bounds; @@ -500,7 +509,7 @@ eek_renderer_real_render_key_outline (EekRenderer *self, gboolean rotate) { cairo_save (cr); - prepare_render_key (self, cr, key, scale, rotate); + eek_renderer_apply_transformation_for_key (self, cr, key, scale, rotate); render_key_outline (self, cr, key); cairo_restore (cr); } @@ -513,7 +522,7 @@ eek_renderer_real_render_key (EekRenderer *self, gboolean rotate) { cairo_save (cr); - prepare_render_key (self, cr, key, scale, rotate); + eek_renderer_apply_transformation_for_key (self, cr, key, scale, rotate); render_key (self, cr, key); cairo_restore (cr); } @@ -857,6 +866,24 @@ eek_renderer_render_key_outline (EekRenderer *renderer, rotate); } +void +eek_renderer_render_key_icon (EekRenderer *renderer, + cairo_t *cr, + EekKey *key, + gdouble scale, + gboolean rotate) +{ + g_return_if_fail (EEK_IS_RENDERER(renderer)); + g_return_if_fail (EEK_IS_KEY(key)); + g_return_if_fail (scale >= 0.0); + + EEK_RENDERER_GET_CLASS(renderer)->render_key_icon (renderer, + cr, + key, + scale, + rotate); +} + void eek_renderer_render_key (EekRenderer *renderer, cairo_t *cr, diff --git a/eek/eek-renderer.h b/eek/eek-renderer.h index 687c9564..7969c577 100644 --- a/eek/eek-renderer.h +++ b/eek/eek-renderer.h @@ -68,9 +68,15 @@ struct _EekRendererClass void (* render_keyboard) (EekRenderer *self, cairo_t *cr); + void (* render_key_icon) (EekRenderer *self, + cairo_t *cr, + EekKey *key, + gdouble scale, + gboolean rotate); + /*< private >*/ /* padding */ - gpointer pdummy[24]; + gpointer pdummy[23]; }; GType eek_renderer_get_type (void) G_GNUC_CONST; @@ -106,6 +112,12 @@ void eek_renderer_render_key (EekRenderer *renderer, gdouble scale, gboolean rotate); +void eek_renderer_render_key_icon (EekRenderer *renderer, + cairo_t *cr, + EekKey *key, + gdouble scale, + gboolean rotate); + void eek_renderer_render_keyboard (EekRenderer *renderer, cairo_t *cr); @@ -122,6 +134,12 @@ void eek_renderer_set_border_width (EekRenderer *renderer, EekKey *eek_renderer_find_key_by_position (EekRenderer *renderer, gdouble x, gdouble y); +void eek_renderer_apply_transformation_for_key + (EekRenderer *renderer, + cairo_t *cr, + EekKey *key, + gdouble scale, + gboolean rotate); G_END_DECLS #endif /* EEK_RENDERER_H */ diff --git a/eek/eek-symbol.c b/eek/eek-symbol.c index 3cdcf89f..0798a811 100644 --- a/eek/eek-symbol.c +++ b/eek/eek-symbol.c @@ -38,6 +38,7 @@ enum { PROP_LABEL, PROP_CATEGORY, PROP_MODIFIER_MASK, + PROP_ICON_NAME, PROP_LAST }; @@ -46,6 +47,7 @@ struct _EekSymbolPrivate { gchar *label; EekSymbolCategory category; EekModifierType modifier_mask; + gchar *icon_name; }; static void eek_serializable_iface_init (EekSerializableIface *iface); @@ -67,6 +69,7 @@ eek_symbol_real_serialize (EekSerializable *self, g_variant_builder_add (builder, "s", priv->label); g_variant_builder_add (builder, "u", priv->category); g_variant_builder_add (builder, "u", priv->modifier_mask); + g_variant_builder_add (builder, "s", priv->icon_name); } static gsize @@ -80,6 +83,7 @@ eek_symbol_real_deserialize (EekSerializable *self, g_variant_get_child (variant, index++, "s", &priv->label); g_variant_get_child (variant, index++, "u", &priv->category); g_variant_get_child (variant, index++, "u", &priv->modifier_mask); + g_variant_get_child (variant, index++, "s", &priv->icon_name); return index; } @@ -111,6 +115,10 @@ eek_symbol_set_property (GObject *object, eek_symbol_set_modifier_mask (EEK_SYMBOL(object), g_value_get_uint (value)); break; + case PROP_ICON_NAME: + eek_symbol_set_icon_name (EEK_SYMBOL(object), + g_value_get_string (value)); + break; default: g_object_set_property (object, g_param_spec_get_name (pspec), @@ -139,6 +147,10 @@ eek_symbol_get_property (GObject *object, g_value_set_uint (value, eek_symbol_get_modifier_mask (EEK_SYMBOL(object))); break; + case PROP_ICON_NAME: + g_value_set_string (value, + eek_symbol_get_icon_name (EEK_SYMBOL(object))); + break; default: g_object_get_property (object, g_param_spec_get_name (pspec), @@ -153,6 +165,8 @@ eek_symbol_finalize (GObject *object) EekSymbolPrivate *priv = EEK_SYMBOL_GET_PRIVATE(object); g_free (priv->name); + g_free (priv->label); + g_free (priv->icon_name); G_OBJECT_CLASS (eek_symbol_parent_class)->finalize (object); } @@ -195,6 +209,13 @@ eek_symbol_class_init (EekSymbolClass *klass) 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT | G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_MODIFIER_MASK, pspec); + + pspec = g_param_spec_string ("icon-name", + "Icon name", + "Icon name used to render the symbol", + NULL, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ICON_NAME, pspec); } static void @@ -205,6 +226,7 @@ eek_symbol_init (EekSymbol *self) priv = self->priv = EEK_SYMBOL_GET_PRIVATE(self); priv->name = NULL; priv->label = NULL; + priv->icon_name = NULL; priv->category = EEK_SYMBOL_CATEGORY_UNKNOWN; priv->modifier_mask = 0; } @@ -347,3 +369,26 @@ eek_symbol_is_modifier (EekSymbol *symbol) return eek_symbol_get_modifier_mask (symbol) != 0; } +void +eek_symbol_set_icon_name (EekSymbol *symbol, + const gchar *icon_name) +{ + EekSymbolPrivate *priv; + + g_return_if_fail (EEK_IS_SYMBOL(symbol)); + + priv = EEK_SYMBOL_GET_PRIVATE(symbol); + g_free (priv->icon_name); + priv->icon_name = g_strdup (icon_name); +} + +G_CONST_RETURN gchar * +eek_symbol_get_icon_name (EekSymbol *symbol) +{ + EekSymbolPrivate *priv; + + g_return_val_if_fail (EEK_IS_SYMBOL(symbol), NULL); + + priv = EEK_SYMBOL_GET_PRIVATE(symbol); + return priv->icon_name; +} diff --git a/eek/eek-symbol.h b/eek/eek-symbol.h index e570f561..24fd8f1b 100644 --- a/eek/eek-symbol.h +++ b/eek/eek-symbol.h @@ -85,22 +85,27 @@ struct _EekSymbolClass { GObjectClass parent_class; }; -GType eek_symbol_get_type (void) G_GNUC_CONST; +GType eek_symbol_get_type (void) G_GNUC_CONST; -EekSymbol *eek_symbol_new (const gchar *name); -void eek_symbol_set_name (EekSymbol *symbol, - const gchar *name); -G_CONST_RETURN gchar *eek_symbol_get_name (EekSymbol *symbol); -void eek_symbol_set_label (EekSymbol *symbol, - const gchar *label); -gchar *eek_symbol_get_label (EekSymbol *symbol); -void eek_symbol_set_category (EekSymbol *symbol, - EekSymbolCategory category); -EekSymbolCategory eek_symbol_get_category (EekSymbol *symbol); -EekModifierType eek_symbol_get_modifier_mask (EekSymbol *keysym); -void eek_symbol_set_modifier_mask (EekSymbol *keysym, - EekModifierType mask); -gboolean eek_symbol_is_modifier (EekSymbol *symbol); +EekSymbol *eek_symbol_new (const gchar *name); +void eek_symbol_set_name (EekSymbol *symbol, + const gchar *name); +G_CONST_RETURN gchar *eek_symbol_get_name (EekSymbol *symbol); +void eek_symbol_set_label (EekSymbol *symbol, + const gchar *label); +gchar *eek_symbol_get_label (EekSymbol *symbol); +void eek_symbol_set_category (EekSymbol *symbol, + EekSymbolCategory category); +EekSymbolCategory eek_symbol_get_category (EekSymbol *symbol); +EekModifierType eek_symbol_get_modifier_mask + (EekSymbol *keysym); +void eek_symbol_set_modifier_mask + (EekSymbol *keysym, + EekModifierType mask); +gboolean eek_symbol_is_modifier (EekSymbol *symbol); +void eek_symbol_set_icon_name (EekSymbol *symbol, + const gchar *icon_name); +G_CONST_RETURN gchar *eek_symbol_get_icon_name (EekSymbol *symbol); G_END_DECLS diff --git a/eek/eek-xml-layout.c b/eek/eek-xml-layout.c index 59bd92df..b29dee03 100644 --- a/eek/eek-xml-layout.c +++ b/eek/eek-xml-layout.c @@ -65,6 +65,7 @@ struct _ParseCallbackData { GSList *points; GSList *symbols; gchar *label; + gchar *icon; guint keyval; gint groups, levels; EekOutline outline; @@ -161,7 +162,7 @@ start_element_callback (GMarkupParseContext *pcontext, const gchar **values = attribute_values; gint column = -1, row = -1, groups = -1, levels = -1; guint keyval = EEK_INVALID_KEYSYM; - gchar *name = NULL, *label = NULL, *id = NULL, *version = NULL; + gchar *name = NULL, *label = NULL, *icon = NULL, *id = NULL, *version = NULL; validate (element_name, data->element_stack, error); if (error && *error) @@ -178,6 +179,8 @@ start_element_callback (GMarkupParseContext *pcontext, name = g_strdup (*values); else if (g_strcmp0 (*names, "label") == 0) label = g_strdup (*values); + else if (g_strcmp0 (*names, "icon") == 0) + icon = g_strdup (*values); else if (g_strcmp0 (*names, "keyval") == 0) keyval = strtoul (*values, NULL, 10); else if (g_strcmp0 (*names, "version") == 0) @@ -225,6 +228,7 @@ start_element_callback (GMarkupParseContext *pcontext, if (g_strcmp0 (element_name, "keysym") == 0) { data->label = g_strdup (label); + data->icon = g_strdup (icon); data->keyval = keyval; } @@ -235,6 +239,7 @@ start_element_callback (GMarkupParseContext *pcontext, out: g_free (name); g_free (label); + g_free (icon); g_free (id); g_free (version); @@ -406,6 +411,10 @@ end_element_callback (GMarkupParseContext *pcontext, eek_symbol_set_label (EEK_SYMBOL(keysym), data->label); g_free (data->label); } + if (data->icon) { + eek_symbol_set_icon_name (EEK_SYMBOL(keysym), data->icon); + g_free (data->icon); + } data->symbols = g_slist_prepend (data->symbols, keysym); goto out; }