From 3a33240e301a02fa6ee0aa0bd41780a0e3a28633 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 18 Jan 2013 19:48:04 +0100 Subject: Add IndicatorNg IndicatorNg is an indicator object that reads an indicator service file and watches the bus for a corresponding service to appear. It turns the menus and actions exported by the service into an indicator entry. --- libindicator/Makefile.am | 4 +- libindicator/indicator-ng.c | 444 ++++++++++++++++++++++++++++++++++++++++++++ libindicator/indicator-ng.h | 21 +++ tools/indicator-loader.c | 22 ++- 4 files changed, 485 insertions(+), 6 deletions(-) create mode 100644 libindicator/indicator-ng.c create mode 100644 libindicator/indicator-ng.h diff --git a/libindicator/Makefile.am b/libindicator/Makefile.am index ef6c6c7..de87e3b 100644 --- a/libindicator/Makefile.am +++ b/libindicator/Makefile.am @@ -51,7 +51,9 @@ libindicator_la_SOURCES = \ indicator-object-marshal.h \ indicator-object-marshal.c \ indicator-service.c \ - indicator-service-manager.c + indicator-service-manager.c \ + indicator-ng.c \ + indicator-ng.h libindicator_la_CFLAGS = \ $(LIBINDICATOR_CFLAGS) \ diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c new file mode 100644 index 0000000..d0d2177 --- /dev/null +++ b/libindicator/indicator-ng.c @@ -0,0 +1,444 @@ + +#include "indicator-ng.h" + +#include + +struct _IndicatorNg +{ + IndicatorObject parent; + + gchar *service_file; + gchar *name; + gchar *object_path; + gchar *profile; + gchar *header_action; + + guint name_watch_id; + + GActionGroup *actions; + GMenuModel *menu; + + GtkWidget *label; + GtkWidget *image; + GtkWidget *gtkmenu; + gchar *accessible_desc; +}; + +static void indicator_ng_initable_iface_init (GInitableIface *initable); +G_DEFINE_TYPE_WITH_CODE (IndicatorNg, indicator_ng, INDICATOR_OBJECT_TYPE, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, indicator_ng_initable_iface_init)) + +enum +{ + PROP_0, + PROP_SERVICE_FILE, + PROP_PROFILE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES]; + +static void +indicator_ng_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + IndicatorNg *self = INDICATOR_NG (object); + + switch (property_id) + { + case PROP_SERVICE_FILE: + g_value_set_string (value, self->service_file); + break; + + case PROP_PROFILE: + g_value_set_string (value, self->profile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +indicator_ng_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + IndicatorNg *self = INDICATOR_NG (object); + + switch (property_id) + { + case PROP_SERVICE_FILE: /* construct-only */ + self->service_file = g_strdup (g_value_get_string (value)); + break; + + case PROP_PROFILE: /* construct-only */ + self->profile = g_strdup (g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +indicator_ng_free_actions_and_menu (IndicatorNg *self) +{ + if (self->actions) + { + gtk_widget_insert_action_group (self->gtkmenu, self->name, self->actions); + g_signal_handlers_disconnect_by_data (self->actions, self); + g_clear_object (&self->actions); + } + + if (self->menu) + { + g_signal_handlers_disconnect_by_data (self->menu, self); + g_clear_object (&self->menu); + } +} + +static void +indicator_ng_dispose (GObject *object) +{ + IndicatorNg *self = INDICATOR_NG (object); + + if (self->name_watch_id) + { + g_bus_unwatch_name (self->name_watch_id); + self->name_watch_id = 0; + } + + indicator_ng_free_actions_and_menu (self); + + g_clear_object (&self->service_file); + g_clear_object (&self->label); + g_clear_object (&self->image); + g_clear_object (&self->gtkmenu); + + G_OBJECT_CLASS (indicator_ng_parent_class)->dispose (object); +} + +static void +indicator_ng_finalize (GObject *object) +{ + IndicatorNg *self = INDICATOR_NG (object); + + g_free (self->service_file); + g_free (self->name); + g_free (self->object_path); + g_free (self->accessible_desc); + g_free (self->header_action); + + G_OBJECT_CLASS (indicator_ng_parent_class)->finalize (object); +} + +static GtkLabel * +indicator_ng_get_label (IndicatorObject *io) +{ + IndicatorNg *self = INDICATOR_NG (io); + + gtk_widget_show (self->label); + + return GTK_LABEL (self->label); +} + +static GtkImage * +indicator_ng_get_image (IndicatorObject *io) +{ + IndicatorNg *self = INDICATOR_NG (io); + + gtk_widget_show (self->image); + + return GTK_IMAGE (self->image); +} + +static GtkMenu * +indicator_ng_get_menu (IndicatorObject *io) +{ + IndicatorNg *self = INDICATOR_NG (io); + + return GTK_MENU (self->gtkmenu); +} + +static const gchar * +indicator_ng_get_accessible_desc (IndicatorObject *io) +{ + IndicatorNg *self = INDICATOR_NG (io); + + return self->accessible_desc; +} + +static const gchar * +indicator_ng_get_name_hint (IndicatorObject *io) +{ + IndicatorNg *self = INDICATOR_NG (io); + + return self->name; +} + +static void +indicator_ng_set_accessible_desc (IndicatorNg *self, + const gchar *accessible_desc) +{ + GList *entries; + + g_free (self->accessible_desc); + + self->accessible_desc = g_strdup (accessible_desc); + + entries = indicator_object_get_entries (INDICATOR_OBJECT (self)); + g_return_if_fail (entries != NULL); + + g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entries->data); + + g_list_free (entries); +} + +static gboolean +gtk_image_set_from_gicon_string (GtkImage *img, + const gchar *str) +{ + GIcon *icon; + GError *error = NULL; + + icon = str ? g_icon_new_for_string (str, &error) : NULL; + if (icon) + { + gtk_image_set_from_gicon (img, icon, GTK_ICON_SIZE_LARGE_TOOLBAR); + g_object_unref (icon); + return TRUE; + } + else + { + if (error) + { + g_warning ("invalid icon string '%s': %s", str, error->message); + g_error_free (error); + } + return FALSE; + } +} + +static void +indicator_ng_update_entry (IndicatorNg *self) +{ + GVariant *state; + + g_return_if_fail (self->menu != NULL); + g_return_if_fail (self->actions != NULL); + + if (!self->header_action || + !g_action_group_has_action (self->actions, self->header_action)) + { + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); + return; + } + + state = g_action_group_get_action_state (self->actions, self->header_action); + if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) + { + gchar *label; + gchar *iconstr; + gchar *accessible_desc; + gboolean visible; + + g_variant_get (state, "(sssb)", &label, &iconstr, &accessible_desc, &visible); + + gtk_label_set_label (GTK_LABEL (self->label), label); + if (!gtk_image_set_from_gicon_string (GTK_IMAGE (self->image), iconstr)) + gtk_widget_hide (self->image); + indicator_ng_set_accessible_desc (self, accessible_desc); + indicator_object_set_visible (INDICATOR_OBJECT (self), visible); + + g_free (label); + g_free (iconstr); + g_free (accessible_desc); + } + else + g_warning ("the action of the indicator menu item must have state with type (sssb)"); + + if (state) + g_variant_unref (state); +} + +static void +indicator_ng_menu_changed (GMenuModel *menu, + gint position, + gint removed, + gint added, + gpointer user_data) +{ + IndicatorNg *self = user_data; + + g_return_if_fail (position == 0); + g_return_if_fail (added < 2 && removed < 2 && added ^ removed); + + if (removed) + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); + + if (added) + { + GMenuModel *popup; + gchar *action; + + g_clear_pointer (&self->header_action, g_free); + g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action); + if (action && g_str_has_prefix (action, "indicator.")) + self->header_action = g_strdup (action + 10); + + popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); + if (popup) + { + gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->gtkmenu), popup, NULL, TRUE); + g_object_unref (popup); + } + + indicator_ng_update_entry (self); + + g_free (action); + } +} + +static void +indicator_ng_service_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + IndicatorNg *self = user_data; + gchar *menu_object_path; + + g_assert (!self->actions); + g_assert (!self->menu); + + self->actions = G_ACTION_GROUP (g_dbus_action_group_get (connection, name_owner, self->object_path)); + gtk_widget_insert_action_group (self->gtkmenu, "indicator", self->actions); + g_signal_connect_swapped (self->actions, "action-added", G_CALLBACK (indicator_ng_update_entry), self); + g_signal_connect_swapped (self->actions, "action-removed", G_CALLBACK (indicator_ng_update_entry), self); + g_signal_connect_swapped (self->actions, "action-state-changed", G_CALLBACK (indicator_ng_update_entry), self); + + menu_object_path = g_strconcat (self->object_path, "/", self->profile, NULL); + self->menu = G_MENU_MODEL (g_dbus_menu_model_get (connection, name_owner, menu_object_path)); + g_signal_connect (self->menu, "items-changed", G_CALLBACK (indicator_ng_menu_changed), self); + if (g_menu_model_get_n_items (self->menu)) + indicator_ng_menu_changed (self->menu, 0, 0, 1, self); + + indicator_ng_update_entry (self); + + g_free (menu_object_path); +} + +static void +indicator_ng_service_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + IndicatorNg *self = user_data; + + indicator_ng_free_actions_and_menu (self); + + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); +} + +static gboolean +indicator_ng_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + IndicatorNg *self = INDICATOR_NG (initable); + GKeyFile *keyfile; + gchar *bus_name = NULL; + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, + self->service_file, + G_KEY_FILE_NONE, + error)) + { + g_key_file_free (keyfile); + return FALSE; + } + + if ((self->name = g_key_file_get_string (keyfile, "Indicator Service", "Name", error)) && + (bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error)) && + (self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error))) + { + + self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, + bus_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, + indicator_ng_service_appeared, + indicator_ng_service_vanished, + self, NULL); + } + + g_free (bus_name); + g_key_file_free (keyfile); + + return self->name_watch_id > 0; +} + +static void +indicator_ng_class_init (IndicatorNgClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS (class); + + object_class->get_property = indicator_ng_get_property; + object_class->set_property = indicator_ng_set_property; + object_class->dispose = indicator_ng_dispose; + object_class->finalize = indicator_ng_finalize; + + io_class->get_label = indicator_ng_get_label; + io_class->get_image = indicator_ng_get_image; + io_class->get_menu = indicator_ng_get_menu; + io_class->get_accessible_desc = indicator_ng_get_accessible_desc; + io_class->get_name_hint = indicator_ng_get_name_hint; + + properties[PROP_SERVICE_FILE] = g_param_spec_string ("service-file", + "Service file", + "Path of the service file", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PROFILE] = g_param_spec_string ("profile", + "Profile", + "Indicator profile", + "desktop", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, N_PROPERTIES, properties); +} + +static void +indicator_ng_initable_iface_init (GInitableIface *initable) +{ + initable->init = indicator_ng_initable_init; +} + +static void +indicator_ng_init (IndicatorNg *self) +{ + self->label = g_object_ref_sink (gtk_label_new (NULL)); + self->image = g_object_ref_sink (gtk_image_new ()); + self->gtkmenu = g_object_ref_sink (gtk_menu_new ()); + + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); +} + +IndicatorNg * +indicator_ng_new (const gchar *service_file, + GError **error) +{ + return g_initable_new (INDICATOR_TYPE_NG, NULL, error, + "service-file", service_file, + NULL); +} diff --git a/libindicator/indicator-ng.h b/libindicator/indicator-ng.h new file mode 100644 index 0000000..2187443 --- /dev/null +++ b/libindicator/indicator-ng.h @@ -0,0 +1,21 @@ +#ifndef __INDICATOR_NG_H__ +#define __INDICATOR_NG_H__ + +#include + +#define INDICATOR_TYPE_NG (indicator_ng_get_type ()) +#define INDICATOR_NG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_TYPE_NG, IndicatorNg)) +#define INDICATOR_NG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_TYPE_NG, IndicatorNgClass)) +#define INDICATOR_IS_NG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_NG)) +#define INDICATOR_IS_NG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_TYPE_NG)) +#define INDICATOR_NG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_TYPE_NG, IndicatorNgClass)) + +typedef struct _IndicatorNg IndicatorNg; +typedef IndicatorObjectClass IndicatorNgClass; + +GType indicator_ng_get_type (void); + +IndicatorNg * indicator_ng_new (const gchar *service_file, + GError **error); + +#endif diff --git a/tools/indicator-loader.c b/tools/indicator-loader.c index 560fc45..51d2e46 100644 --- a/tools/indicator-loader.c +++ b/tools/indicator-loader.c @@ -24,6 +24,7 @@ License along with this library. If not, see #include #include +#include static GHashTable * entry_to_menuitem = NULL; @@ -129,14 +130,25 @@ load_module (const gchar * name, GtkWidget * menu) g_debug("Looking at Module: %s", name); g_return_val_if_fail(name != NULL, FALSE); - if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) { - return FALSE; - } - g_debug("Loading Module: %s", name); /* Build the object for the module */ - IndicatorObject * io = indicator_object_new_from_file(name); + IndicatorObject *io; + if (g_str_has_suffix(name, G_MODULE_SUFFIX)) { + io = indicator_object_new_from_file(name); + } + else if (g_str_has_suffix(name, ".indicator")) { + GError *error = NULL; + + io = INDICATOR_OBJECT(indicator_ng_new(name, &error)); + if (!io) { + g_warning ("could not load indicator from '%s': %s", name, error->message); + g_error_free (error); + return FALSE; + } + } + else + return FALSE; /* Connect to it's signals */ g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menu); -- cgit v1.2.3 From aaae4aa191dc5ad4dd643822b33397bc6faace0e Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 18 Jan 2013 22:17:54 +0100 Subject: Only build and use IndicatorNg in the gtk3 build --- libindicator/Makefile.am | 6 +++++- tools/indicator-loader.c | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libindicator/Makefile.am b/libindicator/Makefile.am index de87e3b..edb4fac 100644 --- a/libindicator/Makefile.am +++ b/libindicator/Makefile.am @@ -51,9 +51,13 @@ libindicator_la_SOURCES = \ indicator-object-marshal.h \ indicator-object-marshal.c \ indicator-service.c \ - indicator-service-manager.c \ + indicator-service-manager.c + +if USE_GTK3 +libindicator_la_SOURCES += \ indicator-ng.c \ indicator-ng.h +endif libindicator_la_CFLAGS = \ $(LIBINDICATOR_CFLAGS) \ diff --git a/tools/indicator-loader.c b/tools/indicator-loader.c index 51d2e46..d275c43 100644 --- a/tools/indicator-loader.c +++ b/tools/indicator-loader.c @@ -24,7 +24,10 @@ License along with this library. If not, see #include #include + +#if GTK_MAJOR_VERSION == 3 #include +#endif static GHashTable * entry_to_menuitem = NULL; @@ -137,6 +140,7 @@ load_module (const gchar * name, GtkWidget * menu) if (g_str_has_suffix(name, G_MODULE_SUFFIX)) { io = indicator_object_new_from_file(name); } +#if GTK_MAJOR_VERSION == 3 else if (g_str_has_suffix(name, ".indicator")) { GError *error = NULL; @@ -147,6 +151,7 @@ load_module (const gchar * name, GtkWidget * menu) return FALSE; } } +#endif else return FALSE; -- cgit v1.2.3 From 3822cf902d3b431a182a7416b5f0238ac324ca1d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 18 Jan 2013 22:18:27 +0100 Subject: Bump gtk3 dependency for gtk_widget_insert_action_group --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6c34ad0..48f759f 100644 --- a/configure.ac +++ b/configure.ac @@ -43,7 +43,7 @@ AS_IF([test "x$enable_deprecations" = xno], ############################## GTK_REQUIRED_VERSION=2.18 -GTK3_REQUIRED_VERSION=2.91 +GTK3_REQUIRED_VERSION=3.6 GIO_UNIX_REQUIRED_VERSION=2.22 AC_ARG_WITH([gtk], -- cgit v1.2.3 From ca52243cf50336e0ef9cb0d1dd867cb657580f1b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 10:58:26 +0100 Subject: indicator-ng.h: use local include --- libindicator/indicator-ng.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libindicator/indicator-ng.h b/libindicator/indicator-ng.h index 2187443..4dd333e 100644 --- a/libindicator/indicator-ng.h +++ b/libindicator/indicator-ng.h @@ -1,7 +1,7 @@ #ifndef __INDICATOR_NG_H__ #define __INDICATOR_NG_H__ -#include +#include "indicator-object.h" #define INDICATOR_TYPE_NG (indicator_ng_get_type ()) #define INDICATOR_NG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_TYPE_NG, IndicatorNg)) -- cgit v1.2.3 From af3ac653f86db8fefe9475a3354662636afd15e2 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 16:38:11 +0100 Subject: indicator-ng: always set an accessible description to avoid imminent warning --- libindicator/indicator-ng.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index d0d2177..ea02847 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -431,6 +431,12 @@ indicator_ng_init (IndicatorNg *self) self->image = g_object_ref_sink (gtk_image_new ()); self->gtkmenu = g_object_ref_sink (gtk_menu_new ()); + /* work around IndicatorObject's warning that the accessible + * description is missing. We never set it on construction, but when + * the menu model has arrived on the bus. + */ + self->accessible_desc = g_strdup (""); + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); } -- cgit v1.2.3 From 869d977e5add09a08ec6e1fcf1d77f8286adca53 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 16:39:55 +0100 Subject: indicator-ng: fix crash (tried to free a string with g_object_unref) --- libindicator/indicator-ng.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index ea02847..10c362c 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -114,7 +114,6 @@ indicator_ng_dispose (GObject *object) indicator_ng_free_actions_and_menu (self); - g_clear_object (&self->service_file); g_clear_object (&self->label); g_clear_object (&self->image); g_clear_object (&self->gtkmenu); -- cgit v1.2.3 From 4b301ff893da66cb0a8f5e4c541efa7e70ee67a3 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 17:04:02 +0100 Subject: indicator-ng: add indicator_ng_new_for_profile --- libindicator/indicator-ng.c | 11 +++++++++++ libindicator/indicator-ng.h | 10 +++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 10c362c..32a9e0c 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -447,3 +447,14 @@ indicator_ng_new (const gchar *service_file, "service-file", service_file, NULL); } + +IndicatorNg * +indicator_ng_new_for_profile (const gchar *service_file, + const gchar *profile, + GError **error) +{ + return g_initable_new (INDICATOR_TYPE_NG, NULL, error, + "service-file", service_file, + "profile", profile, + NULL); +} diff --git a/libindicator/indicator-ng.h b/libindicator/indicator-ng.h index 4dd333e..fb4553f 100644 --- a/libindicator/indicator-ng.h +++ b/libindicator/indicator-ng.h @@ -13,9 +13,13 @@ typedef struct _IndicatorNg IndicatorNg; typedef IndicatorObjectClass IndicatorNgClass; -GType indicator_ng_get_type (void); +GType indicator_ng_get_type (void); -IndicatorNg * indicator_ng_new (const gchar *service_file, - GError **error); +IndicatorNg * indicator_ng_new (const gchar *service_file, + GError **error); + +IndicatorNg * indicator_ng_new_for_profile (const gchar *service_file, + const gchar *profile, + GError **error); #endif -- cgit v1.2.3 From 1585adb6c7da6c021285bcca0cacbb1976c04d15 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 17:09:55 +0100 Subject: indicator-ng: add getters --- libindicator/indicator-ng.c | 16 ++++++++++++++++ libindicator/indicator-ng.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 32a9e0c..7b2b730 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -458,3 +458,19 @@ indicator_ng_new_for_profile (const gchar *service_file, "profile", profile, NULL); } + +const gchar * +indicator_ng_get_service_file (IndicatorNg *self) +{ + g_return_val_if_fail (INDICATOR_IS_NG (self), NULL); + + return self->service_file; +} + +const gchar * +indicator_ng_get_profile (IndicatorNg *self) +{ + g_return_val_if_fail (INDICATOR_IS_NG (self), NULL); + + return self->profile; +} diff --git a/libindicator/indicator-ng.h b/libindicator/indicator-ng.h index fb4553f..9f19958 100644 --- a/libindicator/indicator-ng.h +++ b/libindicator/indicator-ng.h @@ -22,4 +22,8 @@ IndicatorNg * indicator_ng_new_for_profile (const gchar *service_file, const gchar *profile, GError **error); +const gchar * indicator_ng_get_service_file (IndicatorNg *indicator); + +const gchar * indicator_ng_get_profile (IndicatorNg *indicator); + #endif -- cgit v1.2.3 From 36e0fea77699a71d03245900b9c0ae5a7cf2bcf5 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 17:30:05 +0100 Subject: Make sure indicator-ng.h is installed --- libindicator/Makefile.am | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libindicator/Makefile.am b/libindicator/Makefile.am index edb4fac..7bd37ef 100644 --- a/libindicator/Makefile.am +++ b/libindicator/Makefile.am @@ -36,6 +36,10 @@ indicator_headers = \ indicator-service.h \ indicator-service-manager.h +if USE_GTK3 +indicator_headers += indicator-ng.h +endif + libindicatorinclude_HEADERS = \ $(indicator_headers) @@ -54,9 +58,7 @@ libindicator_la_SOURCES = \ indicator-service-manager.c if USE_GTK3 -libindicator_la_SOURCES += \ - indicator-ng.c \ - indicator-ng.h +libindicator_la_SOURCES += indicator-ng.c endif libindicator_la_CFLAGS = \ -- cgit v1.2.3 From d84d6f7cce3715ec88a67418c4d4298237618463 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 21 Jan 2013 17:34:44 +0100 Subject: Add basic tests for indicator-ng --- tests/Makefile.am | 25 +++++++++++++++ tests/com.canonical.test.indicator | 4 +++ tests/test-indicator-ng.c | 62 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 tests/com.canonical.test.indicator create mode 100644 tests/test-indicator-ng.c diff --git a/tests/Makefile.am b/tests/Makefile.am index c98bdbf..97741b0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -466,3 +466,28 @@ DISTCLEANFILES += loader-tester DISTCLEANFILES += $(XML_REPORT) $(HTML_REPORT) +############################# +# Indicator-ng +############################# + +if USE_GTK3 + +check_PROGRAMS += test-indicator-ng + +test_indicator_ng_SOURCES = test-indicator-ng.c + +test_indicator_ng_CFLAGS = \ + $(LIBINDICATOR_CFLAGS) \ + -I$(top_srcdir) \ + -DSRCDIR="\"$(srcdir)\"" \ + -Wall + +test_indicator_ng_LDADD = \ + $(LIBINDICATOR_LIBS) \ + -L$(top_builddir)/libindicator/.libs \ + $(INDICATOR_LIB) + +TESTS += test-indicator-ng +DISTCLEANFILES = test-indicator-ng + +endif diff --git a/tests/com.canonical.test.indicator b/tests/com.canonical.test.indicator new file mode 100644 index 0000000..dad4c94 --- /dev/null +++ b/tests/com.canonical.test.indicator @@ -0,0 +1,4 @@ +[Indicator Service] +Name=indicator-test +BusName=com.canonical.indicator.test +ObjectPath=/com/canonical/indicator/test diff --git a/tests/test-indicator-ng.c b/tests/test-indicator-ng.c new file mode 100644 index 0000000..65a1e72 --- /dev/null +++ b/tests/test-indicator-ng.c @@ -0,0 +1,62 @@ + +#include + +static void +test_non_existing (void) +{ + IndicatorNg *indicator; + GError *error = NULL; + + indicator = indicator_ng_new (SRCDIR "/com.canonical.does.not.exist.indicator", &error); + g_assert (indicator == NULL); + g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT); + + g_clear_error (&error); +} + +static void +test_instantiation (void) +{ + IndicatorNg *indicator; + GError *error = NULL; + + indicator = indicator_ng_new (SRCDIR "/com.canonical.test.indicator", &error); + g_assert (indicator); + g_assert (error == NULL); + + g_assert_cmpstr (indicator_ng_get_service_file (indicator), ==, SRCDIR "/com.canonical.test.indicator"); + g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "desktop"); + + /* no service running, so there shouldn't be any indicators */ + g_assert (indicator_object_get_entries (INDICATOR_OBJECT (indicator)) == NULL); + + g_object_unref (indicator); +} + +static void +test_instantiation_with_profile (void) +{ + IndicatorNg *indicator; + GError *error = NULL; + + indicator = indicator_ng_new_for_profile (SRCDIR "/com.canonical.test.indicator", "greeter", &error); + g_assert (indicator); + g_assert (error == NULL); + + g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "greeter"); + + g_object_unref (indicator); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + gtk_init (&argc, &argv); + + g_test_add_func ("/indicator-ng/non-existing", test_non_existing); + g_test_add_func ("/indicator-ng/instantiation", test_instantiation); + g_test_add_func ("/indicator-ng/instantiation-with-profile", test_instantiation_with_profile); + + return g_test_run (); +} -- cgit v1.2.3 From 67323d4aa99efc18ae2f379cd479d9b23e23dd94 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 22 Jan 2013 18:36:10 +0100 Subject: indicator-ng: properly unset action group when the service disappears --- libindicator/indicator-ng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 7b2b730..8de7112 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -89,7 +89,7 @@ indicator_ng_free_actions_and_menu (IndicatorNg *self) { if (self->actions) { - gtk_widget_insert_action_group (self->gtkmenu, self->name, self->actions); + gtk_widget_insert_action_group (self->gtkmenu, "indicator", NULL); g_signal_handlers_disconnect_by_data (self->actions, self); g_clear_object (&self->actions); } -- cgit v1.2.3 From ac0c8099e033c780066b968e09d955ac1552f862 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 23 Jan 2013 17:29:05 +0100 Subject: indicator-ng: auto start service if it's not running --- libindicator/indicator-ng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 8de7112..d241099 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -369,7 +369,7 @@ indicator_ng_initable_init (GInitable *initable, self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, bus_name, - G_BUS_NAME_WATCHER_FLAGS_NONE, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, indicator_ng_service_appeared, indicator_ng_service_vanished, self, NULL); -- cgit v1.2.3 From 739daae6ed0e8d0c95a131e5a2ea31fc2cfb4ec0 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 23 Jan 2013 17:30:05 +0100 Subject: indicator-ng: use base->get_entries to get the invisible ones, too --- libindicator/indicator-ng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index d241099..a77aa43 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -189,7 +189,7 @@ indicator_ng_set_accessible_desc (IndicatorNg *self, self->accessible_desc = g_strdup (accessible_desc); - entries = indicator_object_get_entries (INDICATOR_OBJECT (self)); + entries = INDICATOR_OBJECT_GET_CLASS (self)->get_entries (INDICATOR_OBJECT (self)); g_return_if_fail (entries != NULL); g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entries->data); -- cgit v1.2.3 From e07deff5a9e253d9e407e75ce2293e3cbb688d12 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 23 Jan 2013 17:55:08 +0100 Subject: indicator-ng: set name hint to the value of the service file's "Name" field --- libindicator/indicator-ng.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index a77aa43..975ae9f 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -38,6 +38,21 @@ enum static GParamSpec *properties[N_PROPERTIES]; +static IndicatorObjectEntry * +indicator_ng_get_entry (IndicatorNg *self) +{ + GList *entries; + IndicatorObjectEntry *entry; + + entries = INDICATOR_OBJECT_GET_CLASS (self)->get_entries (INDICATOR_OBJECT (self)); + g_return_val_if_fail (entries != NULL, NULL); + + entry = entries->data; + + g_list_free (entries); + return entry; +} + static void indicator_ng_get_property (GObject *object, guint property_id, @@ -183,18 +198,14 @@ static void indicator_ng_set_accessible_desc (IndicatorNg *self, const gchar *accessible_desc) { - GList *entries; + IndicatorObjectEntry *entry; - g_free (self->accessible_desc); + entry = indicator_ng_get_entry (self); + g_free (self->accessible_desc); self->accessible_desc = g_strdup (accessible_desc); - entries = INDICATOR_OBJECT_GET_CLASS (self)->get_entries (INDICATOR_OBJECT (self)); - g_return_if_fail (entries != NULL); - - g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entries->data); - - g_list_free (entries); + g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entry); } static gboolean @@ -366,6 +377,10 @@ indicator_ng_initable_init (GInitable *initable, (bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error)) && (self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error))) { + IndicatorObjectEntry *entry; + + entry = indicator_ng_get_entry (self); + entry->name_hint = self->name; self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, bus_name, -- cgit v1.2.3 From 0996bce98e7f8c1de5e0d6b3f67589ab083a4713 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 23 Jan 2013 18:00:49 +0100 Subject: indicator-ng: more elaborate testing Use GTestDBus to spawn a small test service (tests/indicator-test-service.c) and check whether the indicator menu gets turned into a gtkmenu correctly. --- tests/Makefile.am | 20 +++- tests/com.canonical.indicator.test.service.in | 3 + tests/com.canonical.test.nosuchservice.indicator | 4 + tests/indicator-test-service.c | 107 +++++++++++++++++++++ tests/test-indicator-ng.c | 113 +++++++++++++++++++++-- 5 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 tests/com.canonical.indicator.test.service.in create mode 100644 tests/com.canonical.test.nosuchservice.indicator create mode 100644 tests/indicator-test-service.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 97741b0..96825f3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,7 +26,8 @@ EXTRA_DIST = \ session.conf.in \ service-manager-connect.service.in \ service-version-bad.service.in \ - service-version-good.service.in + service-version-good.service.in \ + com.canonical.indicator.test.service.in ############################# # Test Loader @@ -472,6 +473,9 @@ DISTCLEANFILES += $(XML_REPORT) $(HTML_REPORT) if USE_GTK3 +com.canonical.indicator.test.service: com.canonical.indicator.test.service.in Makefile.am + sed -e "s|\@builddir\@|$(abspath $(builddir))|" $< > $@ + check_PROGRAMS += test-indicator-ng test_indicator_ng_SOURCES = test-indicator-ng.c @@ -480,6 +484,7 @@ test_indicator_ng_CFLAGS = \ $(LIBINDICATOR_CFLAGS) \ -I$(top_srcdir) \ -DSRCDIR="\"$(srcdir)\"" \ + -DBUILD_DIR="\"$(abs_builddir)\"" \ -Wall test_indicator_ng_LDADD = \ @@ -488,6 +493,17 @@ test_indicator_ng_LDADD = \ $(INDICATOR_LIB) TESTS += test-indicator-ng -DISTCLEANFILES = test-indicator-ng +DISTCLEANFILES += test-indicator-ng + +# Mock indicator service +check_PROGRAMS += indicator-test-service + +indicator_test_service_SOURCES = indicator-test-service.c +indicator_test_service_CFLAGS = $(LIBINDICATOR_CFLAGS) +indicator_test_service_LDADD = $(LIBINDICATOR_LIBS) + +EXTRA_indicator_test_service_DEPENDENCIES = com.canonical.indicator.test.service + +DISTCLEANFILES += indicator-test-service com.canonical.indicator.test.service endif diff --git a/tests/com.canonical.indicator.test.service.in b/tests/com.canonical.indicator.test.service.in new file mode 100644 index 0000000..3fdbe76 --- /dev/null +++ b/tests/com.canonical.indicator.test.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=com.canonical.indicator.test +Exec=@builddir@/indicator-test-service diff --git a/tests/com.canonical.test.nosuchservice.indicator b/tests/com.canonical.test.nosuchservice.indicator new file mode 100644 index 0000000..8464749 --- /dev/null +++ b/tests/com.canonical.test.nosuchservice.indicator @@ -0,0 +1,4 @@ +[Indicator Service] +Name=indicator-test +BusName=com.canonical.indicator.test.nosuchservice +ObjectPath=/com/canonical/indicator/test diff --git a/tests/indicator-test-service.c b/tests/indicator-test-service.c new file mode 100644 index 0000000..3a6a0b2 --- /dev/null +++ b/tests/indicator-test-service.c @@ -0,0 +1,107 @@ + +#include + +typedef struct +{ + GSimpleActionGroup *actions; + GMenu *menu; + + guint actions_export_id; + guint menu_export_id; +} IndicatorTestService; + +static void +bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + IndicatorTestService *indicator = user_data; + GError *error = NULL; + + indicator->actions_export_id = g_dbus_connection_export_action_group (connection, + "/com/canonical/indicator/test", + G_ACTION_GROUP (indicator->actions), + &error); + if (indicator->actions_export_id == 0) + { + g_warning ("cannot export action group: %s", error->message); + g_error_free (error); + return; + } + + indicator->menu_export_id = g_dbus_connection_export_menu_model (connection, + "/com/canonical/indicator/test/desktop", + G_MENU_MODEL (indicator->menu), + &error); + if (indicator->menu_export_id == 0) + { + g_warning ("cannot export menu: %s", error->message); + g_error_free (error); + return; + } +} + +static void +name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + IndicatorTestService *indicator = user_data; + + if (indicator->actions_export_id) + g_dbus_connection_unexport_action_group (connection, indicator->actions_export_id); + + if (indicator->menu_export_id) + g_dbus_connection_unexport_menu_model (connection, indicator->menu_export_id); +} + +static void +activate_show (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + g_message ("showing"); +} + +int +main (int argc, char **argv) +{ + IndicatorTestService indicator = { 0 }; + GMenuItem *item; + GMenu *submenu; + GActionEntry entries[] = { + { "_header", NULL, NULL, "('Test', 'indicator-test', 'Test indicator', true)", NULL }, + { "show", activate_show, NULL, NULL, NULL } + }; + GMainLoop *loop; + + indicator.actions = g_simple_action_group_new (); + g_simple_action_group_add_entries (indicator.actions, entries, G_N_ELEMENTS (entries), NULL); + + submenu = g_menu_new (); + g_menu_append (submenu, "Show", "indicator.show"); + item = g_menu_item_new (NULL, "indicator._header"); + g_menu_item_set_submenu (item, G_MENU_MODEL (submenu)); + indicator.menu = g_menu_new (); + g_menu_append_item (indicator.menu, item); + + g_bus_own_name (G_BUS_TYPE_SESSION, + "com.canonical.indicator.test", + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired, + NULL, + name_lost, + &indicator, + NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + + g_object_unref (submenu); + g_object_unref (item); + g_object_unref (indicator.actions); + g_object_unref (indicator.menu); + g_object_unref (loop); + + return 0; +} diff --git a/tests/test-indicator-ng.c b/tests/test-indicator-ng.c index 65a1e72..827cc98 100644 --- a/tests/test-indicator-ng.c +++ b/tests/test-indicator-ng.c @@ -1,6 +1,35 @@ #include +static void +indicator_ng_test_func (gconstpointer user_data) +{ + GTestFunc test_func = user_data; + GTestDBus *bus; + + bus = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_add_service_dir (bus, BUILD_DIR); + g_test_dbus_up (bus); + + test_func (); + + g_test_dbus_down (bus); + g_object_unref (bus); +} + +#define indicator_ng_test_add(name, test_func) \ + g_test_add_data_func ("/indicator-ng/" name, test_func, indicator_ng_test_func) + +static gboolean +stop_main_loop (gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + + return FALSE; +} + static void test_non_existing (void) { @@ -19,17 +48,23 @@ test_instantiation (void) { IndicatorNg *indicator; GError *error = NULL; + GMainLoop *loop; - indicator = indicator_ng_new (SRCDIR "/com.canonical.test.indicator", &error); + indicator = indicator_ng_new (SRCDIR "/com.canonical.test.nosuchservice.indicator", &error); g_assert (indicator); g_assert (error == NULL); - g_assert_cmpstr (indicator_ng_get_service_file (indicator), ==, SRCDIR "/com.canonical.test.indicator"); + g_assert_cmpstr (indicator_ng_get_service_file (indicator), ==, SRCDIR "/com.canonical.test.nosuchservice.indicator"); g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "desktop"); - /* no service running, so there shouldn't be any indicators */ + loop = g_main_loop_new (NULL, FALSE); + g_timeout_add (200, stop_main_loop, loop); + g_main_loop_run (loop); + + /* no such service, so there shouldn't be any indicators */ g_assert (indicator_object_get_entries (INDICATOR_OBJECT (indicator)) == NULL); + g_main_loop_unref (loop); g_object_unref (indicator); } @@ -48,15 +83,81 @@ test_instantiation_with_profile (void) g_object_unref (indicator); } +static void +test_menu (void) +{ + IndicatorNg *indicator; + GError *error = NULL; + GMainLoop *loop; + GList *entries; + IndicatorObjectEntry *entry; + + indicator = indicator_ng_new (SRCDIR "/com.canonical.test.indicator", &error); + g_assert (indicator); + g_assert (error == NULL); + + loop = g_main_loop_new (NULL, FALSE); + g_timeout_add (500, stop_main_loop, loop); + g_main_loop_run (loop); + + entries = indicator_object_get_entries (INDICATOR_OBJECT (indicator)); + g_assert_cmpint (g_list_length (entries), ==, 1); + + entry = entries->data; + g_assert_cmpstr (entry->name_hint, ==, "indicator-test"); + g_assert_cmpstr (entry->accessible_desc, ==, "Test indicator"); + g_assert_cmpstr (gtk_label_get_label (entry->label), ==, "Test"); + { + GIcon *icon; + gchar *iconstr; + + gtk_image_get_gicon (entry->image, &icon, NULL); + g_assert (icon); + + iconstr = g_icon_to_string (icon); + g_assert_cmpstr (iconstr, ==, "indicator-test"); + + g_free (iconstr); + } + { + GList *children; + GtkMenuItem *item; + + g_assert (entry->menu != NULL); + + children = gtk_container_get_children (GTK_CONTAINER (entry->menu)); + g_assert_cmpint (g_list_length (children), ==, 1); + + item = children->data; + g_assert (GTK_IS_MENU_ITEM (item)); + g_assert (gtk_widget_is_sensitive (GTK_WIDGET (item))); + g_assert_cmpstr (gtk_menu_item_get_label (item), ==, "Show"); + + g_list_free (children); + } + + g_list_free (entries); + g_main_loop_unref (loop); + g_object_unref (indicator); +} + int main (int argc, char **argv) { + /* gvfs, dconf, and appmenu-gtk leak GDbusConnections, which confuses + * g_test_dbus_down. Make sure we're not using any of those. + */ + g_setenv ("GIO_USE_VFS", "local", TRUE); + g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); + g_unsetenv ("UBUNTU_MENUPROXY"); + g_test_init (&argc, &argv, NULL); gtk_init (&argc, &argv); - g_test_add_func ("/indicator-ng/non-existing", test_non_existing); - g_test_add_func ("/indicator-ng/instantiation", test_instantiation); - g_test_add_func ("/indicator-ng/instantiation-with-profile", test_instantiation_with_profile); + indicator_ng_test_add ("non-existing", test_non_existing); + indicator_ng_test_add ("instantiation", test_instantiation); + indicator_ng_test_add ("instantiation-with-profile", test_instantiation_with_profile); + indicator_ng_test_add ("menu", test_menu); return g_test_run (); } -- cgit v1.2.3 From af0137c6d8061570d484237d57cbfa6b713fdc39 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 23 Jan 2013 19:34:43 +0100 Subject: indicator-ng: test indicator_ng_get_property --- tests/test-indicator-ng.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test-indicator-ng.c b/tests/test-indicator-ng.c index 827cc98..185b3ee 100644 --- a/tests/test-indicator-ng.c +++ b/tests/test-indicator-ng.c @@ -57,6 +57,21 @@ test_instantiation (void) g_assert_cmpstr (indicator_ng_get_service_file (indicator), ==, SRCDIR "/com.canonical.test.nosuchservice.indicator"); g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "desktop"); + { + gchar *service_file; + gchar *profile; + + g_object_get (indicator, "service-file", &service_file, + "profile", &profile, + NULL); + + g_assert_cmpstr (service_file, ==, SRCDIR "/com.canonical.test.nosuchservice.indicator"); + g_assert_cmpstr (profile, ==, "desktop"); + + g_free (service_file); + g_free (profile); + } + loop = g_main_loop_new (NULL, FALSE); g_timeout_add (200, stop_main_loop, loop); g_main_loop_run (loop); -- cgit v1.2.3 From e096111ef669c0425686ac87c7e60d6686032ebe Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 23 Jan 2013 20:05:45 +0100 Subject: Add trim-lcov.py It strips some unreachable branches from the coverage reports, such as g_return_if_fail. --- Makefile.am.coverage | 4 +++- trim-lcov.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100755 trim-lcov.py diff --git a/Makefile.am.coverage b/Makefile.am.coverage index fb97747..dc3b9c8 100644 --- a/Makefile.am.coverage +++ b/Makefile.am.coverage @@ -1,6 +1,8 @@ # Coverage targets +EXTRA_DIST = trim-lcov.py + .PHONY: clean-gcno clean-gcda \ coverage-html generate-coverage-html clean-coverage-html \ coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr @@ -23,7 +25,7 @@ coverage-html: clean-gcda generate-coverage-html: @echo Collecting coverage data - $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool + $(LCOV) --directory $(top_builddir) --capture --no-checksum --compat-libtool | $(top_srcdir)/trim-lcov.py > coverage.info LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info clean-coverage-html: clean-gcda diff --git a/trim-lcov.py b/trim-lcov.py new file mode 100755 index 0000000..c1709c3 --- /dev/null +++ b/trim-lcov.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + +# This script removes branch and/or line coverage data for lines that +# contain a particular substring. +# +# In the interest of "fairness" it removes all branch or coverage data +# when a match is found -- not just negative data. It is therefore +# likely that running this script will actually reduce the total number +# of lines and branches that are marked as covered (in absolute terms). +# +# This script intentionally avoids checking for errors. Any exceptions +# will trigger make to fail. +# +# Author: Ryan Lortie + +import sys + +line_suppress = ['g_assert_not_reached'] +branch_suppress = ['g_assert', 'g_return_if_fail', 'g_return_val_if_fail', 'G_DEFINE_TYPE'] + +def check_suppress(suppressions, source, data): + line, _, rest = data.partition(',') + line = int(line) - 1 + + assert line < len(source) + + for suppression in suppressions: + if suppression in source[line]: + return True + + return False + +source = [] +for line in sys.stdin: + line = line[:-1] + + keyword, _, rest = line.partition(':') + + # Source file + if keyword == 'SF': + source = file(rest).readlines() + + # Branch coverage data + elif keyword == 'BRDA': + if check_suppress(branch_suppress, source, rest): + continue + + # Line coverage data + elif keyword == 'DA': + if check_suppress(line_suppress, source, rest): + continue + + print line -- cgit v1.2.3 From f3f4e1fc318c5ac200208c78e90a59926e929fba Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 24 Jan 2013 10:23:35 +0100 Subject: indicator-ng: use an IndicatorObjectEntry internally We needed the entry anyway (that's what indicator_ng_get_entry was for). --- libindicator/indicator-ng.c | 109 +++++++++++--------------------------------- 1 file changed, 26 insertions(+), 83 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 975ae9f..78cc80f 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -18,9 +18,7 @@ struct _IndicatorNg GActionGroup *actions; GMenuModel *menu; - GtkWidget *label; - GtkWidget *image; - GtkWidget *gtkmenu; + IndicatorObjectEntry entry; gchar *accessible_desc; }; @@ -38,21 +36,6 @@ enum static GParamSpec *properties[N_PROPERTIES]; -static IndicatorObjectEntry * -indicator_ng_get_entry (IndicatorNg *self) -{ - GList *entries; - IndicatorObjectEntry *entry; - - entries = INDICATOR_OBJECT_GET_CLASS (self)->get_entries (INDICATOR_OBJECT (self)); - g_return_val_if_fail (entries != NULL, NULL); - - entry = entries->data; - - g_list_free (entries); - return entry; -} - static void indicator_ng_get_property (GObject *object, guint property_id, @@ -104,7 +87,7 @@ indicator_ng_free_actions_and_menu (IndicatorNg *self) { if (self->actions) { - gtk_widget_insert_action_group (self->gtkmenu, "indicator", NULL); + gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", NULL); g_signal_handlers_disconnect_by_data (self->actions, self); g_clear_object (&self->actions); } @@ -129,9 +112,9 @@ indicator_ng_dispose (GObject *object) indicator_ng_free_actions_and_menu (self); - g_clear_object (&self->label); - g_clear_object (&self->image); - g_clear_object (&self->gtkmenu); + g_clear_object (&self->entry.label); + g_clear_object (&self->entry.image); + g_clear_object (&self->entry.menu); G_OBJECT_CLASS (indicator_ng_parent_class)->dispose (object); } @@ -150,62 +133,23 @@ indicator_ng_finalize (GObject *object) G_OBJECT_CLASS (indicator_ng_parent_class)->finalize (object); } -static GtkLabel * -indicator_ng_get_label (IndicatorObject *io) -{ - IndicatorNg *self = INDICATOR_NG (io); - - gtk_widget_show (self->label); - - return GTK_LABEL (self->label); -} - -static GtkImage * -indicator_ng_get_image (IndicatorObject *io) -{ - IndicatorNg *self = INDICATOR_NG (io); - - gtk_widget_show (self->image); - - return GTK_IMAGE (self->image); -} - -static GtkMenu * -indicator_ng_get_menu (IndicatorObject *io) +static GList * +indicator_ng_get_entries (IndicatorObject *io) { IndicatorNg *self = INDICATOR_NG (io); - return GTK_MENU (self->gtkmenu); -} - -static const gchar * -indicator_ng_get_accessible_desc (IndicatorObject *io) -{ - IndicatorNg *self = INDICATOR_NG (io); - - return self->accessible_desc; -} - -static const gchar * -indicator_ng_get_name_hint (IndicatorObject *io) -{ - IndicatorNg *self = INDICATOR_NG (io); - - return self->name; + return g_list_append (NULL, &self->entry); } static void indicator_ng_set_accessible_desc (IndicatorNg *self, const gchar *accessible_desc) { - IndicatorObjectEntry *entry; - - entry = indicator_ng_get_entry (self); - g_free (self->accessible_desc); self->accessible_desc = g_strdup (accessible_desc); - g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entry); + self->entry.accessible_desc = self->accessible_desc; + g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, &self->entry); } static gboolean @@ -255,12 +199,15 @@ indicator_ng_update_entry (IndicatorNg *self) gchar *iconstr; gchar *accessible_desc; gboolean visible; + gboolean has_icon; g_variant_get (state, "(sssb)", &label, &iconstr, &accessible_desc, &visible); - gtk_label_set_label (GTK_LABEL (self->label), label); - if (!gtk_image_set_from_gicon_string (GTK_IMAGE (self->image), iconstr)) - gtk_widget_hide (self->image); + gtk_label_set_label (GTK_LABEL (self->entry.label), label); + + has_icon = gtk_image_set_from_gicon_string (self->entry.image, iconstr); + gtk_widget_set_visible (GTK_WIDGET (self->entry.image), has_icon); + indicator_ng_set_accessible_desc (self, accessible_desc); indicator_object_set_visible (INDICATOR_OBJECT (self), visible); @@ -303,7 +250,7 @@ indicator_ng_menu_changed (GMenuModel *menu, popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); if (popup) { - gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->gtkmenu), popup, NULL, TRUE); + gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->entry.menu), popup, NULL, TRUE); g_object_unref (popup); } @@ -326,7 +273,7 @@ indicator_ng_service_appeared (GDBusConnection *connection, g_assert (!self->menu); self->actions = G_ACTION_GROUP (g_dbus_action_group_get (connection, name_owner, self->object_path)); - gtk_widget_insert_action_group (self->gtkmenu, "indicator", self->actions); + gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", self->actions); g_signal_connect_swapped (self->actions, "action-added", G_CALLBACK (indicator_ng_update_entry), self); g_signal_connect_swapped (self->actions, "action-removed", G_CALLBACK (indicator_ng_update_entry), self); g_signal_connect_swapped (self->actions, "action-state-changed", G_CALLBACK (indicator_ng_update_entry), self); @@ -377,10 +324,7 @@ indicator_ng_initable_init (GInitable *initable, (bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error)) && (self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error))) { - IndicatorObjectEntry *entry; - - entry = indicator_ng_get_entry (self); - entry->name_hint = self->name; + self->entry.name_hint = self->name; self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, bus_name, @@ -407,11 +351,7 @@ indicator_ng_class_init (IndicatorNgClass *class) object_class->dispose = indicator_ng_dispose; object_class->finalize = indicator_ng_finalize; - io_class->get_label = indicator_ng_get_label; - io_class->get_image = indicator_ng_get_image; - io_class->get_menu = indicator_ng_get_menu; - io_class->get_accessible_desc = indicator_ng_get_accessible_desc; - io_class->get_name_hint = indicator_ng_get_name_hint; + io_class->get_entries = indicator_ng_get_entries; properties[PROP_SERVICE_FILE] = g_param_spec_string ("service-file", "Service file", @@ -441,15 +381,18 @@ indicator_ng_initable_iface_init (GInitableIface *initable) static void indicator_ng_init (IndicatorNg *self) { - self->label = g_object_ref_sink (gtk_label_new (NULL)); - self->image = g_object_ref_sink (gtk_image_new ()); - self->gtkmenu = g_object_ref_sink (gtk_menu_new ()); + self->entry.label = g_object_ref_sink (gtk_label_new (NULL)); + gtk_widget_show (GTK_WIDGET (self->entry.label)); + + self->entry.image = g_object_ref_sink (gtk_image_new ()); + self->entry.menu = g_object_ref_sink (gtk_menu_new ()); /* work around IndicatorObject's warning that the accessible * description is missing. We never set it on construction, but when * the menu model has arrived on the bus. */ self->accessible_desc = g_strdup (""); + self->entry.accessible_desc = self->accessible_desc; indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); } -- cgit v1.2.3 From 05535f5916cdcb3a23334b6cb7b323f5508257ef Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 24 Jan 2013 11:22:39 +0100 Subject: indicator-ng: show broken image when g_icon_for_string returns NULL --- libindicator/indicator-ng.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 78cc80f..be73eb3 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -152,28 +152,33 @@ indicator_ng_set_accessible_desc (IndicatorNg *self, g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, &self->entry); } -static gboolean -gtk_image_set_from_gicon_string (GtkImage *img, - const gchar *str) +static void +indicator_ng_set_icon_from_string (IndicatorNg *self, + const gchar *str) { GIcon *icon; GError *error = NULL; - icon = str ? g_icon_new_for_string (str, &error) : NULL; + if (str == NULL || *str == '\0') + { + gtk_image_clear (self->entry.image); + gtk_widget_hide (GTK_WIDGET (self->entry.image)); + return; + } + + gtk_widget_show (GTK_WIDGET (self->entry.image)); + + icon = g_icon_new_for_string (str, &error); if (icon) { - gtk_image_set_from_gicon (img, icon, GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_image_set_from_gicon (self->entry.image, icon, GTK_ICON_SIZE_LARGE_TOOLBAR); g_object_unref (icon); - return TRUE; } else { - if (error) - { - g_warning ("invalid icon string '%s': %s", str, error->message); - g_error_free (error); - } - return FALSE; + g_warning ("invalid icon string '%s': %s", str, error->message); + gtk_image_set_from_stock (self->entry.image, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_LARGE_TOOLBAR); + g_error_free (error); } } @@ -199,15 +204,11 @@ indicator_ng_update_entry (IndicatorNg *self) gchar *iconstr; gchar *accessible_desc; gboolean visible; - gboolean has_icon; g_variant_get (state, "(sssb)", &label, &iconstr, &accessible_desc, &visible); gtk_label_set_label (GTK_LABEL (self->entry.label), label); - - has_icon = gtk_image_set_from_gicon_string (self->entry.image, iconstr); - gtk_widget_set_visible (GTK_WIDGET (self->entry.image), has_icon); - + indicator_ng_set_icon_from_string (self, iconstr); indicator_ng_set_accessible_desc (self, accessible_desc); indicator_object_set_visible (INDICATOR_OBJECT (self), visible); -- cgit v1.2.3 From 088b36be863ed966426754b1505009967254293c Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 24 Jan 2013 11:25:11 +0100 Subject: indicator-ng: save unnecessary allocations by using "&" in g_variant_get --- libindicator/indicator-ng.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index be73eb3..b7ea334 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -200,21 +200,17 @@ indicator_ng_update_entry (IndicatorNg *self) state = g_action_group_get_action_state (self->actions, self->header_action); if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) { - gchar *label; - gchar *iconstr; - gchar *accessible_desc; + const gchar *label; + const gchar *iconstr; + const gchar *accessible_desc; gboolean visible; - g_variant_get (state, "(sssb)", &label, &iconstr, &accessible_desc, &visible); + g_variant_get (state, "(&s&s&sb)", &label, &iconstr, &accessible_desc, &visible); gtk_label_set_label (GTK_LABEL (self->entry.label), label); indicator_ng_set_icon_from_string (self, iconstr); indicator_ng_set_accessible_desc (self, accessible_desc); indicator_object_set_visible (INDICATOR_OBJECT (self), visible); - - g_free (label); - g_free (iconstr); - g_free (accessible_desc); } else g_warning ("the action of the indicator menu item must have state with type (sssb)"); -- cgit v1.2.3 From db1caf23cdfb98ce99086eebe46660928b43f6e9 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 24 Jan 2013 11:29:24 +0100 Subject: indicator-ng: document error conditions in menu_changed --- libindicator/indicator-ng.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index b7ea334..82af030 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -228,6 +228,10 @@ indicator_ng_menu_changed (GMenuModel *menu, { IndicatorNg *self = user_data; + /* The menu may only contain one item (the indicator title menu). + * Thus, the position is always 0, and there is either exactly one + * item added or exactly one item removed. + */ g_return_if_fail (position == 0); g_return_if_fail (added < 2 && removed < 2 && added ^ removed); -- cgit v1.2.3 From f02ca5a6b148d7ab6c33692da84f4601af4fd8c8 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 24 Jan 2013 11:39:12 +0100 Subject: indicator-ng: check return value of g_menu_model_get_item_attribute If it returns false, we'd use uninitialized memory. --- libindicator/indicator-ng.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 82af030..003ec70 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -244,9 +244,11 @@ indicator_ng_menu_changed (GMenuModel *menu, gchar *action; g_clear_pointer (&self->header_action, g_free); - g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action); - if (action && g_str_has_prefix (action, "indicator.")) - self->header_action = g_strdup (action + 10); + if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action) && + g_str_has_prefix (action, "indicator.")) + { + self->header_action = g_strdup (action + 10); + } popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); if (popup) -- cgit v1.2.3 From 82ecfaaa89684a7504913fb039d0dd8302c82ec9 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 25 Jan 2013 10:29:57 +0100 Subject: indicator-ng: require header item to have x-canonical-type set --- libindicator/indicator-ng.c | 53 ++++++++++++++++++++++++++++++------------ tests/indicator-test-service.c | 1 + 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 003ec70..05ece8d 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -219,6 +219,23 @@ indicator_ng_update_entry (IndicatorNg *self) g_variant_unref (state); } +static gboolean +indicator_ng_menu_item_is_of_type (GMenuModel *menu, + gint index, + const gchar *expected_type) +{ + gchar *type; + gboolean has_type = FALSE; + + if (g_menu_model_get_item_attribute (menu, index, "x-canonical-type", "s", &type)) + { + has_type = g_str_equal (type, expected_type); + g_free (type); + } + + return has_type; +} + static void indicator_ng_menu_changed (GMenuModel *menu, gint position, @@ -240,26 +257,32 @@ indicator_ng_menu_changed (GMenuModel *menu, if (added) { - GMenuModel *popup; - gchar *action; - g_clear_pointer (&self->header_action, g_free); - if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action) && - g_str_has_prefix (action, "indicator.")) - { - self->header_action = g_strdup (action + 10); - } - popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); - if (popup) + if (indicator_ng_menu_item_is_of_type (self->menu, 0, "com.canonical.indicator.root")) { - gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->entry.menu), popup, NULL, TRUE); - g_object_unref (popup); - } + GMenuModel *popup; + gchar *action; - indicator_ng_update_entry (self); + if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action) && + g_str_has_prefix (action, "indicator.")) + { + self->header_action = g_strdup (action + 10); + } - g_free (action); + popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); + if (popup) + { + gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->entry.menu), popup, NULL, TRUE); + g_object_unref (popup); + } + + indicator_ng_update_entry (self); + + g_free (action); + } + else + g_warning ("indicator menu item must be of type 'com.canonical.indicator.root'"); } } diff --git a/tests/indicator-test-service.c b/tests/indicator-test-service.c index 3a6a0b2..0393677 100644 --- a/tests/indicator-test-service.c +++ b/tests/indicator-test-service.c @@ -81,6 +81,7 @@ main (int argc, char **argv) submenu = g_menu_new (); g_menu_append (submenu, "Show", "indicator.show"); item = g_menu_item_new (NULL, "indicator._header"); + g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.root"); g_menu_item_set_submenu (item, G_MENU_MODEL (submenu)); indicator.menu = g_menu_new (); g_menu_append_item (indicator.menu, item); -- cgit v1.2.3 From 70480923e6151c8173032710beb99707bb5b9485 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 25 Jan 2013 10:46:58 +0100 Subject: indicator-ng: lazily allocate entry.label and entry.image Most indicators only need one of those. --- libindicator/indicator-ng.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 05ece8d..f27fbf0 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -161,11 +161,17 @@ indicator_ng_set_icon_from_string (IndicatorNg *self, if (str == NULL || *str == '\0') { - gtk_image_clear (self->entry.image); - gtk_widget_hide (GTK_WIDGET (self->entry.image)); + if (self->entry.image) + { + gtk_image_clear (self->entry.image); + gtk_widget_hide (GTK_WIDGET (self->entry.image)); + } return; } + if (!self->entry.image) + self->entry.image = g_object_ref_sink (gtk_image_new ()); + gtk_widget_show (GTK_WIDGET (self->entry.image)); icon = g_icon_new_for_string (str, &error); @@ -182,6 +188,24 @@ indicator_ng_set_icon_from_string (IndicatorNg *self, } } +static void +indicator_ng_set_label (IndicatorNg *self, + const gchar *label) +{ + if (label == NULL || *label == '\0') + { + if (self->entry.label) + gtk_widget_hide (GTK_WIDGET (self->entry.label)); + return; + } + + if (!self->entry.label) + self->entry.label = g_object_ref_sink (gtk_label_new (NULL)); + + gtk_label_set_label (GTK_LABEL (self->entry.label), label); + gtk_widget_show (GTK_WIDGET (self->entry.label)); +} + static void indicator_ng_update_entry (IndicatorNg *self) { @@ -207,7 +231,7 @@ indicator_ng_update_entry (IndicatorNg *self) g_variant_get (state, "(&s&s&sb)", &label, &iconstr, &accessible_desc, &visible); - gtk_label_set_label (GTK_LABEL (self->entry.label), label); + indicator_ng_set_label (self, label); indicator_ng_set_icon_from_string (self, iconstr); indicator_ng_set_accessible_desc (self, accessible_desc); indicator_object_set_visible (INDICATOR_OBJECT (self), visible); @@ -407,10 +431,6 @@ indicator_ng_initable_iface_init (GInitableIface *initable) static void indicator_ng_init (IndicatorNg *self) { - self->entry.label = g_object_ref_sink (gtk_label_new (NULL)); - gtk_widget_show (GTK_WIDGET (self->entry.label)); - - self->entry.image = g_object_ref_sink (gtk_image_new ()); self->entry.menu = g_object_ref_sink (gtk_menu_new ()); /* work around IndicatorObject's warning that the accessible -- cgit v1.2.3 From 9c857a30aa23445708c7ed7f1d00a7d0986946bd Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 25 Jan 2013 11:11:59 +0100 Subject: indicator-ng: use indicator_image_helper gtk_icon_set_from_gicon doesn't scale rectangular icons correctly. This adds indicator_image_helper_update_from_gicon() and makes refresh_image() set a broken image instead of erroring out when an icon couldn't be found. --- libindicator/indicator-image-helper.c | 24 ++++++++++++++++++++---- libindicator/indicator-image-helper.h | 8 +++++--- libindicator/indicator-ng.c | 3 ++- tests/test-indicator-ng.c | 13 +------------ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/libindicator/indicator-image-helper.c b/libindicator/indicator-image-helper.c index 63488a1..382d0c9 100644 --- a/libindicator/indicator-image-helper.c +++ b/libindicator/indicator-image-helper.c @@ -62,7 +62,12 @@ refresh_image (GtkImage * image) /* Grab the filename */ icon_filename = gtk_icon_info_get_filename(icon_info); } - g_return_if_fail(icon_filename != NULL); /* An error because we don't have a filename */ + + /* show a broken image if we don't have a filename */ + if (icon_filename == NULL) { + gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_LARGE_TOOLBAR); + return; + } /* Build a pixbuf */ GError * error = NULL; @@ -132,7 +137,8 @@ indicator_image_helper (const gchar * name) /* Build us an image */ GtkImage * image = GTK_IMAGE(gtk_image_new()); - indicator_image_helper_update(image, name); + if (name) + indicator_image_helper_update(image, name); return image; } @@ -144,17 +150,27 @@ indicator_image_helper_update (GtkImage * image, const gchar * name) g_return_if_fail(name != NULL); g_return_if_fail(name[0] != '\0'); g_return_if_fail(GTK_IS_IMAGE(image)); - gboolean seen_previously = FALSE; /* Build us a GIcon */ GIcon * icon_names = g_themed_icon_new_with_default_fallbacks(name); g_warn_if_fail(icon_names != NULL); g_return_if_fail(icon_names != NULL); + indicator_image_helper_update_from_gicon (image, icon_names); + + g_object_unref (icon_names); + return; +} + +void +indicator_image_helper_update_from_gicon (GtkImage *image, GIcon *icon) +{ + gboolean seen_previously = FALSE; + seen_previously = (g_object_get_data(G_OBJECT(image), INDICATOR_NAMES_DATA) != NULL); /* Attach our names to the image */ - g_object_set_data_full(G_OBJECT(image), INDICATOR_NAMES_DATA, icon_names, g_object_unref); + g_object_set_data_full(G_OBJECT(image), INDICATOR_NAMES_DATA, g_object_ref (icon), g_object_unref); /* Put the pixbuf in */ refresh_image(image); diff --git a/libindicator/indicator-image-helper.h b/libindicator/indicator-image-helper.h index 77e2f0a..290f4e2 100644 --- a/libindicator/indicator-image-helper.h +++ b/libindicator/indicator-image-helper.h @@ -26,8 +26,10 @@ License along with this library. If not, see #include -GtkImage * indicator_image_helper (const gchar * name); -void indicator_image_helper_update (GtkImage * image, - const gchar * name); +GtkImage * indicator_image_helper (const gchar * name); +void indicator_image_helper_update (GtkImage * image, + const gchar * name); +void indicator_image_helper_update_from_gicon (GtkImage * image, + GIcon * icon); #endif /* __INDICATOR_IMAGE_HELPER_H__ */ diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index f27fbf0..f4c98c6 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -1,5 +1,6 @@ #include "indicator-ng.h" +#include "indicator-image-helper.h" #include @@ -177,7 +178,7 @@ indicator_ng_set_icon_from_string (IndicatorNg *self, icon = g_icon_new_for_string (str, &error); if (icon) { - gtk_image_set_from_gicon (self->entry.image, icon, GTK_ICON_SIZE_LARGE_TOOLBAR); + indicator_image_helper_update_from_gicon (self->entry.image, icon); g_object_unref (icon); } else diff --git a/tests/test-indicator-ng.c b/tests/test-indicator-ng.c index 185b3ee..150cc37 100644 --- a/tests/test-indicator-ng.c +++ b/tests/test-indicator-ng.c @@ -122,18 +122,7 @@ test_menu (void) g_assert_cmpstr (entry->name_hint, ==, "indicator-test"); g_assert_cmpstr (entry->accessible_desc, ==, "Test indicator"); g_assert_cmpstr (gtk_label_get_label (entry->label), ==, "Test"); - { - GIcon *icon; - gchar *iconstr; - - gtk_image_get_gicon (entry->image, &icon, NULL); - g_assert (icon); - - iconstr = g_icon_to_string (icon); - g_assert_cmpstr (iconstr, ==, "indicator-test"); - - g_free (iconstr); - } + g_assert (gtk_image_get_storage_type (entry->image) == GTK_IMAGE_STOCK); { GList *children; GtkMenuItem *item; -- cgit v1.2.3 From c02297da219a3a44d061543ff64bb953e2065128 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 25 Jan 2013 11:14:13 +0100 Subject: indicator-ng: use strlen instead of hard coding the length --- libindicator/indicator-ng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index f4c98c6..bd0a19c 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -292,7 +292,7 @@ indicator_ng_menu_changed (GMenuModel *menu, if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action) && g_str_has_prefix (action, "indicator.")) { - self->header_action = g_strdup (action + 10); + self->header_action = g_strdup (action + strlen ("indicator.")); } popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); -- cgit v1.2.3 From dce947bb991d5d3f6cca2752da17afd186e42acd Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 25 Jan 2013 11:55:02 +0100 Subject: trim-lcov.py: add license header --- trim-lcov.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/trim-lcov.py b/trim-lcov.py index c1709c3..8465e95 100755 --- a/trim-lcov.py +++ b/trim-lcov.py @@ -1,5 +1,23 @@ #!/usr/bin/python +# Copyright 2013 Canonical Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# version 3.0 as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License version 3.0 for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library. If not, see +# . +# +# Author: Ryan Lortie + + # This script removes branch and/or line coverage data for lines that # contain a particular substring. # @@ -10,8 +28,6 @@ # # This script intentionally avoids checking for errors. Any exceptions # will trigger make to fail. -# -# Author: Ryan Lortie import sys -- cgit v1.2.3 From 8ff25e5375c3a0cb399fa687bf541f1cc0f4a020 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 30 Jan 2013 17:49:32 +0100 Subject: indicator-ng: simplify flow in initable_init --- libindicator/indicator-ng.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index bd0a19c..6ec02b4 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -362,29 +362,28 @@ indicator_ng_initable_init (GInitable *initable, gchar *bus_name = NULL; keyfile = g_key_file_new (); - if (!g_key_file_load_from_file (keyfile, - self->service_file, - G_KEY_FILE_NONE, - error)) - { - g_key_file_free (keyfile); - return FALSE; - } + if (!g_key_file_load_from_file (keyfile, self->service_file, G_KEY_FILE_NONE, error)) + goto out; - if ((self->name = g_key_file_get_string (keyfile, "Indicator Service", "Name", error)) && - (bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error)) && - (self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error))) - { - self->entry.name_hint = self->name; - - self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, - bus_name, - G_BUS_NAME_WATCHER_FLAGS_AUTO_START, - indicator_ng_service_appeared, - indicator_ng_service_vanished, - self, NULL); - } + if (!(self->name = g_key_file_get_string (keyfile, "Indicator Service", "Name", error))) + goto out; + + self->entry.name_hint = self->name; + + if (!(bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error))) + goto out; + + if (!(self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error))) + goto out; + + self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, + bus_name, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + indicator_ng_service_appeared, + indicator_ng_service_vanished, + self, NULL); +out: g_free (bus_name); g_key_file_free (keyfile); -- cgit v1.2.3 From f9f86fbfa558e83d8591fbec43e95dcb5cc34584 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 6 Feb 2013 15:37:38 -0500 Subject: indicator-ng: try to restart the service when it crashes This uses a (slightly) awkward heuristic: when the well-known name vanishes from the session bus, it only restarts the service when it didn't explicitly hide the indicator before. --- libindicator/indicator-ng.c | 79 ++++++++++++++++++++++++++++++++++++++--- libindicator/indicator-object.c | 7 ++++ libindicator/indicator-object.h | 1 + 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 6ec02b4..b2a95fe 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -11,16 +11,20 @@ struct _IndicatorNg gchar *service_file; gchar *name; gchar *object_path; + gchar *bus_name; gchar *profile; gchar *header_action; guint name_watch_id; + GDBusConnection *session_bus; GActionGroup *actions; GMenuModel *menu; IndicatorObjectEntry entry; gchar *accessible_desc; + + gint64 last_service_restart; }; static void indicator_ng_initable_iface_init (GInitableIface *initable); @@ -111,6 +115,8 @@ indicator_ng_dispose (GObject *object) self->name_watch_id = 0; } + g_clear_object (&self->session_bus); + indicator_ng_free_actions_and_menu (self); g_clear_object (&self->entry.label); @@ -128,6 +134,7 @@ indicator_ng_finalize (GObject *object) g_free (self->service_file); g_free (self->name); g_free (self->object_path); + g_free (self->bus_name); g_free (self->accessible_desc); g_free (self->header_action); @@ -323,6 +330,8 @@ indicator_ng_service_appeared (GDBusConnection *connection, g_assert (!self->actions); g_assert (!self->menu); + self->session_bus = g_object_ref (connection); + self->actions = G_ACTION_GROUP (g_dbus_action_group_get (connection, name_owner, self->object_path)); gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", self->actions); g_signal_connect_swapped (self->actions, "action-added", G_CALLBACK (indicator_ng_update_entry), self); @@ -340,6 +349,41 @@ indicator_ng_service_appeared (GDBusConnection *connection, g_free (menu_object_path); } +static void +indicator_ng_service_started (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + IndicatorNg *self = user_data; + GError *error = NULL; + GVariant *reply; + + reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), result, &error); + if (!reply) + { + g_warning ("Could not activate service '%s': %s", self->name, error->message); + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); + g_error_free (error); + return; + } + + switch (g_variant_get_uint32 (reply)) + { + case 1: /* DBUS_START_REPLY_SUCCESS */ + break; + + case 2: /* DBUS_START_REPLY_ALREADY_RUNNING */ + g_warning ("could not start service '%s': it is already running", self->name); + indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); + break; + + default: + g_assert_not_reached (); + } + + g_variant_unref (reply); +} + static void indicator_ng_service_vanished (GDBusConnection *connection, const gchar *name, @@ -349,7 +393,34 @@ indicator_ng_service_vanished (GDBusConnection *connection, indicator_ng_free_actions_and_menu (self); - indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); + /* Names may vanish because the service decided it doesn't need to + * show its indicator anymore, or because it crashed. Let's assume it + * crashes and restart it unless it explicitly hid its indicator. */ + + if (indicator_object_entry_is_visible (INDICATOR_OBJECT (self), &self->entry)) + { + gint64 now; + + /* take care not to start it if it repeatedly crashes */ + now = g_get_monotonic_time (); + if (now - self->last_service_restart < 1 * G_USEC_PER_SEC) + return; + + self->last_service_restart = now; + + g_dbus_connection_call (self->session_bus, + "org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "StartServiceByName", + g_variant_new ("(su)", self->bus_name, 0), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + indicator_ng_service_started, + self); + } } static gboolean @@ -359,7 +430,6 @@ indicator_ng_initable_init (GInitable *initable, { IndicatorNg *self = INDICATOR_NG (initable); GKeyFile *keyfile; - gchar *bus_name = NULL; keyfile = g_key_file_new (); if (!g_key_file_load_from_file (keyfile, self->service_file, G_KEY_FILE_NONE, error)) @@ -370,21 +440,20 @@ indicator_ng_initable_init (GInitable *initable, self->entry.name_hint = self->name; - if (!(bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error))) + if (!(self->bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error))) goto out; if (!(self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error))) goto out; self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, - bus_name, + self->bus_name, G_BUS_NAME_WATCHER_FLAGS_AUTO_START, indicator_ng_service_appeared, indicator_ng_service_vanished, self, NULL); out: - g_free (bus_name); g_key_file_free (keyfile); return self->name_watch_id > 0; diff --git a/libindicator/indicator-object.c b/libindicator/indicator-object.c index ba2d377..30a6543 100644 --- a/libindicator/indicator-object.c +++ b/libindicator/indicator-object.c @@ -935,3 +935,10 @@ set_property (GObject * object, } } +gboolean +indicator_object_entry_is_visible (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_return_val_if_fail (INDICATOR_IS_OBJECT (io), FALSE); + + return entry_get_private (io, entry)->visibility == ENTRY_VISIBLE; +} diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index d8551f9..3c1a203 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -199,6 +199,7 @@ GList * indicator_object_get_entries (IndicatorObject * io); guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry); guint indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry); void indicator_object_set_visible (IndicatorObject * io, gboolean visible); +gboolean indicator_object_entry_is_visible (IndicatorObject * io, IndicatorObjectEntry * entry); void indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); void indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp); void indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); -- cgit v1.2.3 From 88aa74b63ee73620f265a5cfa474fd19b5cf9ad7 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 6 Feb 2013 16:03:33 -0500 Subject: indicator-ng: don't hide the indicator if the service is already running --- libindicator/indicator-ng.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index b2a95fe..481a37c 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -374,7 +374,6 @@ indicator_ng_service_started (GObject *source_object, case 2: /* DBUS_START_REPLY_ALREADY_RUNNING */ g_warning ("could not start service '%s': it is already running", self->name); - indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); break; default: -- cgit v1.2.3 From 40d7c42d5212dc97ce6b07f05828fb62440d0694 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 14 Feb 2013 16:16:48 -0500 Subject: indicator-ng: add license header --- libindicator/indicator-ng.c | 18 ++++++++++++++++++ libindicator/indicator-ng.h | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/libindicator/indicator-ng.c b/libindicator/indicator-ng.c index 481a37c..f2b957b 100644 --- a/libindicator/indicator-ng.c +++ b/libindicator/indicator-ng.c @@ -1,3 +1,21 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ #include "indicator-ng.h" #include "indicator-image-helper.h" diff --git a/libindicator/indicator-ng.h b/libindicator/indicator-ng.h index 9f19958..f074a47 100644 --- a/libindicator/indicator-ng.h +++ b/libindicator/indicator-ng.h @@ -1,3 +1,22 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + #ifndef __INDICATOR_NG_H__ #define __INDICATOR_NG_H__ -- cgit v1.2.3