diff options
author | Ted Gould <ted@gould.cx> | 2010-06-24 09:21:02 -0500 |
---|---|---|
committer | Ted Gould <ted@gould.cx> | 2010-06-24 09:21:02 -0500 |
commit | 1a54d0070d99cca1322995bb74ab6027ee1dc434 (patch) | |
tree | fabe24cc1446812e9a8691098f634d89f889a6c5 /libdbusmenu-gtk | |
parent | 849f700ec70b1b92cb49d736e2b3148de20053ed (diff) | |
parent | da2663f744d60ab4ffcc0a798448a48266532d3d (diff) | |
download | libdbusmenu-1a54d0070d99cca1322995bb74ab6027ee1dc434.tar.gz libdbusmenu-1a54d0070d99cca1322995bb74ab6027ee1dc434.tar.bz2 libdbusmenu-1a54d0070d99cca1322995bb74ab6027ee1dc434.zip |
Import upstream version 0.3.3
Diffstat (limited to 'libdbusmenu-gtk')
-rw-r--r-- | libdbusmenu-gtk/client.c | 181 | ||||
-rw-r--r-- | libdbusmenu-gtk/client.h | 3 | ||||
-rw-r--r-- | libdbusmenu-gtk/genericmenuitem.c | 5 | ||||
-rw-r--r-- | libdbusmenu-gtk/menuitem.c | 247 | ||||
-rw-r--r-- | libdbusmenu-gtk/menuitem.h | 7 |
5 files changed, 442 insertions, 1 deletions
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c index 3bd0af6..c73c90f 100644 --- a/libdbusmenu-gtk/client.c +++ b/libdbusmenu-gtk/client.c @@ -36,6 +36,15 @@ License version 3 and version 2.1 along with this program. If not, see #include "menuitem.h" #include "genericmenuitem.h" +/* Private */ +typedef struct _DbusmenuGtkClientPrivate DbusmenuGtkClientPrivate; +struct _DbusmenuGtkClientPrivate { + GtkAccelGroup * agroup; +}; + +#define DBUSMENU_GTKCLIENT_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_GTKCLIENT_TYPE, DbusmenuGtkClientPrivate)) + /* Prototypes */ static void dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass); static void dbusmenu_gtkclient_init (DbusmenuGtkClient *self); @@ -62,6 +71,8 @@ dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + g_type_class_add_private (klass, sizeof (DbusmenuGtkClientPrivate)); + object_class->dispose = dbusmenu_gtkclient_dispose; object_class->finalize = dbusmenu_gtkclient_finalize; @@ -73,6 +84,10 @@ dbusmenu_gtkclient_class_init (DbusmenuGtkClientClass *klass) static void dbusmenu_gtkclient_init (DbusmenuGtkClient *self) { + DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(self); + + priv->agroup = NULL; + dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_DEFAULT, new_item_normal); dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_SEPARATOR, new_item_seperator); @@ -85,6 +100,12 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self) static void dbusmenu_gtkclient_dispose (GObject *object) { + DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(object); + + if (priv->agroup != NULL) { + g_object_unref(priv->agroup); + priv->agroup = NULL; + } G_OBJECT_CLASS (dbusmenu_gtkclient_parent_class)->dispose (object); return; @@ -99,6 +120,153 @@ dbusmenu_gtkclient_finalize (GObject *object) return; } +/* Structure for passing data to swap_agroup */ +typedef struct _swap_agroup_t swap_agroup_t; +struct _swap_agroup_t { + DbusmenuGtkClient * client; + GtkAccelGroup * old_agroup; + GtkAccelGroup * new_agroup; +}; + +/* Looks at the old version of the accelerator group and + the new one and makes the state proper. */ +static gboolean +do_swap_agroup (DbusmenuMenuitem * mi, gpointer userdata) { + swap_agroup_t * data = (swap_agroup_t *)userdata; + + /* If we don't have a shortcut we don't care */ + if (!dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_SHORTCUT)) { + return FALSE; + } + + guint key = 0; + GdkModifierType modifiers = 0; + + dbusmenu_menuitem_property_get_shortcut(mi, &key, &modifiers); + + if (key == 0) { + return FALSE; + } + + g_debug("Setting shortcut on '%s': %d %X", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL), key, modifiers); + + GtkMenuItem * gmi = dbusmenu_gtkclient_menuitem_get(data->client, mi); + if (gmi == NULL) { + return FALSE; + } + + const gchar * accel_path = gtk_menu_item_get_accel_path(gmi); + + if (accel_path != NULL) { + gtk_accel_map_change_entry(accel_path, key, modifiers, TRUE /* replace */); + } else { + gchar * accel_path = g_strdup_printf("<Appmenus>/Generated/%X/%d", GPOINTER_TO_UINT(data->client), dbusmenu_menuitem_get_id(mi)); + + gtk_accel_map_add_entry(accel_path, key, modifiers); + gtk_widget_set_accel_path(GTK_WIDGET(gmi), accel_path, data->new_agroup); + g_free(accel_path); + } + + GtkMenu * submenu = dbusmenu_gtkclient_menuitem_get_submenu(data->client, mi); + if (submenu != NULL) { + gtk_menu_set_accel_group(submenu, data->new_agroup); + } + + return TRUE; +} + +static void +swap_agroup (DbusmenuMenuitem *mi, gpointer userdata) { + do_swap_agroup (mi, userdata); + + return; /* See what I did here, Ted? :) */ +} + +/* Refresh the shortcut for an entry */ +static void +refresh_shortcut (DbusmenuGtkClient * client, DbusmenuMenuitem * mi) +{ + g_return_if_fail(DBUSMENU_IS_GTKCLIENT(client)); + g_return_if_fail(DBUSMENU_IS_MENUITEM(mi)); + + DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(client); + + swap_agroup_t data; + data.client = client; + data.old_agroup = priv->agroup; + data.new_agroup = priv->agroup; + + if (do_swap_agroup(mi, &data)) { + guint key = 0; + GdkModifierType mod = 0; + GtkMenuItem *gmi = dbusmenu_gtkclient_menuitem_get (client, mi); + + dbusmenu_menuitem_property_get_shortcut (mi, &key, &mod); + + if (key != 0) { + gtk_widget_add_accelerator (GTK_WIDGET (gmi), "activate", priv->agroup, key, mod, GTK_ACCEL_VISIBLE); + } + } + + return; +} + + +/** + dbusmenu_gtkclient_set_accel_group: + @client: To set the group on + @agroup: The new acceleration group + + Sets the acceleration group for the menu items with accelerators + on this client. +*/ +void +dbusmenu_gtkclient_set_accel_group (DbusmenuGtkClient * client, GtkAccelGroup * agroup) +{ + g_return_if_fail(DBUSMENU_IS_GTKCLIENT(client)); + g_return_if_fail(GTK_IS_ACCEL_GROUP(agroup)); + + DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(client); + + DbusmenuMenuitem * root = dbusmenu_client_get_root(DBUSMENU_CLIENT(client)); + if (root != NULL) { + swap_agroup_t data; + data.client = client; + data.old_agroup = priv->agroup; + data.new_agroup = agroup; + + dbusmenu_menuitem_foreach(root, swap_agroup, &data); + } + + if (priv->agroup != NULL) { + g_object_unref(priv->agroup); + priv->agroup = NULL; + } + + priv->agroup = agroup; + + return; +} + +/** + dbusmenu_gtkclient_get_accel_group: + @client: Client to query for an accelerator group + + Gets the accel group for this client. + + Return value: Either a valid group or #NULL on error or + none set. +*/ +GtkAccelGroup * +dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client) +{ + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), NULL); + + DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(client); + + return priv->agroup; +} + /* Internal Functions */ static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem"; @@ -225,6 +393,17 @@ menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkMen return; } +/* Special handler for the shortcut changing as we need to have the + client for that one to get the accel group. */ +static void +menu_shortcut_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, DbusmenuGtkClient * client) +{ + if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_SHORTCUT)) { + refresh_shortcut(client, mi); + } + return; +} + /* Call back that happens when the DbusmenuMenuitem is destroyed. We're making sure to clean up everything else down the pipe. */ @@ -291,6 +470,7 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * /* DbusmenuMenuitem signals */ g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi); + g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_shortcut_change_cb), client); g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(delete_child), client); g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(move_child), client); @@ -305,6 +485,7 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * process_sensitive(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_ENABLED)); process_toggle_type(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE)); process_toggle_state(item, gmi, dbusmenu_menuitem_property_get_value(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE)); + refresh_shortcut(client, item); /* Oh, we're a child, let's deal with that */ if (parent != NULL) { diff --git a/libdbusmenu-gtk/client.h b/libdbusmenu-gtk/client.h index 7672bf7..a7c96ee 100644 --- a/libdbusmenu-gtk/client.h +++ b/libdbusmenu-gtk/client.h @@ -79,6 +79,9 @@ DbusmenuGtkClient * dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_obje GtkMenuItem * dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem * item); GtkMenu * dbusmenu_gtkclient_menuitem_get_submenu (DbusmenuGtkClient * client, DbusmenuMenuitem * item); +void dbusmenu_gtkclient_set_accel_group (DbusmenuGtkClient * client, GtkAccelGroup * agroup); +GtkAccelGroup * dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client); + void dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * item, GtkMenuItem * gmi, DbusmenuMenuitem * parent); /** diff --git a/libdbusmenu-gtk/genericmenuitem.c b/libdbusmenu-gtk/genericmenuitem.c index 8f40d93..30b072f 100644 --- a/libdbusmenu-gtk/genericmenuitem.c +++ b/libdbusmenu-gtk/genericmenuitem.c @@ -158,6 +158,8 @@ get_hpadding (GtkWidget * widget) static void set_label (GtkMenuItem * menu_item, const gchar * label) { + if (label == NULL) return; + GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item)); GtkLabel * labelw = NULL; gboolean suppress_update = FALSE; @@ -191,9 +193,10 @@ set_label (GtkMenuItem * menu_item, const gchar * label) update the one that we already have. */ if (labelw == NULL) { /* Build it */ - labelw = GTK_LABEL(gtk_label_new(label)); + labelw = GTK_LABEL(gtk_accel_label_new(label)); gtk_label_set_use_underline(GTK_LABEL(labelw), TRUE); gtk_misc_set_alignment(GTK_MISC(labelw), 0.0, 0.5); + gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(labelw), GTK_WIDGET(menu_item)); gtk_widget_show(GTK_WIDGET(labelw)); /* Check to see if it needs to be in the bin for this diff --git a/libdbusmenu-gtk/menuitem.c b/libdbusmenu-gtk/menuitem.c index 23ff311..9924546 100644 --- a/libdbusmenu-gtk/menuitem.c +++ b/libdbusmenu-gtk/menuitem.c @@ -27,6 +27,9 @@ License version 3 and version 2.1 along with this program. If not, see */ #include "menuitem.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> +#include <dbus/dbus-gtype-specialized.h> /** dbusmenu_menuitem_property_set_image: @@ -128,3 +131,247 @@ dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar * return icon; } +/** + dbusmenu_menuitem_property_set_shortcut_string: + @menuitem: The #DbusmenuMenuitem to set the shortcut on + @shortcut: String describing the shortcut + + This function takes a GTK shortcut string as defined in + #gtk_accelerator_parse and turns that into the information + required to send it over DBusmenu. + + Return value: Whether it was successful at setting the property. +*/ +gboolean +dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, const gchar * shortcut) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), FALSE); + g_return_val_if_fail(shortcut != NULL, FALSE); + + guint key = 0; + GdkModifierType modifier = 0; + + gtk_accelerator_parse(shortcut, &key, &modifier); + + if (key == 0) { + g_warning("Unable to parse shortcut string '%s'", shortcut); + return FALSE; + } + + return dbusmenu_menuitem_property_set_shortcut(menuitem, key, modifier); +} + +/** + dbusmenu_menuitem_property_set_shortcut: + @menuitem: The #DbusmenuMenuitem to set the shortcut on + @key: The keycode of the key to send + @modifier: A bitmask of modifiers used to activate the item + + Takes the modifer described by @key and @modifier and places that into + the format sending across Dbus for shortcuts. + + Return value: Whether it was successful at setting the property. +*/ +gboolean +dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), FALSE); + g_return_val_if_fail(gtk_accelerator_valid(key, modifier), FALSE); + + GArray * array = g_array_sized_new(TRUE, TRUE, sizeof(gchar *), 4); /* Four seems like the max we'd need, plus it's still small */ + + const gchar * control_val = DBUSMENU_MENUITEM_SHORTCUT_CONTROL; + const gchar * alt_val = DBUSMENU_MENUITEM_SHORTCUT_ALT; + const gchar * shift_val = DBUSMENU_MENUITEM_SHORTCUT_SHIFT; + const gchar * super_val = DBUSMENU_MENUITEM_SHORTCUT_SUPER; + + if (modifier & GDK_CONTROL_MASK) { + g_array_append_val(array, control_val); + } + if (modifier & GDK_MOD1_MASK) { + g_array_append_val(array, alt_val); + } + if (modifier & GDK_SHIFT_MASK) { + g_array_append_val(array, shift_val); + } + if (modifier & GDK_SUPER_MASK) { + g_array_append_val(array, super_val); + } + + const gchar * keyname = gdk_keyval_name(key); + g_array_append_val(array, keyname); + + GType type = dbus_g_type_get_collection("GPtrArray", G_TYPE_STRV); + GPtrArray * wrapper = (GPtrArray *)dbus_g_type_specialized_construct(type); + + GValue value = {0,}; + g_value_init(&value, type); + g_value_take_boxed(&value, wrapper); + + DBusGTypeSpecializedAppendContext ctx; + dbus_g_type_specialized_init_append(&value, &ctx); + + GValue strval = {0,}; + g_value_init(&strval, G_TYPE_STRV); + g_value_take_boxed(&strval, array->data); + g_array_free(array, FALSE); + + dbus_g_type_specialized_collection_append(&ctx, &strval); + dbus_g_type_specialized_collection_end_append(&ctx); + + dbusmenu_menuitem_property_set_value(menuitem, DBUSMENU_MENUITEM_PROP_SHORTCUT, &value); + + return TRUE; +} + +/* Look at the closures in an accel group and find + the one that matches the one we've been passed */ +static gboolean +find_closure (GtkAccelKey * key, GClosure * closure, gpointer user_data) +{ + return closure == user_data; +} + +/** + dbusmenu_menuitem_property_set_shortcut_menuitem: + @menuitem: The #DbusmenuMenuitem to set the shortcut on + @gmi: A menu item to steal the shortcut off of + + Takes the shortcut that is installed on a menu item and calls + #dbusmenu_menuitem_property_set_shortcut with it. It also sets + up listeners to watch it change. + + Return value: Whether it was successful at setting the property. +*/ +gboolean +dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, const GtkMenuItem * gmi) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), FALSE); + g_return_val_if_fail(GTK_IS_MENU_ITEM(gmi), FALSE); + + GClosure * closure = NULL; + GtkWidget *label = GTK_BIN (gmi)->child; + + if (GTK_IS_ACCEL_LABEL (label)) + { + g_object_get (label, + "accel-closure", &closure, + NULL); + } + + if (closure == NULL) + return FALSE; + + GtkAccelGroup * group = gtk_accel_group_from_accel_closure(closure); + + /* Seriously, if this returns NULL something is seriously + wrong in GTK. */ + g_return_val_if_fail(group != NULL, FALSE); + + GtkAccelKey * key = gtk_accel_group_find(group, find_closure, closure); + /* Again, not much we can do except complain loudly. */ + g_return_val_if_fail(key != NULL, FALSE); + + if (!gtk_accelerator_valid (key->accel_key, key->accel_mods)) + return FALSE; + + return dbusmenu_menuitem_property_set_shortcut(menuitem, key->accel_key, key->accel_mods); +} + +/* A set of typed data for the interator */ +typedef struct _iter_data_t iter_data_t; +struct _iter_data_t { + guint * key; + GdkModifierType * modifier; +}; + +/* Goes through the wrapper items. In reality we only support one + so it checks to see if a key is set first. But, we could possibly, + support more in the future. */ +static void +_wrapper_iterator (const GValue * value, gpointer user_data) +{ + iter_data_t * iter_data = (iter_data_t *)user_data; + + if (*iter_data->key != 0) { + g_warning("Shortcut is more than one entry. Which we don't currently support. Taking the first."); + return; + } + + if (!G_VALUE_HOLDS(value, G_TYPE_STRV)) { + g_warning("Unexpected shortcut structure. Value array is: %s", G_VALUE_TYPE_NAME(value)); + return; + } + + gchar ** stringarray = (gchar **)g_value_get_boxed(value); + if (stringarray == NULL) { + return; + } + + const gchar * last_string = NULL; + int i; + + for (i = 0; stringarray[i] != NULL; i++) { + last_string = stringarray[i]; + + if (g_strcmp0(last_string, DBUSMENU_MENUITEM_SHORTCUT_CONTROL) == 0) { + *iter_data->modifier |= GDK_CONTROL_MASK; + continue; + } + if (g_strcmp0(last_string, DBUSMENU_MENUITEM_SHORTCUT_ALT) == 0) { + *iter_data->modifier |= GDK_MOD1_MASK; + continue; + } + if (g_strcmp0(last_string, DBUSMENU_MENUITEM_SHORTCUT_SHIFT) == 0) { + *iter_data->modifier |= GDK_SHIFT_MASK; + continue; + } + if (g_strcmp0(last_string, DBUSMENU_MENUITEM_SHORTCUT_SUPER) == 0) { + *iter_data->modifier |= GDK_SUPER_MASK; + continue; + } + } + + if (last_string != NULL) { + GdkModifierType tempmod; + gtk_accelerator_parse(last_string, iter_data->key, &tempmod); + } + + return; +} + +/** + dbusmenu_menuitem_property_get_shortcut: + @menuitem: The #DbusmenuMenuitem to get the shortcut off + @key: Location to put the key value + @modifier: Location to put the modifier mask + + This function gets a GTK shortcut as a key and a mask + for use to set the accelerators. +*/ +void +dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifier) +{ + *key = 0; + *modifier = 0; + + g_return_if_fail(DBUSMENU_IS_MENUITEM(menuitem)); + + const GValue * wrapper = dbusmenu_menuitem_property_get_value(menuitem, DBUSMENU_MENUITEM_PROP_SHORTCUT); + if (wrapper == NULL) { + return; + } + if (!dbus_g_type_is_collection(G_VALUE_TYPE(wrapper))) { + g_warning("Unexpected shortcut structure. Wrapper is: %s", G_VALUE_TYPE_NAME(wrapper)); + return; + } + + iter_data_t iter_data; + iter_data.key = key; + iter_data.modifier = modifier; + + dbus_g_type_collection_value_iterate(wrapper, _wrapper_iterator, &iter_data); + + return; +} + diff --git a/libdbusmenu-gtk/menuitem.h b/libdbusmenu-gtk/menuitem.h index ff458de..6960f76 100644 --- a/libdbusmenu-gtk/menuitem.h +++ b/libdbusmenu-gtk/menuitem.h @@ -32,8 +32,15 @@ License version 3 and version 2.1 along with this program. If not, see #include <glib.h> #include <gdk-pixbuf/gdk-pixbuf.h> #include <libdbusmenu-glib/menuitem.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> gboolean dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar * property, const GdkPixbuf * data); GdkPixbuf * dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar * property); +gboolean dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier); +gboolean dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, const gchar * shortcut); +gboolean dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, const GtkMenuItem * gmi); +void dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifiers); + #endif |