/* * Copyright 2013 Canonical Ltd. * * 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 . * * Authors: * Lars Uebernickel */ #include "idosourcemenuitem.h" #include #include "idodetaillabel.h" #include "idoactionhelper.h" typedef GtkMenuItemClass IdoSourceMenuItemClass; struct _IdoSourceMenuItem { GtkMenuItem parent; GtkWidget *icon; GtkWidget *label; GtkWidget *detail; gint64 time; guint timer_id; }; G_DEFINE_TYPE (IdoSourceMenuItem, ido_source_menu_item, GTK_TYPE_MENU_ITEM); static void ido_source_menu_item_constructed (GObject *object) { IdoSourceMenuItem *item = IDO_SOURCE_MENU_ITEM (object); GtkWidget *grid; gint icon_width; gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL); item->icon = g_object_ref (gtk_image_new ()); gtk_widget_set_margin_start(item->icon, icon_width); gtk_widget_set_margin_end(item->icon, 6); item->label = g_object_ref (gtk_label_new ("")); gtk_label_set_max_width_chars (GTK_LABEL (item->label), 40); gtk_label_set_ellipsize (GTK_LABEL (item->label), PANGO_ELLIPSIZE_END); gtk_widget_set_halign(item->label, GTK_ALIGN_START); gtk_widget_set_valign(item->label, GTK_ALIGN_CENTER); item->detail = g_object_ref (ido_detail_label_new ("")); gtk_widget_set_halign (item->detail, GTK_ALIGN_END); gtk_widget_set_hexpand (item->detail, TRUE); gtk_style_context_add_class (gtk_widget_get_style_context (item->detail), "accelerator"); grid = gtk_grid_new (); gtk_grid_attach (GTK_GRID (grid), item->icon, 0, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), item->label, 1, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), item->detail, 2, 0, 1, 1); gtk_container_add (GTK_CONTAINER (object), grid); gtk_widget_show_all (grid); G_OBJECT_CLASS (ido_source_menu_item_parent_class)->constructed (object); } static gchar * ido_source_menu_item_time_span_string (gint64 timestamp) { gchar *str; gint64 span; gint hours; gint minutes; span = MAX (g_get_real_time () - timestamp, 0) / G_USEC_PER_SEC; hours = span / 3600; minutes = (span / 60) % 60; if (hours == 0) { /* TRANSLATORS: number of minutes that have passed */ str = g_strdup_printf (ngettext ("%d min", "%d min", minutes), minutes); } else { /* TRANSLATORS: number of hours that have passed */ str = g_strdup_printf (ngettext ("%d h", "%d h", hours), hours); } return str; } static void ido_source_menu_item_set_detail_time (IdoSourceMenuItem *self, gint64 time) { gchar *str; self->time = time; str = ido_source_menu_item_time_span_string (self->time); ido_detail_label_set_text (IDO_DETAIL_LABEL (self->detail), str); g_free (str); } static gboolean ido_source_menu_item_update_time (gpointer data) { IdoSourceMenuItem *self = data; ido_source_menu_item_set_detail_time (self, self->time); return TRUE; } static void ido_source_menu_item_dispose (GObject *object) { IdoSourceMenuItem *self = IDO_SOURCE_MENU_ITEM (object); if (self->timer_id != 0) { g_source_remove (self->timer_id); self->timer_id = 0; } g_clear_object (&self->icon); g_clear_object (&self->label); g_clear_object (&self->detail); G_OBJECT_CLASS (ido_source_menu_item_parent_class)->dispose (object); } static void ido_source_menu_item_class_init (IdoSourceMenuItemClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = ido_source_menu_item_constructed; object_class->dispose = ido_source_menu_item_dispose; } static void ido_source_menu_item_init (IdoSourceMenuItem *self) { } static void ido_source_menu_item_set_label (IdoSourceMenuItem *item, const gchar *label) { gtk_label_set_label (GTK_LABEL (item->label), label ? label : ""); } static void ido_source_menu_item_set_icon (IdoSourceMenuItem *item, GIcon *icon) { if (icon) gtk_image_set_from_gicon (GTK_IMAGE (item->icon), icon, GTK_ICON_SIZE_MENU); else gtk_image_clear (GTK_IMAGE (item->icon)); } static void ido_source_menu_item_activate (GtkMenuItem *item, gpointer user_data) { IdoActionHelper *helper = user_data; /* The parameter signifies whether this source was activated (TRUE) or * dismissed (FALSE). Since there's no UI to dismiss a gtkmenuitem, * this always passes TRUE. */ ido_action_helper_activate_with_parameter (helper, g_variant_new_boolean (TRUE)); } static void ido_source_menu_item_state_changed (IdoActionHelper *helper, GVariant *state, gpointer user_data) { IdoSourceMenuItem *item = user_data; guint32 count; gint64 time; const gchar *str; if (item->timer_id != 0) { g_source_remove (item->timer_id); item->timer_id = 0; } g_return_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)"))); g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); if (count != 0) ido_detail_label_set_count (IDO_DETAIL_LABEL (item->detail), count); else if (time != 0) { ido_source_menu_item_set_detail_time (item, time); item->timer_id = g_timeout_add_seconds (59, ido_source_menu_item_update_time, item); } else if (str != NULL && *str) ido_detail_label_set_text (IDO_DETAIL_LABEL (item->detail), str); } GtkMenuItem * ido_source_menu_item_new_from_menu_model (GMenuItem *menuitem, GActionGroup *actions) { GtkMenuItem *item; GVariant *serialized_icon; GIcon *icon = NULL; gchar *label; gchar *action = NULL; item = g_object_new (IDO_TYPE_SOURCE_MENU_ITEM, NULL); if (g_menu_item_get_attribute (menuitem, "label", "s", &label)) { ido_source_menu_item_set_label (IDO_SOURCE_MENU_ITEM (item), label); g_free (label); } serialized_icon = g_menu_item_get_attribute_value (menuitem, "icon", NULL); if (serialized_icon) { icon = g_icon_deserialize (serialized_icon); g_variant_unref (serialized_icon); } ido_source_menu_item_set_icon (IDO_SOURCE_MENU_ITEM (item), icon); if (g_menu_item_get_attribute (menuitem, "action", "s", &action)) { IdoActionHelper *helper; helper = ido_action_helper_new (GTK_WIDGET (item), actions, action, NULL); g_signal_connect (helper, "action-state-changed", G_CALLBACK (ido_source_menu_item_state_changed), item); g_signal_connect_object (item, "activate", G_CALLBACK (ido_source_menu_item_activate), helper, 0); g_signal_connect_swapped (item, "destroy", G_CALLBACK (g_object_unref), helper); g_free (action); } if (icon) g_object_unref (icon); return item; }