From 0a86676833595e0684bd1ba0dea18709fea74e7a Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Sat, 1 Sep 2012 10:27:34 +0200 Subject: libmessaging-menu: make generated .gir compatible with vala In particular, mamke sure the and tags are included in the .gir. Fixes launchpad bug #1044096, thanks Jim! --- libmessaging-menu/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index ef37700..7a6ee31 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -50,8 +50,10 @@ MessagingMenu-1.0.gir: libmessaging-menu.la MessagingMenu_1_0_gir_NAMESPACE = MessagingMenu MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS) +MessagingMenu_1_0_gir_SCANNERFLAGS = --c-include="messaging-menu.h" MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la MessagingMenu_1_0_gir_FILES = messaging-menu.c messaging-menu.h +MessagingMenu_1_0_gir_EXPORT_PACKAGES = messaging-menu INTROSPECTION_GIRS += MessagingMenu-1.0.gir girdir = $(datadir)/gir-1.0 -- cgit v1.2.3 From deff47a562860c1ab698d3861d387208e55bba06 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Sun, 2 Sep 2012 12:12:59 +0200 Subject: libmessaging-menu: add @include hint to documentation --- libmessaging-menu/messaging-menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 93727fc..4363019 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -27,6 +27,7 @@ * SECTION:messaging-menu * @title: MessagingMenuApp * @short_description: An application section in the messaging menu + * @include: messaging-menu.h * * A #MessagingMenuApp represents an application section in the * Messaging Menu. An application section is tied to an installed -- cgit v1.2.3 From 3856b84e523ec134d1429cf0cef0546f27754200 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Sun, 2 Sep 2012 12:16:14 +0200 Subject: libmessaging-menu: document that @time is expressed in microseconds --- libmessaging-menu/messaging-menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 4363019..a292461 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -763,7 +763,7 @@ void messaging_menu_app_append_source_with_count (MessagingMenuApp *app, * @id: a unique identifier for the source to be added * @icon: (allow-none): the icon associated with the source * @label: a user-visible string best describing the source - * @time: the time when the source was created + * @time: the time when the source was created, in microseconds * * Inserts a new message source into the section representing @app and * initializes it with @time. @@ -791,7 +791,7 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, * @id: a unique identifier for the source to be added * @icon: (allow-none): the icon associated with the source * @label: a user-visible string best describing the source - * @time: the time when the source was created + * @time: the time when the source was created, in microseconds * * Appends a new message source to the end of the section representing @app and * initializes it with @time. @@ -1045,7 +1045,7 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, * messaging_menu_app_set_source_time: * @app: a #MessagingMenuApp * @source_id: a source id - * @time: the new time for the source + * @time: the new time for the source, in microseconds * * Updates the time of @source_id to @time. * -- cgit v1.2.3 From 78ebf86005bf747ad7f1060e7991d4abeb5ed83f Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Sun, 2 Sep 2012 12:30:12 +0200 Subject: libmessaging-menu: add convenience doc The most common case when inserting a timed source is to insert a source with the current time. Emphasize this in the documentation by linking to the convenience methods from the _with_time variants. --- libmessaging-menu/messaging-menu.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index a292461..ebddf0f 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -766,7 +766,8 @@ void messaging_menu_app_append_source_with_count (MessagingMenuApp *app, * @time: the time when the source was created, in microseconds * * Inserts a new message source into the section representing @app and - * initializes it with @time. + * initializes it with @time. Use messaging_menu_app_insert_source() to + * insert a source with the current time. * * To change the time, use messaging_menu_app_set_source_time(). * @@ -793,8 +794,10 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, * @label: a user-visible string best describing the source * @time: the time when the source was created, in microseconds * - * Appends a new message source to the end of the section representing @app and - * initializes it with @time. + * Appends a new message source to the end of the section representing + * @app and initializes it with @time. Use + * messaging_menu_app_append_source() to append a source with the + * current time. * * To change the time, use messaging_menu_app_set_source_time(). * -- cgit v1.2.3 From ec60f89bde965c61f186f9363b47b2581371bbd5 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 3 Sep 2012 13:19:58 +0200 Subject: im-source-menu-item: refactor setting detail string --- src/im-source-menu-item.c | 61 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c index 5aebb68..0973567 100644 --- a/src/im-source-menu-item.c +++ b/src/im-source-menu-item.c @@ -63,7 +63,7 @@ im_source_menu_item_constructed (GObject *object) 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_widget_set_halign (priv->detail, GTK_ALIGN_END); gtk_style_context_add_class (gtk_widget_get_style_context (priv->detail), "accelerator"); grid = gtk_grid_new (); @@ -146,16 +146,54 @@ im_source_menu_item_time_span_string (gint64 timestamp) return str; } +static void +im_source_menu_item_set_detail_count (ImSourceMenuItem *self, + guint32 count) +{ + ImSourceMenuItemPrivate *priv = self->priv; + gchar *str; + + str = g_strdup_printf ("%d", count); + gtk_label_set_text (GTK_LABEL (priv->detail), str); + + g_free (str); +} + +static void +im_source_menu_item_set_detail_time (ImSourceMenuItem *self, + gint64 time) +{ + ImSourceMenuItemPrivate *priv = self->priv; + gchar *str; + + priv->time = time; + + str = im_source_menu_item_time_span_string (priv->time); + gtk_label_set_text (GTK_LABEL (priv->detail), str); + + g_free (str); +} + +static void +im_source_menu_item_set_detail (ImSourceMenuItem *self, + const gchar *detail) +{ + ImSourceMenuItemPrivate *priv = self->priv; + gchar *str; + + str = collapse_whitespace (detail); + gtk_label_set_text (GTK_LABEL (priv->detail), str); + + g_free (str); +} + static gboolean im_source_menu_item_update_time (gpointer data) { ImSourceMenuItem *self = data; - gchar *str; - str = im_source_menu_item_time_span_string (self->priv->time); - gtk_label_set_text (GTK_LABEL (self->priv->detail), str); + im_source_menu_item_set_detail_time (self, self->priv->time); - g_free (str); return TRUE; } @@ -167,7 +205,6 @@ im_source_menu_item_set_state (ImSourceMenuItem *self, guint32 count; gint64 time; const gchar *str; - gchar *detail; if (priv->timer_id != 0) { @@ -180,21 +217,15 @@ im_source_menu_item_set_state (ImSourceMenuItem *self, g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); if (count != 0) - detail = g_strdup_printf ("%d", count); + im_source_menu_item_set_detail_count (self, count); else if (time != 0) { - priv->time = time; - detail = im_source_menu_item_time_span_string (time); + im_source_menu_item_set_detail_time (self, time); priv->timer_id = g_timeout_add_seconds (59, im_source_menu_item_update_time, self); } else if (str != NULL && *str) - detail = collapse_whitespace (str); - else - detail = NULL; - - gtk_label_set_text (GTK_LABEL (priv->detail), detail ? detail : ""); + im_source_menu_item_set_detail (self, str); - g_free (detail); return TRUE; } -- cgit v1.2.3 From 641cda4f3387597e297afee9429d89be2d79893b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 3 Sep 2012 15:37:23 +0200 Subject: im-source-menu-item: draw lozenges around counts A new widget class IdoDetailLabel is introduced, which can display either a string or a count. Counts are drawn as lozenges. --- src/Makefile.am | 2 + src/ido-detail-label.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++ src/ido-detail-label.h | 59 +++++++ src/im-source-menu-item.c | 79 +--------- 4 files changed, 458 insertions(+), 74 deletions(-) create mode 100644 src/ido-detail-label.c create mode 100644 src/ido-detail-label.h diff --git a/src/Makefile.am b/src/Makefile.am index 4d86730..1df80e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,6 +21,8 @@ libmessaging_la_SOURCES = \ im-app-menu-item.h \ im-source-menu-item.c \ im-source-menu-item.h \ + ido-detail-label.c \ + ido-detail-label.h \ indicator-messages-service.c \ indicator-messages-service.h dbus-data.h diff --git a/src/ido-detail-label.c b/src/ido-detail-label.c new file mode 100644 index 0000000..5bfe884 --- /dev/null +++ b/src/ido-detail-label.c @@ -0,0 +1,392 @@ +/* + * Copyright 2012 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 "ido-detail-label.h" + +#include + +G_DEFINE_TYPE (IdoDetailLabel, ido_detail_label, GTK_TYPE_WIDGET) + +struct _IdoDetailLabelPrivate +{ + gchar *text; + PangoLayout *layout; + gboolean draw_lozenge; +}; + +enum +{ + PROP_0, + PROP_TEXT, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES]; + +static void +ido_detail_label_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + IdoDetailLabel *self = IDO_DETAIL_LABEL (object); + + switch (property_id) + { + case PROP_TEXT: + g_value_set_string (value, self->priv->text); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ido_detail_label_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + IdoDetailLabel *self = IDO_DETAIL_LABEL (object); + + switch (property_id) + { + case PROP_TEXT: + ido_detail_label_set_text (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + + +static void +ido_detail_label_finalize (GObject *object) +{ + IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv; + + g_free (priv->text); +} + +static void +ido_detail_label_dispose (GObject *object) +{ + IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv; + + g_clear_object (&priv->layout); +} + +static void +ido_detail_label_ensure_layout (IdoDetailLabel *label) +{ + IdoDetailLabelPrivate *priv = label->priv; + + if (priv->layout == NULL) + { + priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), priv->text); + pango_layout_set_alignment (priv->layout, PANGO_ALIGN_CENTER); + pango_layout_set_ellipsize (priv->layout, PANGO_ELLIPSIZE_END); + pango_layout_set_height (priv->layout, -1); + + // TODO update layout on "style-updated" and "direction-changed" + } +} + +static void +cairo_lozenge (cairo_t *cr, + double x, + double y, + double w, + double h, + double radius) +{ + double x1 = x + w - radius; + double x2 = x + radius; + double y1 = y + radius; + double y2 = y + h - radius; + + cairo_move_to (cr, x + radius, y); + cairo_arc (cr, x1, y1, radius, G_PI * 1.5, G_PI * 2); + cairo_arc (cr, x1, y2, radius, 0, G_PI * 0.5); + cairo_arc (cr, x2, y2, radius, G_PI * 0.5, G_PI); + cairo_arc (cr, x2, y1, radius, G_PI, G_PI * 1.5); +} + +static PangoFontMetrics * +gtk_widget_get_font_metrics (GtkWidget *widget, + PangoContext *context) +{ + const PangoFontDescription *font; + + font = gtk_style_context_get_font (gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget)); + + return pango_context_get_metrics (context, + font, + pango_context_get_language (context)); +} + +static gint +ido_detail_label_get_minimum_text_width (IdoDetailLabel *label) +{ + IdoDetailLabelPrivate *priv = label->priv; + PangoContext *context; + PangoFontMetrics *metrics; + gint char_width; + gint w; + + context = pango_layout_get_context (priv->layout); + metrics = gtk_widget_get_font_metrics (GTK_WIDGET (label), context); + char_width = pango_font_metrics_get_approximate_digit_width (metrics); + + w = 2 * char_width / PANGO_SCALE; + pango_font_metrics_unref (metrics); + return w; +} + +static gboolean +ido_detail_label_draw (GtkWidget *widget, + cairo_t *cr) +{ + IdoDetailLabel *label = IDO_DETAIL_LABEL (widget); + IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv; + PangoRectangle extents; + GtkAllocation allocation; + double x, w, h, radius; + GdkRGBA color; + + if (!priv->text || !*priv->text) + return TRUE; + + gtk_widget_get_allocation (widget, &allocation); + + ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget)); + + pango_layout_get_extents (priv->layout, NULL, &extents); + pango_extents_to_pixels (&extents, NULL); + + h = MIN (allocation.height, extents.height); + radius = floor (h / 2.0); + w = MAX (ido_detail_label_get_minimum_text_width (label), extents.width) + 2.0 * radius; + x = allocation.width - w; + + pango_layout_set_width (priv->layout, (allocation.width - 2 * radius) * PANGO_SCALE); + pango_layout_get_extents (priv->layout, NULL, &extents); + pango_extents_to_pixels (&extents, NULL); + + gtk_style_context_get_color (gtk_widget_get_style_context (widget), + gtk_widget_get_state_flags (widget), + &color); + gdk_cairo_set_source_rgba (cr, &color); + + cairo_set_line_width (cr, 1.0); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + + if (priv->draw_lozenge) + cairo_lozenge (cr, x, 0.0, w, h, radius); + + cairo_move_to (cr, x + radius, (allocation.height - extents.height) / 2.0); + pango_cairo_layout_path (cr, priv->layout); + cairo_fill (cr); + + return TRUE; +} + +static void +ido_detail_label_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv; + PangoRectangle extents; + double radius; + + ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget)); + + pango_layout_get_extents (priv->layout, NULL, &extents); + pango_extents_to_pixels (&extents, NULL); + + radius = floor (extents.height / 2.0); + + *minimum = ido_detail_label_get_minimum_text_width (IDO_DETAIL_LABEL (widget)) + 2.0 * radius; + *natural = MAX (*minimum, extents.width + 2.0 * radius); +} + +static void +ido_detail_label_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv; + PangoContext *context; + PangoFontMetrics *metrics; + PangoRectangle extents; + + ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget)); + + pango_layout_get_extents (priv->layout, NULL, &extents); + pango_extents_to_pixels (&extents, NULL); + context = pango_layout_get_context (priv->layout); + metrics = gtk_widget_get_font_metrics (widget, context); + + *minimum = *natural = (pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics)) / PANGO_SCALE; + + pango_font_metrics_unref (metrics); +} + +static void +ido_detail_label_class_init (IdoDetailLabelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = ido_detail_label_get_property; + object_class->set_property = ido_detail_label_set_property; + object_class->finalize = ido_detail_label_finalize; + object_class->dispose = ido_detail_label_dispose; + + widget_class->draw = ido_detail_label_draw; + widget_class->get_preferred_width = ido_detail_label_get_preferred_width; + widget_class->get_preferred_height = ido_detail_label_get_preferred_height; + + g_type_class_add_private (klass, sizeof (IdoDetailLabelPrivate)); + + properties[PROP_TEXT] = g_param_spec_string ("text", + "Text", + "The text of the label", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); +} + +static void +ido_detail_label_init (IdoDetailLabel *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + IDO_TYPE_DETAIL_LABEL, + IdoDetailLabelPrivate); + + gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); +} + +GtkWidget * +ido_detail_label_new (const gchar *label) +{ + return g_object_new (IDO_TYPE_DETAIL_LABEL, + "text", label, + NULL); +} + +const gchar * +ido_detail_label_get_text (IdoDetailLabel *label) +{ + g_return_val_if_fail (IDO_IS_DETAIL_LABEL (label), NULL); + return label->priv->text; +} + +static void +ido_detail_label_clear (IdoDetailLabel *label) +{ + g_free (label->priv->text); + g_clear_object (&label->priv->layout); +} + +/* 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); +} + +void +ido_detail_label_set_text (IdoDetailLabel *label, + const gchar *text) +{ + IdoDetailLabelPrivate *priv; + + g_return_if_fail (IDO_IS_DETAIL_LABEL (label)); + priv = label->priv; + + ido_detail_label_clear (label); + + priv->text = collapse_whitespace (text); + label->priv->draw_lozenge = FALSE; + + g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]); + gtk_widget_queue_resize (GTK_WIDGET (label)); +} + +void +ido_detail_label_set_count (IdoDetailLabel *label, + gint count) +{ + IdoDetailLabelPrivate *priv; + + g_return_if_fail (IDO_IS_DETAIL_LABEL (label)); + priv = label->priv; + + ido_detail_label_clear (label); + + priv->text = g_strdup_printf ("%d", count); + label->priv->draw_lozenge = TRUE; + + g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]); + gtk_widget_queue_resize (GTK_WIDGET (label)); +} diff --git a/src/ido-detail-label.h b/src/ido-detail-label.h new file mode 100644 index 0000000..1995fee --- /dev/null +++ b/src/ido-detail-label.h @@ -0,0 +1,59 @@ +/* + * Copyright 2012 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 + */ + +#ifndef __IDO_DETAIL_LABEL_H__ +#define __IDO_DETAIL_LABEL_H__ + +#include + +#define IDO_TYPE_DETAIL_LABEL (ido_detail_label_get_type()) +#define IDO_DETAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_DETAIL_LABEL, IdoDetailLabel)) +#define IDO_DETAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDO_TYPE_DETAIL_LABEL, IdoDetailLabelClass)) +#define IDO_IS_DETAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_DETAIL_LABEL)) +#define IDO_IS_DETAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDO_TYPE_DETAIL_LABEL)) +#define IDO_DETAIL_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDO_TYPE_DETAIL_LABEL, IdoDetailLabelClass)) + +typedef struct _IdoDetailLabel IdoDetailLabel; +typedef struct _IdoDetailLabelClass IdoDetailLabelClass; +typedef struct _IdoDetailLabelPrivate IdoDetailLabelPrivate; + +struct _IdoDetailLabel +{ + GtkWidget parent; + IdoDetailLabelPrivate *priv; +}; + +struct _IdoDetailLabelClass +{ + GtkWidgetClass parent_class; +}; + +GType ido_detail_label_get_type (void) G_GNUC_CONST; + +GtkWidget * ido_detail_label_new (const gchar *str); + +const gchar * ido_detail_label_get_text (IdoDetailLabel *label); + +void ido_detail_label_set_text (IdoDetailLabel *label, + const gchar *text); + +void ido_detail_label_set_count (IdoDetailLabel *label, + gint count); + +#endif diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c index 0973567..5f89d07 100644 --- a/src/im-source-menu-item.c +++ b/src/im-source-menu-item.c @@ -20,6 +20,7 @@ #include "im-source-menu-item.h" #include +#include "ido-detail-label.h" struct _ImSourceMenuItemPrivate { @@ -60,10 +61,9 @@ im_source_menu_item_constructed (GObject *object) priv->label = g_object_ref (gtk_label_new ("")); - priv->detail = g_object_ref (gtk_label_new ("")); + priv->detail = g_object_ref (ido_detail_label_new ("")); gtk_widget_set_halign (priv->detail, GTK_ALIGN_END); gtk_widget_set_hexpand (priv->detail, TRUE); - gtk_widget_set_halign (priv->detail, GTK_ALIGN_END); gtk_style_context_add_class (gtk_widget_get_style_context (priv->detail), "accelerator"); grid = gtk_grid_new (); @@ -77,49 +77,6 @@ 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) { @@ -146,19 +103,6 @@ im_source_menu_item_time_span_string (gint64 timestamp) return str; } -static void -im_source_menu_item_set_detail_count (ImSourceMenuItem *self, - guint32 count) -{ - ImSourceMenuItemPrivate *priv = self->priv; - gchar *str; - - str = g_strdup_printf ("%d", count); - gtk_label_set_text (GTK_LABEL (priv->detail), str); - - g_free (str); -} - static void im_source_menu_item_set_detail_time (ImSourceMenuItem *self, gint64 time) @@ -169,20 +113,7 @@ im_source_menu_item_set_detail_time (ImSourceMenuItem *self, priv->time = time; str = im_source_menu_item_time_span_string (priv->time); - gtk_label_set_text (GTK_LABEL (priv->detail), str); - - g_free (str); -} - -static void -im_source_menu_item_set_detail (ImSourceMenuItem *self, - const gchar *detail) -{ - ImSourceMenuItemPrivate *priv = self->priv; - gchar *str; - - str = collapse_whitespace (detail); - gtk_label_set_text (GTK_LABEL (priv->detail), str); + ido_detail_label_set_text (IDO_DETAIL_LABEL (priv->detail), str); g_free (str); } @@ -217,14 +148,14 @@ im_source_menu_item_set_state (ImSourceMenuItem *self, g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); if (count != 0) - im_source_menu_item_set_detail_count (self, count); + ido_detail_label_set_count (IDO_DETAIL_LABEL (priv->detail), count); else if (time != 0) { im_source_menu_item_set_detail_time (self, time); priv->timer_id = g_timeout_add_seconds (59, im_source_menu_item_update_time, self); } else if (str != NULL && *str) - im_source_menu_item_set_detail (self, str); + ido_detail_label_set_text (IDO_DETAIL_LABEL (priv->detail), str); return TRUE; } -- cgit v1.2.3 From a83f13375bc72d0d96523295bd9e76ab75af9255 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 3 Sep 2012 15:45:33 +0200 Subject: im-source-menu-item: ellipsize label when it's longer than 40 em Also make sure that the label is left-aligned when ellipsized. --- src/im-source-menu-item.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c index 5f89d07..775fa91 100644 --- a/src/im-source-menu-item.c +++ b/src/im-source-menu-item.c @@ -60,6 +60,9 @@ im_source_menu_item_constructed (GObject *object) gtk_widget_set_margin_left (priv->icon, icon_width + 6); priv->label = g_object_ref (gtk_label_new ("")); + gtk_label_set_max_width_chars (GTK_LABEL (priv->label), 40); + gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5); priv->detail = g_object_ref (ido_detail_label_new ("")); gtk_widget_set_halign (priv->detail, GTK_ALIGN_END); -- cgit v1.2.3 From 67eb5bea332a35aa1c02984d5a719c696a3e2309 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 3 Sep 2012 16:00:46 +0200 Subject: Update bzrignore --- .bzrignore | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/.bzrignore b/.bzrignore index b3415fa..1163970 100644 --- a/.bzrignore +++ b/.bzrignore @@ -11,6 +11,9 @@ Makefile.in.in stamp-* *.gcno *.gcda +*.stamp +*.gir +*.typelib /COPYING /INSTALL @@ -27,6 +30,8 @@ stamp-* /mkinstalldirs /coverage.info /coveragereport +/gtk-doc.make +/indicator-messages-*.tar.gz /m4/* /po/POTFILES @@ -34,17 +39,27 @@ stamp-* /src/indicator-messages-service /src/indicator-messages-service.c /src/indicator-messages-service.h -/src/status-provider-mc5-marshal.c -/src/status-provider-mc5-marshal.h -/src/status-provider-pidgin-marshal.c -/src/status-provider-pidgin-marshal.h -/src/status-provider-telepathy-marshal.c -/src/status-provider-telepathy-marshal.h -/src/indicator-messages-status-provider-0.5.pc -/src/indicator-messages-status-provider-0.5.pc.in /libmessaging-menu/messaging-menu.pc /libmessaging-menu/indicator-messages-service.c /libmessaging-menu/indicator-messages-service.h -test/test-gactionmuxer +/doc/reference/html +/doc/reference/xml +/doc/reference/messaging-menu-decl-list.txt +/doc/reference/messaging-menu-decl.txt +/doc/reference/messaging-menu-docs.sgml +/doc/reference/messaging-menu-docs.xml +/doc/reference/messaging-menu-overrides.txt +/doc/reference/messaging-menu-sections.txt +/doc/reference/messaging-menu-undeclared.txt +/doc/reference/messaging-menu-undocumented.txt +/doc/reference/messaging-menu-unused.txt +/doc/reference/messaging-menu.args +/doc/reference/messaging-menu.hierarchy +/doc/reference/messaging-menu.interfaces +/doc/reference/messaging-menu.prerequisites +/doc/reference/messaging-menu.signals +/doc/reference/messaging-menu.types + +/test/test-gactionmuxer -- cgit v1.2.3 From b05f7b89e3fd4b61787214742dd7c8597718dc9a Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 4 Sep 2012 10:25:27 +0200 Subject: Hide indicator when no applications are configured to use it This is done by removing the header item from the menu model in the service. On the panel side, call indicator_object_hide() if the recevied menu is empty. --- src/indicator-messages.c | 99 ++++++++++++++++++++++++------------------------ src/messages-service.c | 39 ++++++++++++------- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/src/indicator-messages.c b/src/indicator-messages.c index 942e46f..b802cfe 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -83,8 +83,6 @@ static GtkImage * get_image (IndicatorObject * io); static GtkMenu * get_menu (IndicatorObject * io); static const gchar * get_accessible_desc (IndicatorObject * io); static const gchar * get_name_hint (IndicatorObject * io); -static void update_root_item (IndicatorMessages * self); -static void update_menu (IndicatorMessages *self); static void menu_items_changed (GMenuModel *menu, gint position, gint removed, @@ -212,8 +210,10 @@ static void service_connection_changed (IndicatorServiceManager *sm, INDICATOR_MESSAGES_DBUS_OBJECT)); g_signal_connect (self->menu, "items-changed", G_CALLBACK (menu_items_changed), self); - update_root_item (self); - update_menu (self); + if (g_menu_model_get_n_items (self->menu) == 1) + menu_items_changed (self->menu, 0, 0, 1, self); + else + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); g_object_unref (bus); } @@ -261,24 +261,19 @@ indicator_messages_accessible_desc_updated (IndicatorMessages *self) g_list_free (entries); } -static void -update_root_item (IndicatorMessages * self) +static GIcon * +g_menu_model_get_item_attribute_icon (GMenuModel *menu, + gint index, + const gchar *attribute) { gchar *iconstr; + GIcon *icon = NULL; - if (g_menu_model_get_n_items (self->menu) == 0) - return; - - if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-icon", "s", &iconstr)) { - GIcon *icon; + if (g_menu_model_get_item_attribute (menu, index, attribute, "s", &iconstr)) { GError *error; icon = g_icon_new_for_string (iconstr, &error); - if (icon) { - gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU); - g_object_unref (icon); - } - else { + if (icon == NULL) { g_warning ("unable to load icon: %s", error->message); g_error_free (error); } @@ -286,37 +281,7 @@ update_root_item (IndicatorMessages * self) g_free (iconstr); } - g_free (self->accessible_desc); - self->accessible_desc = NULL; - - g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description", - "s", &self->accessible_desc); - indicator_messages_accessible_desc_updated (self); -} - -static void -update_menu (IndicatorMessages *self) -{ - GMenuModel *popup; - GMenuItem *item; - - if (self->menu == NULL || g_menu_model_get_n_items (self->menu) == 0) - return; - - popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); - if (popup == NULL) - return; - - if (g_menu_model_get_n_items (G_MENU_MODEL (self->menu_wrapper)) == 1) - g_menu_remove (self->menu_wrapper, 0); - - item = g_menu_item_new_section (NULL, popup); - g_menu_item_set_attribute (item, "action-namespace", - "s", get_name_hint (INDICATOR_OBJECT (self))); - g_menu_append_item (self->menu_wrapper, item); - - g_object_unref (item); - g_object_unref (popup); + return icon; } static void @@ -328,9 +293,43 @@ menu_items_changed (GMenuModel *menu, { IndicatorMessages *self = user_data; - if (position == 0) { - update_root_item (self); - update_menu (self); + g_return_if_fail (position == 0); + + if (added == 1) { + GIcon *icon; + GMenuModel *popup; + + indicator_object_set_visible (INDICATOR_OBJECT (self), TRUE); + + icon = g_menu_model_get_item_attribute_icon (menu, 0, "x-canonical-icon"); + if (icon) { + gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU); + g_object_unref (icon); + } + + g_free (self->accessible_desc); + self->accessible_desc = NULL; + if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description", + "s", &self->accessible_desc)) { + indicator_messages_accessible_desc_updated (self); + } + + popup = g_menu_model_get_item_link (menu, 0, G_MENU_LINK_SUBMENU); + if (popup) { + GMenuItem *item; + + item = g_menu_item_new_section (NULL, popup); + g_menu_item_set_attribute (item, "action-namespace", + "s", get_name_hint (INDICATOR_OBJECT (self))); + g_menu_append_item (self->menu_wrapper, item); + + g_object_unref (item); + g_object_unref (popup); + } + } + else if (removed == 1) { + g_menu_remove (self->menu_wrapper, 0); + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); } } diff --git a/src/messages-service.c b/src/messages-service.c index 15c5123..d016ff1 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -175,6 +175,25 @@ add_application (const gchar *desktop_id) g_object_unref (menuitem); } + if (g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 0) { + GMenuItem *header; + GIcon *icon; + gchar *iconstr; + + icon = g_themed_icon_new ("indicator-messages"); + iconstr = g_icon_to_string (icon); + + header = g_menu_item_new (NULL, "messages"); + g_menu_item_set_submenu (header, G_MENU_MODEL (menu)); + g_menu_item_set_attribute (header, "x-canonical-icon", "s", iconstr); + g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages")); + g_menu_append_item (toplevel_menu, header); + + g_object_unref (header); + g_free (iconstr); + g_object_unref (icon); + } + g_free (id); g_object_unref (appinfo); return section; @@ -211,6 +230,12 @@ remove_application (const char *desktop_id) } g_hash_table_remove (applications, id); + + if (g_hash_table_size (applications) == 0 && + g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 1) { + g_menu_remove (toplevel_menu, 0); + } + g_free (id); g_object_unref (appinfo); } @@ -484,9 +509,6 @@ main (int argc, char ** argv) { GMainLoop * mainloop = NULL; IndicatorService * service = NULL; - GMenuItem *header; - GIcon *icon; - gchar *iconstr; /* Glib init */ g_type_init(); @@ -524,16 +546,7 @@ main (int argc, char ** argv) chat_section = create_status_section (); g_menu_append (menu, _("Clear"), "clear"); - icon = g_themed_icon_new ("indicator-messages"); - iconstr = g_icon_to_string (icon); - toplevel_menu = g_menu_new (); - header = g_menu_item_new (NULL, "messages"); - g_menu_item_set_submenu (header, G_MENU_MODEL (menu)); - g_menu_item_set_attribute (header, "x-canonical-icon", "s", iconstr); - g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages")); - g_menu_append_item (toplevel_menu, header); - g_object_unref (header); settings = g_settings_new ("com.canonical.indicator.messages"); @@ -544,8 +557,6 @@ main (int argc, char ** argv) g_main_loop_run(mainloop); /* Clean up */ - g_free (iconstr); - g_object_unref (icon); g_object_unref (messages_service); g_object_unref (chat_section); g_object_unref (settings); -- cgit v1.2.3 From 4f9764785ecaf4bb4b43211340eeb37741c09ccc Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 4 Sep 2012 19:08:05 +0200 Subject: ido-detail-label: chain up dispose and finalize calls --- src/ido-detail-label.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ido-detail-label.c b/src/ido-detail-label.c index 5bfe884..2b996c9 100644 --- a/src/ido-detail-label.c +++ b/src/ido-detail-label.c @@ -84,6 +84,8 @@ ido_detail_label_finalize (GObject *object) IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv; g_free (priv->text); + + G_OBJECT_CLASS (ido_detail_label_parent_class)->finalize (object); } static void @@ -92,6 +94,8 @@ ido_detail_label_dispose (GObject *object) IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv; g_clear_object (&priv->layout); + + G_OBJECT_CLASS (ido_detail_label_parent_class)->dispose (object); } static void -- cgit v1.2.3 From 6a496d6b6b4a3a102d8177fadd0b2faeb8ac646a Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 4 Sep 2012 19:20:20 +0200 Subject: ido-detail-label: factor common code out of set_text and _count This was only half-heartedly done with the _clear function, which left a dangling pointer. Contributed by Charles Kerr. Thanks! --- src/ido-detail-label.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/ido-detail-label.c b/src/ido-detail-label.c index 2b996c9..780a2dd 100644 --- a/src/ido-detail-label.c +++ b/src/ido-detail-label.c @@ -309,13 +309,6 @@ ido_detail_label_get_text (IdoDetailLabel *label) return label->priv->text; } -static void -ido_detail_label_clear (IdoDetailLabel *label) -{ - g_free (label->priv->text); - g_clear_object (&label->priv->layout); -} - /* collapse_whitespace: * @str: the source string * @@ -359,38 +352,45 @@ collapse_whitespace (const gchar *str) return g_string_free (result, FALSE); } +static void +ido_detail_label_set_text_impl (IdoDetailLabel *label, + const gchar *text, + gboolean draw_lozenge) +{ + IdoDetailLabelPrivate * priv = label->priv; + + g_clear_object (&priv->layout); + g_free (priv->text); + + priv->text = g_strdup (text); + priv->draw_lozenge = draw_lozenge; + + g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]); + gtk_widget_queue_resize (GTK_WIDGET (label)); +} + void ido_detail_label_set_text (IdoDetailLabel *label, const gchar *text) { - IdoDetailLabelPrivate *priv; + gchar *str; g_return_if_fail (IDO_IS_DETAIL_LABEL (label)); - priv = label->priv; - ido_detail_label_clear (label); - - priv->text = collapse_whitespace (text); - label->priv->draw_lozenge = FALSE; - - g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]); - gtk_widget_queue_resize (GTK_WIDGET (label)); + str = collapse_whitespace (text); + ido_detail_label_set_text_impl (label, str, FALSE); + g_free (str); } void ido_detail_label_set_count (IdoDetailLabel *label, gint count) { - IdoDetailLabelPrivate *priv; + gchar *text; g_return_if_fail (IDO_IS_DETAIL_LABEL (label)); - priv = label->priv; - - ido_detail_label_clear (label); - priv->text = g_strdup_printf ("%d", count); - label->priv->draw_lozenge = TRUE; - - g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]); - gtk_widget_queue_resize (GTK_WIDGET (label)); + text = g_strdup_printf ("%d", count); + ido_detail_label_set_text_impl (label, text, TRUE); + g_free (text); } -- cgit v1.2.3 From bd13abddd0740eb9cb0859c4a12cdd77d4819d21 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 4 Sep 2012 20:49:44 +0200 Subject: libmessaging-menu: fix shortcut action documentation --- libmessaging-menu/messaging-menu.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index ebddf0f..8066b35 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -43,12 +43,13 @@ * desktop file. Activating this item starts the application. * * Following the application item, the Messaging Menu inserts all - * shortcuts actions found in the desktop file which are marked as - * appearing in the Messaging Menu (the TargetEnvironment or OnlyShowIn - * keywords contains "Messaging Menu"). The NotShowIn keyword contains "Messaging Menu" or whose + * OnlyShowIn keyword does not contain "Messaging Menu" + * will not appear (the * desktop file specification contains a detailed explanation of - * shortcut actions [1]. An application cannot add, remove, or change + * shortcut actions.) An application cannot add, remove, or change * these shortcut items while it is running. * * Next, an application section contains menu items for message sources. -- cgit v1.2.3 From af9183664c534c39d12e34b5c8e7c14ea189ce8b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 5 Sep 2012 15:21:16 +0200 Subject: Watch desktop files for changes This introduces a slightly clumsy "destroy" signal for AppSection to notify outsiders that the desktop file was deleted. This will do for now, but a larger refactoring which pulls all the desktop-file-reading code out of appsection is in order. --- src/app-section.c | 121 ++++++++++++++++++++++++++++++++++++++++++------- src/messages-service.c | 34 ++++++++++---- 2 files changed, 129 insertions(+), 26 deletions(-) diff --git a/src/app-section.c b/src/app-section.c index bed1302..58f1612 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -37,6 +37,7 @@ with this program. If not, see . struct _AppSectionPrivate { GDesktopAppInfo * appinfo; + GFileMonitor *desktop_file_monitor; guint unreadcount; IndicatorDesktopShortcuts * ids; @@ -64,6 +65,7 @@ enum { }; static GParamSpec *properties[NUM_PROPERTIES]; +guint destroy_signal; /* Prototypes */ static void app_section_class_init (AppSectionClass *klass); @@ -98,6 +100,11 @@ static void action_removed (GActionGroup *group, const gchar *action_name, gpointer user_data); static gboolean action_draws_attention (GVariant *state); +static void desktop_file_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer user_data); /* GObject Boilerplate */ G_DEFINE_TYPE (AppSection, app_section, G_TYPE_OBJECT); @@ -138,6 +145,14 @@ app_section_class_init (AppSectionClass *klass) G_PARAM_READABLE); g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); + + destroy_signal = g_signal_new ("destroy", + APP_SECTION_TYPE, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -209,12 +224,18 @@ app_section_set_property (GObject *object, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } + static void app_section_dispose (GObject *object) { AppSection * self = APP_SECTION(object); AppSectionPrivate * priv = self->priv; + if (priv->desktop_file_monitor) { + g_signal_handlers_disconnect_by_func (priv->desktop_file_monitor, desktop_file_changed_cb, self); + g_clear_object (&priv->desktop_file_monitor); + } + g_clear_object (&priv->menu); g_clear_object (&priv->static_shortcuts); @@ -289,6 +310,9 @@ keyfile_loaded (GObject *source_object, G_KEY_FILE_DESKTOP_GROUP, "X-MessagingMenu-UsesChatSection", &error); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]); + if (error) { if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { g_warning ("could not read X-MessagingMenu-UsesChatSection: %s", @@ -298,34 +322,48 @@ keyfile_loaded (GObject *source_object, goto out; } - if (self->priv->uses_chat_status) - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]); - out: g_key_file_free (keyfile); g_free (contents); } static void -app_section_set_app_info (AppSection *self, - GDesktopAppInfo *appinfo) +g_menu_clear (GMenu *menu) +{ + gint n_items = g_menu_model_get_n_items (G_MENU_MODEL (menu)); + + while (n_items--) + g_menu_remove (menu, 0); +} + +static void +g_simple_action_group_clear (GSimpleActionGroup *group) +{ + gchar **actions; + gchar **it; + + actions = g_action_group_list_actions (G_ACTION_GROUP (group)); + for (it = actions; *it; it++) + g_simple_action_group_remove (group, *it); + + g_strfreev (actions); +} + +static void +app_section_update_menu (AppSection *self) { AppSectionPrivate *priv = self->priv; GSimpleAction *launch; GFile *keyfile; GMenuItem *item; gchar *iconstr; + gboolean is_running; - g_return_if_fail (priv->appinfo == NULL); + g_menu_clear (priv->menu); + g_simple_action_group_clear (priv->static_shortcuts); - if (appinfo == NULL) { - g_warning ("appinfo must not be NULL"); - return; - } - - priv->appinfo = g_object_ref (appinfo); - - launch = g_simple_action_new_stateful ("launch", NULL, g_variant_new_boolean (FALSE)); + is_running = priv->name_watch_id > 0; + launch = g_simple_action_new_stateful ("launch", NULL, g_variant_new_boolean (is_running)); g_signal_connect (launch, "activate", G_CALLBACK (activate_cb), self); g_signal_connect (launch, "change-state", G_CALLBACK (launch_action_change_state), self); g_simple_action_group_insert (priv->static_shortcuts, G_ACTION (launch)); @@ -366,14 +404,65 @@ app_section_set_app_info (AppSection *self, keyfile = g_file_new_for_path (g_desktop_app_info_get_filename (priv->appinfo)); g_file_load_contents_async (keyfile, NULL, keyfile_loaded, self); - g_object_unref (keyfile); - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_APPINFO]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]); + g_object_unref (keyfile); g_object_unref (launch); } +static void +desktop_file_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer user_data) +{ + AppSection *self = user_data; + + if (event == G_FILE_MONITOR_EVENT_CHANGED) { + app_section_update_menu (self); + } + else if (event == G_FILE_MONITOR_EVENT_DELETED || + event == G_FILE_MONITOR_EVENT_UNMOUNTED) { + g_signal_emit (self, destroy_signal, 0); + } +} + +static void +app_section_set_app_info (AppSection *self, + GDesktopAppInfo *appinfo) +{ + AppSectionPrivate *priv = self->priv; + GFile *desktop_file; + GError *error = NULL; + + g_return_if_fail (priv->appinfo == NULL); + g_return_if_fail (priv->desktop_file_monitor == NULL); + + if (appinfo == NULL) { + g_warning ("appinfo must not be NULL"); + return; + } + + priv->appinfo = g_object_ref (appinfo); + + desktop_file = g_file_new_for_path (g_desktop_app_info_get_filename (appinfo)); + priv->desktop_file_monitor = g_file_monitor (desktop_file, G_FILE_MONITOR_SEND_MOVED, NULL, &error); + if (priv->desktop_file_monitor == NULL) { + g_warning ("unable to watch desktop file: %s", error->message); + g_error_free (error); + } + g_signal_connect (priv->desktop_file_monitor, "changed", + G_CALLBACK (desktop_file_changed_cb), self); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_APPINFO]); + + app_section_update_menu (self); + + g_object_unref (desktop_file); +} + AppSection * app_section_new (GDesktopAppInfo *appinfo) { diff --git a/src/messages-service.c b/src/messages-service.c index 15c5123..ed9bfe4 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -45,7 +45,6 @@ static GMenu *menu; static GMenuModel *chat_section; static GSettings *settings; - static gchar * g_app_info_get_simple_id (GAppInfo *appinfo) { @@ -138,6 +137,23 @@ uses_chat_status_changed (GObject *object, g_object_unref (first_section); } +static void +remove_section (AppSection *section, + const gchar *id) +{ + int pos = g_menu_find_section (menu, app_section_get_menu (section)); + if (pos >= 0) + g_menu_remove (menu, pos); + g_action_muxer_remove (action_muxer, id); + + g_signal_handlers_disconnect_by_func (section, actions_changed, NULL); + g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL); + g_signal_handlers_disconnect_by_func (section, uses_chat_status_changed, NULL); + g_signal_handlers_disconnect_by_func (section, remove_section, NULL); + + g_hash_table_remove (applications, id); +} + static AppSection * add_application (const gchar *desktop_id) { @@ -167,6 +183,11 @@ add_application (const gchar *desktop_id) G_CALLBACK (draws_attention_changed), NULL); g_signal_connect (section, "notify::uses-chat-status", G_CALLBACK (uses_chat_status_changed), NULL); + g_signal_connect_data (section, "destroy", + G_CALLBACK (remove_section), + g_strdup (id), + (GClosureNotify) g_free, + 0); /* TODO insert it at the right position (alphabetically by application name) */ menuitem = g_menu_item_new_section (NULL, app_section_get_menu (section)); @@ -197,20 +218,13 @@ remove_application (const char *desktop_id) section = g_hash_table_lookup (applications, id); if (section) { - int pos = g_menu_find_section (menu, app_section_get_menu (section)); - if (pos >= 0) - g_menu_remove (menu, pos); - g_action_muxer_remove (action_muxer, id); - - g_signal_handlers_disconnect_by_func (section, actions_changed, NULL); - g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL); - g_signal_handlers_disconnect_by_func (section, uses_chat_status_changed, NULL); + remove_section (section, id); } else { + g_hash_table_remove (applications, id); g_warning ("could not remove '%s', it's not registered", desktop_id); } - g_hash_table_remove (applications, id); g_free (id); g_object_unref (appinfo); } -- cgit v1.2.3 From 45adbc05cf2b51986f8cfc085fe71d061c7fc406 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 5 Sep 2012 15:27:09 +0200 Subject: app-section: remove unused private member --- src/app-section.c | 10 ---------- src/app-section.h | 1 - 2 files changed, 11 deletions(-) diff --git a/src/app-section.c b/src/app-section.c index 58f1612..df79582 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -38,7 +38,6 @@ struct _AppSectionPrivate { GDesktopAppInfo * appinfo; GFileMonitor *desktop_file_monitor; - guint unreadcount; IndicatorDesktopShortcuts * ids; @@ -166,7 +165,6 @@ app_section_init (AppSection *self) priv = self->priv; priv->appinfo = NULL; - priv->unreadcount = 0; priv->menu = g_menu_new (); priv->static_shortcuts = g_simple_action_group_new (); @@ -496,14 +494,6 @@ launch_action_change_state (GSimpleAction *action, g_simple_action_set_state (action, value); } -guint -app_section_get_count (AppSection * self) -{ - AppSectionPrivate * priv = self->priv; - - return priv->unreadcount; -} - const gchar * app_section_get_name (AppSection * self) { diff --git a/src/app-section.h b/src/app-section.h index 711fdc9..32ebbf0 100644 --- a/src/app-section.h +++ b/src/app-section.h @@ -51,7 +51,6 @@ struct _AppSection { GType app_section_get_type (void); AppSection * app_section_new (GDesktopAppInfo *appinfo); -guint app_section_get_count (AppSection * appitem); const gchar * app_section_get_name (AppSection * appitem); const gchar * app_section_get_desktop (AppSection * appitem); GActionGroup * app_section_get_actions (AppSection *self); -- cgit v1.2.3 From 014f841f7cd5a1a665da7fc19c775104ea252ab4 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 5 Sep 2012 21:04:04 +0200 Subject: indicator-messages.c: don't call menu_items_changed directly --- src/indicator-messages.c | 69 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/indicator-messages.c b/src/indicator-messages.c index b802cfe..29979ed 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -92,6 +92,7 @@ static void messages_state_changed (GActionGroup *action_group, gchar *action_name, GVariant *value, gpointer user_data); +static void indicator_messages_add_toplevel_menu (IndicatorMessages *self); G_DEFINE_TYPE (IndicatorMessages, indicator_messages, INDICATOR_OBJECT_TYPE); @@ -211,7 +212,7 @@ static void service_connection_changed (IndicatorServiceManager *sm, g_signal_connect (self->menu, "items-changed", G_CALLBACK (menu_items_changed), self); if (g_menu_model_get_n_items (self->menu) == 1) - menu_items_changed (self->menu, 0, 0, 1, self); + indicator_messages_add_toplevel_menu (self); else indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); @@ -284,6 +285,41 @@ g_menu_model_get_item_attribute_icon (GMenuModel *menu, return icon; } +static void +indicator_messages_add_toplevel_menu (IndicatorMessages *self) +{ + GIcon *icon; + GMenuModel *popup; + + indicator_object_set_visible (INDICATOR_OBJECT (self), TRUE); + + icon = g_menu_model_get_item_attribute_icon (self->menu, 0, "x-canonical-icon"); + if (icon) { + gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU); + g_object_unref (icon); + } + + g_free (self->accessible_desc); + self->accessible_desc = NULL; + if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description", + "s", &self->accessible_desc)) { + indicator_messages_accessible_desc_updated (self); + } + + popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); + if (popup) { + GMenuItem *item; + + item = g_menu_item_new_section (NULL, popup); + g_menu_item_set_attribute (item, "action-namespace", + "s", get_name_hint (INDICATOR_OBJECT (self))); + g_menu_append_item (self->menu_wrapper, item); + + g_object_unref (item); + g_object_unref (popup); + } +} + static void menu_items_changed (GMenuModel *menu, gint position, @@ -296,36 +332,7 @@ menu_items_changed (GMenuModel *menu, g_return_if_fail (position == 0); if (added == 1) { - GIcon *icon; - GMenuModel *popup; - - indicator_object_set_visible (INDICATOR_OBJECT (self), TRUE); - - icon = g_menu_model_get_item_attribute_icon (menu, 0, "x-canonical-icon"); - if (icon) { - gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU); - g_object_unref (icon); - } - - g_free (self->accessible_desc); - self->accessible_desc = NULL; - if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description", - "s", &self->accessible_desc)) { - indicator_messages_accessible_desc_updated (self); - } - - popup = g_menu_model_get_item_link (menu, 0, G_MENU_LINK_SUBMENU); - if (popup) { - GMenuItem *item; - - item = g_menu_item_new_section (NULL, popup); - g_menu_item_set_attribute (item, "action-namespace", - "s", get_name_hint (INDICATOR_OBJECT (self))); - g_menu_append_item (self->menu_wrapper, item); - - g_object_unref (item); - g_object_unref (popup); - } + indicator_messages_add_toplevel_menu (self); } else if (removed == 1) { g_menu_remove (self->menu_wrapper, 0); -- cgit v1.2.3 From 10d5f6214a815d401db7a3ca3f9fc3de8d68cd16 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 5 Sep 2012 21:11:39 +0200 Subject: app-section.c: make destroy_signal static --- src/app-section.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app-section.c b/src/app-section.c index df79582..9bf5087 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -64,7 +64,7 @@ enum { }; static GParamSpec *properties[NUM_PROPERTIES]; -guint destroy_signal; +static guint destroy_signal; /* Prototypes */ static void app_section_class_init (AppSectionClass *klass); -- cgit v1.2.3 From d1da13ed474cfe3053d65833bb4ec2e61f47b440 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 5 Sep 2012 21:13:07 +0200 Subject: app-section.c: remove unused function app_section_get_name --- src/app-section.c | 11 ----------- src/app-section.h | 1 - 2 files changed, 12 deletions(-) diff --git a/src/app-section.c b/src/app-section.c index 9bf5087..523e249 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -494,17 +494,6 @@ launch_action_change_state (GSimpleAction *action, g_simple_action_set_state (action, value); } -const gchar * -app_section_get_name (AppSection * self) -{ - AppSectionPrivate * priv = self->priv; - - if (priv->appinfo) { - return g_app_info_get_name(G_APP_INFO(priv->appinfo)); - } - return NULL; -} - const gchar * app_section_get_desktop (AppSection * self) { diff --git a/src/app-section.h b/src/app-section.h index 32ebbf0..09697c5 100644 --- a/src/app-section.h +++ b/src/app-section.h @@ -51,7 +51,6 @@ struct _AppSection { GType app_section_get_type (void); AppSection * app_section_new (GDesktopAppInfo *appinfo); -const gchar * app_section_get_name (AppSection * appitem); const gchar * app_section_get_desktop (AppSection * appitem); GActionGroup * app_section_get_actions (AppSection *self); GMenuModel * app_section_get_menu (AppSection *appitem); -- cgit v1.2.3 From bdf943c3c2d9aef1511d24e928e9d8652a17e703 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 5 Sep 2012 21:25:28 +0200 Subject: messages-service.c: remove redundant g_hash_table_remove --- src/messages-service.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/messages-service.c b/src/messages-service.c index ed9bfe4..acf4995 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -221,7 +221,6 @@ remove_application (const char *desktop_id) remove_section (section, id); } else { - g_hash_table_remove (applications, id); g_warning ("could not remove '%s', it's not registered", desktop_id); } -- cgit v1.2.3 From d7ad8baeb10ae9ca505156ae715f372a1c18151d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 6 Sep 2012 14:58:22 +0200 Subject: messages-service: fix merge error Check whether no apps are left in remove_section instead of remove_application, so that it is also done when an AppSection emits "destroy". This is a result of improper merging of the watch-desktop-files and hide-indicator branches. --- src/messages-service.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/messages-service.c b/src/messages-service.c index 437f046..7cc037b 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -152,6 +152,11 @@ remove_section (AppSection *section, g_signal_handlers_disconnect_by_func (section, remove_section, NULL); g_hash_table_remove (applications, id); + + if (g_hash_table_size (applications) == 0 && + g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 1) { + g_menu_remove (toplevel_menu, 0); + } } static AppSection * @@ -243,13 +248,6 @@ remove_application (const char *desktop_id) g_warning ("could not remove '%s', it's not registered", desktop_id); } - g_hash_table_remove (applications, id); - - if (g_hash_table_size (applications) == 0 && - g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 1) { - g_menu_remove (toplevel_menu, 0); - } - g_free (id); g_object_unref (appinfo); } -- cgit v1.2.3 From 894c9d2e239a4ecc99319fa2517b6d68bdd72774 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 6 Sep 2012 15:14:00 +0200 Subject: messages-service: update chat section when an app is removed --- src/messages-service.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/messages-service.c b/src/messages-service.c index 7cc037b..bc9ac1b 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -115,9 +115,7 @@ app_section_uses_chat (gpointer key, } static void -uses_chat_status_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) +update_chat_section () { gboolean show_chat; GMenuModel *first_section; @@ -137,6 +135,14 @@ uses_chat_status_changed (GObject *object, g_object_unref (first_section); } +static void +uses_chat_status_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + update_chat_section (); +} + static void remove_section (AppSection *section, const gchar *id) @@ -157,6 +163,8 @@ remove_section (AppSection *section, g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 1) { g_menu_remove (toplevel_menu, 0); } + + update_chat_section (); } static AppSection * -- cgit v1.2.3 From c533fedbc822c22eb30543dbe605c705e95c3920 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 13 Sep 2012 22:46:34 +0200 Subject: Show menu separators between sections Previously, gtk didn't support changing the menu model on an already-created GtkMenu. Since IndicatorObject doesn't allow changing the GtkMenu it exports to the panel, IndicatorMessages created a menu with a single section, into which the menu from the service was inserted (and removed when the service died). This led to seperators not being shown, because separators are only shown between top-level sections. Gtk now has gtk_menu_shell_bind_model, which allows us to get rid of top-level wrapper section. --- configure.ac | 2 +- src/indicator-messages.c | 26 ++++++++------------------ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 37268a2..b3e7a13 100644 --- a/configure.ac +++ b/configure.ac @@ -38,7 +38,7 @@ AC_PROG_CXX # Dependencies ########################### -GTK_REQUIRED_VERSION=3.5.12 +GTK_REQUIRED_VERSION=3.5.17 GIO_UNIX_REQUIRED_VERSION=2.33.10 INDICATOR_REQUIRED_VERSION=0.3.19 GLIB_REQUIRED_VERSION=2.33.10 diff --git a/src/indicator-messages.c b/src/indicator-messages.c index 29979ed..2647a76 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -58,7 +58,6 @@ struct _IndicatorMessages { IndicatorObject parent; IndicatorServiceManager * service; GActionGroup *actions; - GMenu *menu_wrapper; GMenuModel *menu; GtkWidget *image; GtkWidget *gtkmenu; @@ -121,8 +120,7 @@ indicator_messages_init (IndicatorMessages *self) g_signal_connect (self->service, "connection-change", G_CALLBACK (service_connection_changed), self); - self->menu_wrapper = g_menu_new (); - self->gtkmenu = gtk_menu_new_from_model (G_MENU_MODEL (self->menu_wrapper)); + self->gtkmenu = gtk_menu_new (); g_object_ref_sink (self->gtkmenu); self->image = g_object_ref_sink (gtk_image_new ()); @@ -142,7 +140,6 @@ indicator_messages_dispose (GObject *object) g_return_if_fail(self != NULL); g_clear_object (&self->service); - g_clear_object (&self->menu_wrapper); g_clear_object (&self->actions); g_clear_object (&self->menu); g_clear_object (&self->gtkmenu); @@ -184,8 +181,7 @@ static void service_connection_changed (IndicatorServiceManager *sm, g_signal_handlers_disconnect_by_func (self->menu, menu_items_changed, self); g_clear_object (&self->menu); } - if (g_menu_model_get_n_items (G_MENU_MODEL (self->menu_wrapper)) == 1) - g_menu_remove (self->menu_wrapper, 0); + gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->gtkmenu), NULL, NULL, FALSE); if (connected == FALSE) return; @@ -308,14 +304,11 @@ indicator_messages_add_toplevel_menu (IndicatorMessages *self) popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); if (popup) { - GMenuItem *item; + gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->gtkmenu), + popup, + get_name_hint (INDICATOR_OBJECT (self)), + TRUE); - item = g_menu_item_new_section (NULL, popup); - g_menu_item_set_attribute (item, "action-namespace", - "s", get_name_hint (INDICATOR_OBJECT (self))); - g_menu_append_item (self->menu_wrapper, item); - - g_object_unref (item); g_object_unref (popup); } } @@ -331,13 +324,10 @@ menu_items_changed (GMenuModel *menu, g_return_if_fail (position == 0); - if (added == 1) { + if (added == 1) indicator_messages_add_toplevel_menu (self); - } - else if (removed == 1) { - g_menu_remove (self->menu_wrapper, 0); + else if (removed == 1) indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); - } } static void -- cgit v1.2.3 From 8b7183be4edd964fe2dad738962e1b1156300668 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 14 Sep 2012 08:59:34 +0200 Subject: messages-services: don't call g_object_unref with NULL --- src/messages-service.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/messages-service.c b/src/messages-service.c index bc9ac1b..fd0bdcb 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -132,7 +132,8 @@ update_chat_section () g_menu_insert_section (menu, 0, NULL, chat_section); } - g_object_unref (first_section); + if (first_section != NULL) + g_object_unref (first_section); } static void -- cgit v1.2.3 From 624b6e5dd7bd32fc3f57b8c423c52f0d6738d0a7 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Sep 2012 16:10:15 +0200 Subject: libmessaging-menu: don't call SetStatus(OFFLINE) for all apps Only notify the service about status if the application has actually called messaging_menu_app_set_status. This saves a d-bus call per non-chat application and - more importantly - doesn't make the messaging menu go into "unknown status" mode when one application is reporting 'online' status. --- libmessaging-menu/messaging-menu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 8066b35..fe2583b 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -102,6 +102,7 @@ struct _MessagingMenuApp GDesktopAppInfo *appinfo; int registered; /* -1 for unknown */ MessagingMenuStatus status; + gboolean status_set; GSimpleActionGroup *source_actions; GMenu *menu; @@ -370,7 +371,8 @@ created_messages_service (GObject *source_object, messaging_menu_app_register (app); else if (app->registered == FALSE) messaging_menu_app_unregister (app); - messaging_menu_app_set_status (app, app->status); + if (app->status_set) + messaging_menu_app_set_status (app, app->status); } static void @@ -410,7 +412,7 @@ static void messaging_menu_app_init (MessagingMenuApp *app) { app->registered = -1; - app->status = MESSAGING_MENU_STATUS_OFFLINE; + app->status_set = FALSE; app->cancellable = g_cancellable_new (); @@ -538,6 +540,7 @@ messaging_menu_app_set_status (MessagingMenuApp *app, status <= MESSAGING_MENU_STATUS_OFFLINE); app->status = status; + app->status_set = TRUE; /* state will be synced right after connecting to the service */ if (!app->messages_service) -- cgit v1.2.3 From da9fde78ca62bd0b05dfbee3e024fd776c29c373 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Sep 2012 16:17:57 +0200 Subject: libmessaging-menu: don't set app->status when global status changes The application's status only changes when it calls _set_status, so it's wrong to set the internal status when the global status changes. This shouldn't be a problem in practice, as app->status is not accessible from the API. --- libmessaging-menu/messaging-menu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index fe2583b..1b2e694 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -580,8 +580,7 @@ global_status_changed (IndicatorMessagesService *service, status = status_from_string (status_str); g_return_if_fail (status >= 0); - app->status = (MessagingMenuStatus)status; - g_signal_emit (app, signals[STATUS_CHANGED], 0, app->status); + g_signal_emit (app, signals[STATUS_CHANGED], 0, status); } static void -- cgit v1.2.3 From 24ee0b6602960f35712adbf668099e06b8ebfb25 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Sep 2012 21:54:37 +0200 Subject: Set the global chat status more intelligently Up until now, the global chat status was set every time an application called _set_status. Thus, global status really meant "status of the app that last changed the status". Now, the service remembers the chat status for each application and sets the global status as a combination of all of application statuses. If applications have different statuses, the menu items are shown in an inconsistent state. This is implemented in IdoMenuItem by making it accept state as an array of strings in addition to a single string. It is drawn inconsistent if the state contains the menu item's target value in addition to other values. When the global status is changed through the messaging menu, the service doesn't update the action immediately anymore. Instead, it notifies all applications about the change via the "status-changed" signal. Applications must call _set_state to acknowledge that they have indeed changed their state. This is consistent with libmessaging-menu's documentation and design. Also, the SetStatus D-Bus call was missing a "desktop-id" parameter to tell the menu which application changed status. Changing this doesn't break existing apps, as the D-Bus interface is considered private to indicator-messages. --- libmessaging-menu/messaging-menu.c | 1 + src/app-section.c | 51 +++++++++++++++ src/app-section.h | 3 + src/ido-menu-item.c | 34 +++++++++- src/messages-service.c | 125 ++++++++++++++++++++++++------------- src/messages-service.xml | 1 + 6 files changed, 168 insertions(+), 47 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 1b2e694..8a95b0a 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -547,6 +547,7 @@ messaging_menu_app_set_status (MessagingMenuApp *app, return; indicator_messages_service_call_set_status (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), status_ids [status], app->cancellable, NULL, NULL); diff --git a/src/app-section.c b/src/app-section.c index 523e249..6aac52a 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -50,6 +50,7 @@ struct _AppSectionPrivate gboolean draws_attention; gboolean uses_chat_status; + gchar *chat_status; guint name_watch_id; }; @@ -60,6 +61,7 @@ enum { PROP_ACTIONS, PROP_DRAWS_ATTENTION, PROP_USES_CHAT_STATUS, + PROP_CHAT_STATUS, NUM_PROPERTIES }; @@ -78,6 +80,7 @@ static void app_section_set_property (GObject *object, const GValue *value, GParamSpec *pspec); static void app_section_dispose (GObject *object); +static void app_section_finalize (GObject *object); static void activate_cb (GSimpleAction *action, GVariant *param, gpointer userdata); @@ -118,6 +121,7 @@ app_section_class_init (AppSectionClass *klass) object_class->get_property = app_section_get_property; object_class->set_property = app_section_set_property; object_class->dispose = app_section_dispose; + object_class->finalize = app_section_finalize; properties[PROP_APPINFO] = g_param_spec_object ("app-info", "AppInfo", @@ -143,6 +147,13 @@ app_section_class_init (AppSectionClass *klass) FALSE, G_PARAM_READABLE); + properties[PROP_CHAT_STATUS] = g_param_spec_string ("chat-status", + "Chat status", + "Current chat status of the application", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); destroy_signal = g_signal_new ("destroy", @@ -199,6 +210,10 @@ app_section_get_property (GObject *object, g_value_set_boolean (value, app_section_get_uses_chat_status (self)); break; + case PROP_CHAT_STATUS: + g_value_set_string (value, app_section_get_status (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -218,6 +233,10 @@ app_section_set_property (GObject *object, app_section_set_app_info (self, g_value_get_object (value)); break; + case PROP_CHAT_STATUS: + app_section_set_status (self, g_value_get_string (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -261,6 +280,16 @@ app_section_dispose (GObject *object) G_OBJECT_CLASS (app_section_parent_class)->dispose (object); } +static void +app_section_finalize (GObject *object) +{ + AppSection * self = APP_SECTION(object); + + g_free (self->priv->chat_status); + + G_OBJECT_CLASS (app_section_parent_class)->dispose (object); +} + /* Respond to one of the shortcuts getting clicked on. */ static void nick_activate_cb (GSimpleAction *action, @@ -668,10 +697,12 @@ app_section_unset_object_path (AppSection *self) } priv->draws_attention = FALSE; + g_clear_pointer (&priv->chat_status, g_free); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHAT_STATUS]); g_action_group_change_action_state (G_ACTION_GROUP (priv->static_shortcuts), "launch", g_variant_new_boolean (FALSE)); @@ -763,3 +794,23 @@ app_section_get_uses_chat_status (AppSection *self) return priv->uses_chat_status; } + +const gchar * +app_section_get_status (AppSection *self) +{ + AppSectionPrivate * priv = self->priv; + + return priv->chat_status; +} + +void +app_section_set_status (AppSection *self, + const gchar *status) +{ + AppSectionPrivate * priv = self->priv; + + g_free (priv->chat_status); + priv->chat_status = g_strdup (status); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHAT_STATUS]); +} diff --git a/src/app-section.h b/src/app-section.h index 09697c5..7c39e8e 100644 --- a/src/app-section.h +++ b/src/app-section.h @@ -63,6 +63,9 @@ void app_section_set_object_path (AppSection *self, const gchar *object_path); void app_section_unset_object_path (AppSection *self); gboolean app_section_get_uses_chat_status (AppSection *self); +const gchar * app_section_get_status (AppSection *self); +void app_section_set_status (AppSection *self, + const gchar *status); G_END_DECLS diff --git a/src/ido-menu-item.c b/src/ido-menu-item.c index 6b19d2a..32044ff 100644 --- a/src/ido-menu-item.c +++ b/src/ido-menu-item.c @@ -99,8 +99,37 @@ ido_menu_item_set_state (IdoMenuItem *self, if (priv->target) { ido_menu_item_set_has_indicator (self, TRUE); + ido_menu_item_set_active (self, FALSE); gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (self), TRUE); - ido_menu_item_set_active (self, g_variant_equal (priv->target, state)); + gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (self), FALSE); + + if (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING)) + { + ido_menu_item_set_active (self, g_variant_equal (priv->target, state)); + } + else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("as")) && + g_variant_is_of_type (priv->target, G_VARIANT_TYPE_STRING)) + { + const gchar *target_str; + const gchar **state_strs; + const gchar **it; + + target_str = g_variant_get_string (priv->target, NULL); + state_strs = g_variant_get_strv (state, NULL); + + it = state_strs; + while (*it != NULL && !g_str_equal (*it, target_str)) + it++; + + if (*it != NULL) + { + ido_menu_item_set_active (self, TRUE); + gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (self), + g_strv_length ((gchar **)state_strs) > 1); + } + + g_free (state_strs); + } } else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN)) { @@ -258,7 +287,8 @@ ido_menu_item_activate (GtkMenuItem *item) if (!priv->in_set_active && priv->action && priv->action_group) g_action_group_activate_action (priv->action_group, priv->action, priv->target); - GTK_MENU_ITEM_CLASS (ido_menu_item_parent_class)->activate (item); + if (priv->in_set_active) + GTK_MENU_ITEM_CLASS (ido_menu_item_parent_class)->activate (item); } static void diff --git a/src/messages-service.c b/src/messages-service.c index fd0bdcb..821598f 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -144,6 +144,52 @@ uses_chat_status_changed (GObject *object, update_chat_section (); } +static gboolean +strv_contains (const gchar **strv, + const gchar *needle) +{ + const gchar **it; + + it = strv; + while (*it != NULL && !g_str_equal (*it, needle)) + it++; + + return *it != NULL; +} + +static void +update_chat_status () +{ + GHashTableIter iter; + AppSection *section; + const gchar *statuses[] = { NULL, NULL, NULL, NULL, NULL, NULL }; + int pos = 0; + GAction *status; + + g_hash_table_iter_init (&iter, applications); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) §ion)) { + const gchar *status_str = NULL; + + status_str = app_section_get_status (section); + if (status_str != NULL && !strv_contains (statuses, status_str)) + statuses[pos++] = status_str; + } + + + status = g_simple_action_group_lookup (actions, "status"); + g_return_if_fail (status != NULL); + + g_simple_action_set_state (G_SIMPLE_ACTION (status), g_variant_new_strv (statuses, -1)); +} + +static void +chat_status_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + update_chat_status (); +} + static void remove_section (AppSection *section, const gchar *id) @@ -156,6 +202,7 @@ remove_section (AppSection *section, g_signal_handlers_disconnect_by_func (section, actions_changed, NULL); g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL); g_signal_handlers_disconnect_by_func (section, uses_chat_status_changed, NULL); + g_signal_handlers_disconnect_by_func (section, chat_status_changed, NULL); g_signal_handlers_disconnect_by_func (section, remove_section, NULL); g_hash_table_remove (applications, id); @@ -165,6 +212,7 @@ remove_section (AppSection *section, g_menu_remove (toplevel_menu, 0); } + update_chat_status (); update_chat_section (); } @@ -197,6 +245,8 @@ add_application (const gchar *desktop_id) G_CALLBACK (draws_attention_changed), NULL); g_signal_connect (section, "notify::uses-chat-status", G_CALLBACK (uses_chat_status_changed), NULL); + g_signal_connect (section, "notify::chat-status", + G_CALLBACK (chat_status_changed), NULL); g_signal_connect_data (section, "destroy", G_CALLBACK (remove_section), g_strdup (id), @@ -322,48 +372,15 @@ clear_action_activate (GSimpleAction *simple, } static void -radio_item_activate (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) -{ - g_action_change_state (G_ACTION (action), parameter); -} - -static gboolean -g_action_state_equal (GAction *action, - GVariant *value) -{ - GVariant *state; - gboolean eq; - - state = g_action_get_state (action); - g_return_val_if_fail (state != NULL, FALSE); - - eq = g_variant_equal (state, value); - - g_variant_unref (state); - return eq; -} - -static void -change_status_action (GSimpleAction *action, - GVariant *value, - gpointer user_data) +status_action_activate (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) { const gchar *status; - g_variant_get (value, "&s", &status); + status = g_variant_get_string (parameter, NULL); - g_return_if_fail (g_str_equal (status, "available") || - g_str_equal (status, "away")|| - g_str_equal (status, "busy") || - g_str_equal (status, "invisible") || - g_str_equal (status, "offline")); - - if (!g_action_state_equal (G_ACTION (action), value)) { - g_simple_action_set_state (action, value); - indicator_messages_service_emit_status_changed (messages_service, status); - } + indicator_messages_service_emit_status_changed (messages_service, status); } static void @@ -405,17 +422,35 @@ unregister_application (IndicatorMessagesService *service, static void set_status (IndicatorMessagesService *service, GDBusMethodInvocation *invocation, + const gchar *desktop_id, const gchar *status_str, gpointer user_data) { - GAction *status; + GDesktopAppInfo *appinfo; + gchar *id; + AppSection *section; - status = g_simple_action_group_lookup (actions, "status"); - g_return_if_fail (status != NULL); + g_return_if_fail (g_str_equal (status_str, "available") || + g_str_equal (status_str, "away")|| + g_str_equal (status_str, "busy") || + g_str_equal (status_str, "invisible") || + g_str_equal (status_str, "offline")); - g_action_change_state (status, g_variant_new_string (status_str)); + appinfo = g_desktop_app_info_new (desktop_id); + if (!appinfo) { + g_warning ("could not set status for '%s', there's no desktop file with that id", desktop_id); + return; + } + + id = g_app_info_get_simple_id (G_APP_INFO (appinfo)); + section = g_hash_table_lookup (applications, id); + if (section != NULL) + app_section_set_status (section, status_str); indicator_messages_service_complete_set_status (service, invocation); + + g_free (id); + g_object_unref (appinfo); } static GSimpleActionGroup * @@ -425,6 +460,7 @@ create_action_group (void) GSimpleAction *messages; GSimpleAction *clear; GSimpleAction *status; + const gchar *default_status[] = { "offline", NULL }; actions = g_simple_action_group_new (); @@ -433,9 +469,8 @@ create_action_group (void) g_variant_new_boolean (FALSE)); status = g_simple_action_new_stateful ("status", G_VARIANT_TYPE ("s"), - g_variant_new ("s", "offline")); - g_signal_connect (status, "activate", G_CALLBACK (radio_item_activate), NULL); - g_signal_connect (status, "change-state", G_CALLBACK (change_status_action), NULL); + g_variant_new_strv (default_status, -1)); + g_signal_connect (status, "activate", G_CALLBACK (status_action_activate), NULL); clear = g_simple_action_new ("clear", NULL); g_simple_action_set_enabled (clear, FALSE); diff --git a/src/messages-service.xml b/src/messages-service.xml index edd47c7..00ae154 100644 --- a/src/messages-service.xml +++ b/src/messages-service.xml @@ -12,6 +12,7 @@ + -- cgit v1.2.3 From 4d8755ace7d159d09346828336e2ac7698c16f96 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Sep 2012 23:13:46 +0200 Subject: Use indicator-message icons with chat status Changes the state of the "messages" action from a boolean (draws-attention) to a string (icon-name). This has the added benefit that more logic is moved from the plugin into the service. It also fixes an edge case: the messaging menu didn't have the blue icon after the service restarted (if anything was drawing attention). --- src/indicator-messages.c | 39 +++++++++++++++--- src/messages-service.c | 100 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 109 insertions(+), 30 deletions(-) diff --git a/src/indicator-messages.c b/src/indicator-messages.c index 2647a76..db4f634 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -87,6 +87,9 @@ static void menu_items_changed (GMenuModel *menu, gint removed, gint added, gpointer user_data); +static void messages_action_added (GActionGroup *action_group, + gchar *action_name, + gpointer user_data); static void messages_state_changed (GActionGroup *action_group, gchar *action_name, GVariant *value, @@ -174,6 +177,7 @@ static void service_connection_changed (IndicatorServiceManager *sm, GError *error = NULL; if (self->actions != NULL) { + g_signal_handlers_disconnect_by_func (self->actions, messages_action_added, self); g_signal_handlers_disconnect_by_func (self->actions, messages_state_changed, self); g_clear_object (&self->actions); } @@ -199,6 +203,8 @@ static void service_connection_changed (IndicatorServiceManager *sm, gtk_widget_insert_action_group (self->gtkmenu, get_name_hint (INDICATOR_OBJECT (self)), self->actions); + g_signal_connect (self->actions, "action-added::messages", + G_CALLBACK (messages_action_added), self); g_signal_connect (self->actions, "action-state-changed::messages", G_CALLBACK (messages_state_changed), self); @@ -330,6 +336,32 @@ menu_items_changed (GMenuModel *menu, indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); } +static void +indicator_messages_update_icon (IndicatorMessages *self, + GVariant *state) +{ + g_return_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING)); + + gtk_image_set_from_icon_name (GTK_IMAGE (self->image), + g_variant_get_string (state, NULL), + GTK_ICON_SIZE_MENU); + +} + +static void +messages_action_added (GActionGroup *action_group, + gchar *action_name, + gpointer user_data) +{ + IndicatorMessages *self = user_data; + GVariant *state; + + state = g_action_group_get_action_state (action_group, "messages"); + indicator_messages_update_icon (self, state); + + g_variant_unref (state); +} + static void messages_state_changed (GActionGroup *action_group, gchar *action_name, @@ -338,10 +370,5 @@ messages_state_changed (GActionGroup *action_group, { IndicatorMessages *self = user_data; - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)); - - if (g_variant_get_boolean (value)) - gtk_image_set_from_icon_name (GTK_IMAGE (self->image), "indicator-messages-new", GTK_ICON_SIZE_MENU); - else - gtk_image_set_from_icon_name (GTK_IMAGE (self->image), "indicator-messages", GTK_ICON_SIZE_MENU); + indicator_messages_update_icon (self, value); } diff --git a/src/messages-service.c b/src/messages-service.c index 821598f..f71614c 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -35,6 +35,8 @@ with this program. If not, see . #include "gmenuutils.h" #include "indicator-messages-service.h" +#define NUM_STATUSES 5 + static GHashTable *applications; IndicatorMessagesService *messages_service; @@ -44,6 +46,52 @@ static GMenu *toplevel_menu; static GMenu *menu; static GMenuModel *chat_section; static GSettings *settings; +gboolean draws_attention; +const gchar *global_status[6]; /* max 5: available, away, busy, invisible, offline */ + +static gchar * +indicator_messages_get_icon_name () +{ + GString *name; + GIcon *icon; + gchar *iconstr; + + name = g_string_new ("indicator-messages"); + + if (global_status[0] != NULL) + { + if (global_status[1] != NULL) + g_string_append (name, "-mixed"); + else + g_string_append_printf (name, "-%s", global_status[0]); + } + + if (draws_attention) + g_string_append (name, "-new"); + + icon = g_themed_icon_new (name->str); + iconstr = g_icon_to_string (icon); + + g_object_unref (icon); + g_string_free (name, TRUE); + + return iconstr; +} + +static void +indicator_messages_update_icon () +{ + GSimpleAction *messages; + gchar *icon; + + messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages")); + g_return_if_fail (messages != NULL); + + icon = indicator_messages_get_icon_name (); + g_simple_action_set_state (messages, g_variant_new_string (icon)); + + g_free (icon); +} static gchar * g_app_info_get_simple_id (GAppInfo *appinfo) @@ -91,18 +139,16 @@ draws_attention_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { - GSimpleAction *messages; GSimpleAction *clear; - gboolean attention; - messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages")); clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear")); - g_return_if_fail (messages != NULL && clear != NULL); + g_return_if_fail (clear != NULL); + + draws_attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL; - attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL; + g_simple_action_set_enabled (clear, draws_attention); - g_simple_action_set_state (messages, g_variant_new_boolean (attention)); - g_simple_action_set_enabled (clear, attention); + indicator_messages_update_icon (); } static gboolean @@ -134,6 +180,8 @@ update_chat_section () if (first_section != NULL) g_object_unref (first_section); + + indicator_messages_update_icon (); } static void @@ -162,24 +210,33 @@ update_chat_status () { GHashTableIter iter; AppSection *section; - const gchar *statuses[] = { NULL, NULL, NULL, NULL, NULL, NULL }; - int pos = 0; + int pos; GAction *status; + for (pos = 0; pos < G_N_ELEMENTS (global_status); pos++) + global_status[pos] = NULL; + + pos = 0; g_hash_table_iter_init (&iter, applications); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) §ion)) { + while (g_hash_table_iter_next (&iter, NULL, (gpointer) §ion) && + pos < G_N_ELEMENTS (global_status)) + { const gchar *status_str = NULL; status_str = app_section_get_status (section); - if (status_str != NULL && !strv_contains (statuses, status_str)) - statuses[pos++] = status_str; + if (status_str != NULL && !strv_contains (global_status, status_str)) + global_status[pos++] = status_str; } + if (pos == 0) + global_status[0] = "offline"; status = g_simple_action_group_lookup (actions, "status"); g_return_if_fail (status != NULL); - g_simple_action_set_state (G_SIMPLE_ACTION (status), g_variant_new_strv (statuses, -1)); + g_simple_action_set_state (G_SIMPLE_ACTION (status), g_variant_new_strv (global_status, -1)); + + indicator_messages_update_icon (); } static void @@ -262,21 +319,13 @@ add_application (const gchar *desktop_id) if (g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 0) { GMenuItem *header; - GIcon *icon; - gchar *iconstr; - - icon = g_themed_icon_new ("indicator-messages"); - iconstr = g_icon_to_string (icon); header = g_menu_item_new (NULL, "messages"); g_menu_item_set_submenu (header, G_MENU_MODEL (menu)); - g_menu_item_set_attribute (header, "x-canonical-icon", "s", iconstr); g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages")); g_menu_append_item (toplevel_menu, header); g_object_unref (header); - g_free (iconstr); - g_object_unref (icon); } g_free (id); @@ -461,12 +510,14 @@ create_action_group (void) GSimpleAction *clear; GSimpleAction *status; const gchar *default_status[] = { "offline", NULL }; + gchar *icon; actions = g_simple_action_group_new (); - /* state of the messages action mirrors "draws-attention" */ - messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("b"), - g_variant_new_boolean (FALSE)); + /* state of the messages action is its icon name */ + icon = indicator_messages_get_icon_name (); + messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("s"), + g_variant_new_string (icon)); status = g_simple_action_new_stateful ("status", G_VARIANT_TYPE ("s"), g_variant_new_strv (default_status, -1)); @@ -480,6 +531,7 @@ create_action_group (void) g_simple_action_group_insert (actions, G_ACTION (status)); g_simple_action_group_insert (actions, G_ACTION (clear)); + g_free (icon); return actions; } -- cgit v1.2.3 From c03005628b210cc7b775088d4693a87ca13f99e4 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Sep 2012 08:28:17 +0200 Subject: messages-service: define all global variables as static --- src/messages-service.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/messages-service.c b/src/messages-service.c index f71614c..73f3d35 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -39,15 +39,15 @@ with this program. If not, see . static GHashTable *applications; -IndicatorMessagesService *messages_service; +static IndicatorMessagesService *messages_service; static GSimpleActionGroup *actions; static GActionMuxer *action_muxer; static GMenu *toplevel_menu; static GMenu *menu; static GMenuModel *chat_section; static GSettings *settings; -gboolean draws_attention; -const gchar *global_status[6]; /* max 5: available, away, busy, invisible, offline */ +static gboolean draws_attention; +static const gchar *global_status[6]; /* max 5: available, away, busy, invisible, offline */ static gchar * indicator_messages_get_icon_name () -- cgit v1.2.3 From a055ac6232395007927915e6773ebc6abe9eed83 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Sep 2012 09:28:17 +0200 Subject: Bump required gtk version to 3.5.18 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b3e7a13..e350b83 100644 --- a/configure.ac +++ b/configure.ac @@ -38,7 +38,7 @@ AC_PROG_CXX # Dependencies ########################### -GTK_REQUIRED_VERSION=3.5.17 +GTK_REQUIRED_VERSION=3.5.18 GIO_UNIX_REQUIRED_VERSION=2.33.10 INDICATOR_REQUIRED_VERSION=0.3.19 GLIB_REQUIRED_VERSION=2.33.10 -- cgit v1.2.3 From c5527d3044dd1e7d9909e48c91de7bf4cbc86fd4 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Sep 2012 09:30:48 +0200 Subject: 12.10.3 --- NEWS | 11 +++++++++++ configure.ac | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 32b926d..8eceb54 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,15 @@ +12.10.3 + +* hide indicator when no application shows in the menu (lp #661059) +* remove apps as soon as they are uninstalled (lp #864545) +* make generated .gir compatible with vala (lp #104496) +* draw counts as lozenges (lp #1046331) +* show separators (lp #1048245) +* show chat presence in the panel (lp #859905) +* improve documentation + + 12.10.2 * libmessaging-menu: diff --git a/configure.ac b/configure.ac index e350b83..38abd7a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT(indicator-messages, 12.10.2) +AC_INIT(indicator-messages, 12.10.3) AC_PREREQ(2.62) -- cgit v1.2.3 From 4640bd97d81bce760044b19fd24ea5a70ceca1c0 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 25 Sep 2012 09:47:00 +0200 Subject: Request LARGE_TOOLBAR icons for the panel To make it consistent with the other indicators. --- src/indicator-messages.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/indicator-messages.c b/src/indicator-messages.c index db4f634..13b3409 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -297,7 +297,7 @@ indicator_messages_add_toplevel_menu (IndicatorMessages *self) icon = g_menu_model_get_item_attribute_icon (self->menu, 0, "x-canonical-icon"); if (icon) { - gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU); + gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_LARGE_TOOLBAR); g_object_unref (icon); } @@ -344,8 +344,7 @@ indicator_messages_update_icon (IndicatorMessages *self, gtk_image_set_from_icon_name (GTK_IMAGE (self->image), g_variant_get_string (state, NULL), - GTK_ICON_SIZE_MENU); - + GTK_ICON_SIZE_LARGE_TOOLBAR); } static void -- cgit v1.2.3 From 22a6fdb518be4189a13f4d8a7f0b07122cc87cdd Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 28 Sep 2012 17:01:02 +0200 Subject: Include fallback icon names And use g_icon_new_from_string in the indicator to make it load fallbacks. --- src/indicator-messages.c | 15 ++++++++++++--- src/messages-service.c | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/indicator-messages.c b/src/indicator-messages.c index 13b3409..5c5df31 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -340,11 +340,20 @@ static void indicator_messages_update_icon (IndicatorMessages *self, GVariant *state) { + GIcon *icon; + GError *error = NULL; + g_return_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING)); - gtk_image_set_from_icon_name (GTK_IMAGE (self->image), - g_variant_get_string (state, NULL), - GTK_ICON_SIZE_LARGE_TOOLBAR); + icon = g_icon_new_for_string (g_variant_get_string (state, NULL), &error); + if (icon == NULL) { + g_warning ("unable to load icon: %s", error->message); + g_error_free (error); + } + else { + gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_LARGE_TOOLBAR); + g_object_unref (icon); + } } static void diff --git a/src/messages-service.c b/src/messages-service.c index 73f3d35..b36a0a2 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -70,6 +70,10 @@ indicator_messages_get_icon_name () g_string_append (name, "-new"); icon = g_themed_icon_new (name->str); + g_themed_icon_append_name (G_THEMED_ICON (icon), + draws_attention ? "indicator-messages-new" + : "indicator-messages"); + iconstr = g_icon_to_string (icon); g_object_unref (icon); -- cgit v1.2.3 From 7a1769409539c7d672af51106d0fa186b32bd0ea Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 1 Oct 2012 17:26:44 +0200 Subject: libmessaging-menu: don't crash when getting an invalid desktop id Instead, silently don't export menus and actions. The single warning about the desktop id being invalid should be enough. --- libmessaging-menu/messaging-menu.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 8a95b0a..3c5c6d4 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -148,7 +148,8 @@ messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) { gchar *path; - g_return_val_if_fail (app->appinfo != NULL, NULL); + if (!app->appinfo) + return NULL; path = g_strconcat ("/com/canonical/indicator/messages/", g_app_info_get_id (G_APP_INFO (app->appinfo)), @@ -170,6 +171,10 @@ export_menus_and_actions (GObject *source, guint id; gchar *object_path; + object_path = messaging_menu_app_get_dbus_object_path (app); + if (!object_path) + return; + bus = g_bus_get_finish (res, &error); if (bus == NULL) { @@ -178,8 +183,6 @@ export_menus_and_actions (GObject *source, return; } - object_path = messaging_menu_app_get_dbus_object_path (app); - id = g_dbus_connection_export_action_group (bus, object_path, G_ACTION_GROUP (app->source_actions), @@ -479,6 +482,8 @@ messaging_menu_app_register (MessagingMenuApp *app) return; object_path = messaging_menu_app_get_dbus_object_path (app); + if (!object_path) + return; indicator_messages_service_call_register_application (app->messages_service, g_app_info_get_id (G_APP_INFO (app->appinfo)), @@ -510,6 +515,9 @@ messaging_menu_app_unregister (MessagingMenuApp *app) if (!app->messages_service) return; + if (!app->appinfo) + return; + indicator_messages_service_call_unregister_application (app->messages_service, g_app_info_get_id (G_APP_INFO (app->appinfo)), app->cancellable, @@ -546,6 +554,9 @@ messaging_menu_app_set_status (MessagingMenuApp *app, if (!app->messages_service) return; + if (!app->appinfo) + return; + indicator_messages_service_call_set_status (app->messages_service, g_app_info_get_id (G_APP_INFO (app->appinfo)), status_ids [status], -- cgit v1.2.3 From 36692583866dac99672571d6d83ed2c65b9e795d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 2 Oct 2012 19:40:38 +0200 Subject: 12.10.4 --- NEWS | 7 +++++++ configure.ac | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8eceb54..629218d 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,11 @@ +12.10.4 + +* specify fallback icons for the ones with status emblem (lp #1056595) +* make icon size consistent with the other indicators (lp #1055966) +* libmessaging-menu: fix crash when receiving a invalid desktop id (lp #1058386) + + 12.10.3 * hide indicator when no application shows in the menu (lp #661059) diff --git a/configure.ac b/configure.ac index 38abd7a..971bd96 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT(indicator-messages, 12.10.3) +AC_INIT(indicator-messages, 12.10.4) AC_PREREQ(2.62) -- cgit v1.2.3