diff options
Diffstat (limited to 'src/idousermenuitem.c')
-rw-r--r-- | src/idousermenuitem.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/src/idousermenuitem.c b/src/idousermenuitem.c new file mode 100644 index 0000000..655ae21 --- /dev/null +++ b/src/idousermenuitem.c @@ -0,0 +1,468 @@ +/* +Copyright 2011 Canonical Ltd. + +Authors: + Conor Curran <conor.curran@canonical.com> + Mirco Müller <mirco.mueller@canonical.com> + Charles Kerr <charles.kerr@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, as published +by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include <gtk/gtk.h> + +#include "idousermenuitem.h" +#include "idoactionhelper.h" + +#define FALLBACK_ICON_NAME "avatar-default" + +enum +{ + PROP_0, + PROP_LABEL, + PROP_ICON, + PROP_IS_LOGGED_IN, + PROP_IS_CURRENT_USER, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _IdoUserMenuItemPrivate +{ + GtkWidget* user_image; + GtkWidget* user_name; + GtkWidget* container; + GtkWidget* tick_icon; + gboolean is_logged_in; + gboolean is_current_user; + gchar * label; + GIcon * icon; +}; + +G_DEFINE_TYPE (IdoUserMenuItem, ido_user_menu_item, GTK_TYPE_MENU_ITEM); + +/* Prototypes */ +static gboolean ido_user_menu_item_primitive_draw_cb_gtk_3 (GtkWidget * image, + cairo_t * cr, + gpointer gself); + + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IdoUserMenuItem * self = IDO_USER_MENU_ITEM (o); + + switch (property_id) + { + case PROP_LABEL: + g_value_set_string (value, self->priv->label); + break; + + case PROP_ICON: + g_value_set_object (value, self->priv->icon); + break; + + case PROP_IS_LOGGED_IN: + g_value_set_boolean (value, self->priv->is_logged_in); + break; + + case PROP_IS_CURRENT_USER: + g_value_set_boolean (value, self->priv->is_current_user); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + break; + } +} + +static void +my_set_property (GObject * o, + guint property_id, + const GValue * value, + GParamSpec * pspec) +{ + IdoUserMenuItem * self = IDO_USER_MENU_ITEM (o); + + switch (property_id) + { + case PROP_LABEL: + ido_user_menu_item_set_label (self, g_value_get_string (value)); + break; + + case PROP_ICON: + ido_user_menu_item_set_icon (self, g_value_get_object (value)); + break; + + case PROP_IS_LOGGED_IN: + ido_user_menu_item_set_logged_in (self, g_value_get_boolean (value)); + break; + + case PROP_IS_CURRENT_USER: + self->priv->is_current_user = g_value_get_boolean (value); + gtk_widget_queue_draw (GTK_WIDGET(self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + break; + } +} + +static void +my_dispose (GObject *object) +{ + IdoUserMenuItem * self = IDO_USER_MENU_ITEM (object); + + g_clear_object (&self->priv->icon); + + G_OBJECT_CLASS (ido_user_menu_item_parent_class)->dispose (object); +} + +static void +my_finalize (GObject *object) +{ + IdoUserMenuItem * self = IDO_USER_MENU_ITEM (object); + + g_free (self->priv->label); + + G_OBJECT_CLASS (ido_user_menu_item_parent_class)->finalize (object); +} + +static void +ido_user_menu_item_class_init (IdoUserMenuItemClass *klass) +{ + GParamFlags prop_flags; + GObjectClass * gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (IdoUserMenuItemPrivate)); + + gobject_class->get_property = my_get_property; + gobject_class->set_property = my_set_property; + gobject_class->dispose = my_dispose; + gobject_class->finalize = my_finalize; + + prop_flags = G_PARAM_CONSTRUCT + | G_PARAM_READWRITE + | G_PARAM_STATIC_STRINGS; + + properties[PROP_LABEL] = g_param_spec_string ("label", + "The user's name", + "The name to display", + "J. Random User", + prop_flags); + + properties[PROP_ICON] = g_param_spec_object ("icon", + "Icon", + "The user's GIcon", + G_TYPE_OBJECT, + prop_flags); + + properties[PROP_IS_LOGGED_IN] = g_param_spec_boolean ("is-logged-in", + "is logged in", + "is user logged in?", + FALSE, + prop_flags); + + properties[PROP_IS_CURRENT_USER] = g_param_spec_boolean ("is-current-user", + "is current user", + "is user current?", + FALSE, + prop_flags); + + g_object_class_install_properties (gobject_class, PROP_LAST, properties); +} + +static void +ido_user_menu_item_init (IdoUserMenuItem *self) +{ + IdoUserMenuItemPrivate * priv; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + IDO_USER_MENU_ITEM_TYPE, + IdoUserMenuItemPrivate); + + priv = self->priv; + + // Create the UI elements. + priv->user_image = gtk_image_new (); + gtk_misc_set_alignment(GTK_MISC(priv->user_image), 0.0, 0.0); + + priv->user_name = gtk_label_new (NULL); + + priv->container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); + + priv->tick_icon = gtk_image_new_from_icon_name ("account-logged-in", + GTK_ICON_SIZE_MENU); + gtk_misc_set_alignment(GTK_MISC(priv->tick_icon), 1.0, 0.5); + + // Pack it together + gtk_box_pack_start (GTK_BOX (priv->container), + priv->user_image, + FALSE, + FALSE, + 0); + gtk_box_pack_start (GTK_BOX (priv->container), + priv->user_name, + FALSE, + FALSE, + 3); + gtk_box_pack_end (GTK_BOX(priv->container), + priv->tick_icon, + FALSE, + FALSE, 5); + + gtk_widget_show_all (priv->container); + gtk_container_add (GTK_CONTAINER (self), priv->container); + gtk_widget_show_all (priv->tick_icon); + gtk_widget_set_no_show_all (priv->tick_icon, TRUE); + gtk_widget_hide (priv->tick_icon); + + + // Fetch the drawing context. + g_signal_connect_after (GTK_WIDGET(self), "draw", + G_CALLBACK(ido_user_menu_item_primitive_draw_cb_gtk_3), + GTK_WIDGET(self)); +} + + +/*****************************************************************/ + +static gboolean +ido_user_menu_item_primitive_draw_cb_gtk_3 (GtkWidget * widget, + cairo_t * cr, + gpointer user_data) +{ + IdoUserMenuItemPrivate * priv; + + g_return_val_if_fail(IS_IDO_USER_MENU_ITEM(user_data), FALSE); + + priv = IDO_USER_MENU_ITEM(user_data)->priv; + + /* Draw dot only when user is the current user. */ + if (priv->is_current_user) + { + GtkStyleContext * style_context; + GtkStateFlags state_flags; + GdkRGBA color; + gdouble x, y; + + /* get the foreground color */ + style_context = gtk_widget_get_style_context (widget); + state_flags = gtk_widget_get_state_flags (widget); + gtk_style_context_get_color (style_context, state_flags, &color); + + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + x = allocation.x + 13; + y = allocation.height / 2; + + cairo_arc (cr, x, y, 3.0, 0.0, 2 * G_PI); + + gdk_cairo_set_source_rgba (cr, &color); + + cairo_fill (cr); + } + + return FALSE; +} + +/*** +**** PUBLIC API +***/ + +void +ido_user_menu_item_set_icon (IdoUserMenuItem * self, GIcon * icon) +{ + IdoUserMenuItemPrivate * p = self->priv; + GtkImage * image = GTK_IMAGE (p->user_image); + + g_clear_object (&p->icon); + + if (icon != NULL) + g_object_ref (icon); + else + icon = g_themed_icon_new_with_default_fallbacks (FALLBACK_ICON_NAME); + + gtk_image_set_from_gicon (image, icon, GTK_ICON_SIZE_MENU); +} + +void +ido_user_menu_item_set_icon_from_file (IdoUserMenuItem * self, const char * filename) +{ + GFile * file = g_file_new_for_path (filename); + GIcon * icon = g_file_icon_new (file); + + ido_user_menu_item_set_icon (self, icon); + + g_clear_object (&icon); + g_clear_object (&file); +} + +void +ido_user_menu_item_set_logged_in (IdoUserMenuItem * self, gboolean is_logged_in) +{ + gtk_widget_set_visible (self->priv->tick_icon, is_logged_in); +} + +void +ido_user_menu_item_set_current_user (IdoUserMenuItem * self, gboolean is_current_user) +{ + self->priv->is_current_user = is_current_user; + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +void +ido_user_menu_item_set_label (IdoUserMenuItem * self, const char * label) +{ + gtk_label_set_label (GTK_LABEL(self->priv->user_name), label); +} + +GtkWidget* +ido_user_menu_item_new (void) +{ + return GTK_WIDGET (g_object_new (IDO_USER_MENU_ITEM_TYPE, NULL)); +} + +/** + * user_menu_item_state_changed: + * + * Updates an IdoUserMenuItem from @state. The state contains a + * dictionary with keys 'active-user' (for the user that the current + * session belongs too) and 'logged-in-users' (a list of all currently + * logged in users). + */ +static void +user_menu_item_state_changed (IdoActionHelper *helper, + GVariant *state, + gpointer user_data) +{ + IdoUserMenuItem *item; + GVariant *target; + GVariant *v; + + item = IDO_USER_MENU_ITEM (ido_action_helper_get_widget (helper)); + + ido_user_menu_item_set_current_user (item, FALSE); + ido_user_menu_item_set_logged_in (item, FALSE); + + target = ido_action_helper_get_action_target (helper); + g_return_if_fail (g_variant_is_of_type (target, G_VARIANT_TYPE_STRING)); + + if ((v = g_variant_lookup_value (state, "active-user", G_VARIANT_TYPE_STRING))) + { + if (g_variant_equal (v, target)) + ido_user_menu_item_set_current_user (item, TRUE); + + g_variant_unref (v); + } + + if ((v = g_variant_lookup_value (state, "logged-in-users", G_VARIANT_TYPE_STRING_ARRAY))) + { + GVariantIter it; + GVariant *user; + + g_variant_iter_init (&it, v); + while ((user = g_variant_iter_next_value (&it))) + { + if (g_variant_equal (user, target)) + ido_user_menu_item_set_logged_in (item, TRUE); + g_variant_unref (user); + } + + g_variant_unref (v); + } +} + +/** + * ido_user_menu_item_new_from_model: + * + * Creates an #IdoUserMenuItem. If @menuitem contains an action, the + * widget is bound to that action in @actions. + * + * Returns: (transfer full): a new #IdoUserMenuItem + */ +GtkMenuItem * +ido_user_menu_item_new_from_model (GMenuItem *menuitem, + GActionGroup *actions) +{ + guint i; + guint n; + IdoUserMenuItem * ido_user; + gchar * str; + gchar * action; + GVariant * v; + GParameter parameters[4]; + + /* create the ido_user */ + + n = 0; + + if (g_menu_item_get_attribute (menuitem, "label", "s", &str)) + { + GParameter p = { "label", G_VALUE_INIT }; + g_value_init (&p.value, G_TYPE_STRING); + g_value_take_string (&p.value, str); + parameters[n++] = p; + } + + if ((v = g_menu_item_get_attribute_value (menuitem, G_MENU_ATTRIBUTE_ICON, NULL))) + { + GParameter p = { "icon", G_VALUE_INIT }; + GIcon * icon = g_icon_deserialize (v); + g_value_init (&p.value, G_TYPE_OBJECT); + g_value_take_object (&p.value, icon); + g_variant_unref (v); + parameters[n++] = p; + } + + g_assert (n <= G_N_ELEMENTS (parameters)); + ido_user = g_object_newv (IDO_USER_MENU_ITEM_TYPE, n, parameters); + + for (i=0; i<n; i++) + g_value_unset (¶meters[i].value); + + /* gie it an ActionHelper */ + + if (g_menu_item_get_attribute (menuitem, "action", "s", &action)) + { + IdoActionHelper *helper; + GVariant *target; + + target = g_menu_item_get_attribute_value (menuitem, "target", G_VARIANT_TYPE_ANY); + + helper = ido_action_helper_new (GTK_WIDGET (ido_user), actions, action, target); + g_signal_connect (helper, "action-state-changed", + G_CALLBACK (user_menu_item_state_changed), NULL); + + g_signal_connect_object (ido_user, "activate", + G_CALLBACK (ido_action_helper_activate), + helper, G_CONNECT_SWAPPED); + g_signal_connect_swapped (ido_user, "destroy", G_CALLBACK (g_object_unref), helper); + + if (target) + g_variant_unref (target); + g_free (action); + } + + return GTK_MENU_ITEM (ido_user); +} + |