diff options
Diffstat (limited to 'src/im-source-menu-item.c')
-rw-r--r-- | src/im-source-menu-item.c | 142 |
1 files changed, 137 insertions, 5 deletions
diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c index 82d553f..269c75d 100644 --- a/src/im-source-menu-item.c +++ b/src/im-source-menu-item.c @@ -19,12 +19,16 @@ #include "im-source-menu-item.h" +#include <libintl.h> + struct _ImSourceMenuItemPrivate { GActionGroup *action_group; gchar *action; + GtkWidget *icon; GtkWidget *label; + GtkWidget *detail; }; enum @@ -48,11 +52,21 @@ im_source_menu_item_constructed (GObject *object) gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL); + priv->icon = g_object_ref (gtk_image_new ()); + gtk_widget_set_margin_left (priv->icon, icon_width + 2); + priv->label = g_object_ref (gtk_label_new ("")); - gtk_widget_set_margin_left (priv->label, icon_width + 2); + + priv->detail = g_object_ref (gtk_label_new ("")); + gtk_widget_set_halign (priv->detail, GTK_ALIGN_END); + gtk_widget_set_hexpand (priv->detail, TRUE); + gtk_misc_set_alignment (GTK_MISC (priv->label), 1.0, 0.5); + gtk_style_context_add_class (gtk_widget_get_style_context (priv->detail), "accelerator"); grid = gtk_grid_new (); - gtk_grid_attach (GTK_GRID (grid), priv->label, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->detail, 2, 0, 1, 1); gtk_container_add (GTK_CONTAINER (object), grid); gtk_widget_show_all (grid); @@ -60,6 +74,104 @@ im_source_menu_item_constructed (GObject *object) G_OBJECT_CLASS (im_source_menu_item_parent_class)->constructed (object); } +/* collapse_whitespace: + * @str: the source string + * + * Collapses all occurences of consecutive whitespace charactes in @str + * into a single space. + * + * Returns: (transfer full): a newly-allocated string + */ +static gchar * +collapse_whitespace (const gchar *str) +{ + GString *result; + gboolean in_space = FALSE; + + if (str == NULL) + return NULL; + + result = g_string_new (""); + + while (*str) + { + gunichar c = g_utf8_get_char_validated (str, -1); + + if (c < 0) + break; + + if (!g_unichar_isspace (c)) + { + g_string_append_unichar (result, c); + in_space = FALSE; + } + else if (!in_space) + { + g_string_append_c (result, ' '); + in_space = TRUE; + } + + str = g_utf8_next_char (str); + } + + return g_string_free (result, FALSE); +} + +static gchar * +im_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 m", "%d m", minutes), minutes); + } + else + { + /* TRANSLATORS: number of hours that have passed */ + str = g_strdup_printf (ngettext ("%d h", "%d h", hours), hours); + } + + return str; +} + +static gboolean +im_source_menu_item_set_state (ImSourceMenuItem *self, + GVariant *state) +{ + ImSourceMenuItemPrivate *priv = self->priv; + guint32 count; + gint64 time; + const gchar *str; + gchar *detail; + + g_return_val_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)")), FALSE); + + g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); + + if (count != 0) + detail = g_strdup_printf ("%d", count); + else if (time != 0) + detail = im_source_menu_item_time_span_string (time); + else if (str != NULL && *str) + detail = collapse_whitespace (str); + else + detail = NULL; + + gtk_label_set_text (GTK_LABEL (priv->detail), detail ? detail : ""); + + g_free (detail); + return TRUE; +} + static void im_source_menu_item_set_action_name (ImSourceMenuItem *self, const gchar *action_name) @@ -77,7 +189,7 @@ im_source_menu_item_set_action_name (ImSourceMenuItem *self, g_action_group_query_action (priv->action_group, priv->action, &enabled, NULL, NULL, NULL, &state)) { - if (!state || !g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)"))) + if (!state || !im_source_menu_item_set_state (self, state)) enabled = FALSE; if (state) @@ -132,7 +244,7 @@ im_source_menu_item_action_state_changed (GActionGroup *action_group, ImSourceMenuItem *self = user_data; if (g_strcmp0 (self->priv->action, action_name) == 0) - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("(uxsb)"))); + im_source_menu_item_set_state (self, value); } static void @@ -166,6 +278,10 @@ im_source_menu_item_dispose (GObject *object) if (self->priv->action_group) im_source_menu_item_set_action_group (self, NULL); + g_clear_object (&self->priv->icon); + g_clear_object (&self->priv->label); + g_clear_object (&self->priv->detail); + G_OBJECT_CLASS (im_source_menu_item_parent_class)->dispose (object); } @@ -226,22 +342,38 @@ im_source_menu_item_init (ImSourceMenuItem *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemPrivate); - g_message (G_STRFUNC); } void im_source_menu_item_set_menu_item (ImSourceMenuItem *self, GMenuItem *menuitem) { + gchar *iconstr = NULL; + GIcon *icon = NULL; gchar *label; gchar *action = NULL; + if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr)) + { + GError *error; + icon = g_icon_new_for_string (iconstr, &error); + if (icon == NULL) + { + g_warning ("unable to set icon: %s", error->message); + g_error_free (error); + } + g_free (iconstr); + } + gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU); + g_menu_item_get_attribute (menuitem, "label", "s", &label); gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : ""); g_menu_item_get_attribute (menuitem, "action", "s", &action); im_source_menu_item_set_action_name (self, action); + if (icon) + g_object_unref (icon); g_free (label); g_free (action); } |