From 93db8c38f2252cb4d506d90721446c0ad524ca3b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Jun 2012 17:25:34 +0200 Subject: Add draws-attention flag to source actions AppSections watch those flags for associated sources and mux them into a draws-attention property for the whole section. --- src/app-section.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++- src/app-section.h | 1 + src/messages-service.c | 43 +++++++++----- 3 files changed, 180 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/app-section.c b/src/app-section.c index 472fb6c..35a842f 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -45,6 +45,8 @@ struct _AppSectionPrivate GMenuModel *remote_menu; GActionGroup *actions; + gboolean draws_attention; + guint name_watch_id; }; @@ -52,6 +54,7 @@ enum { PROP_0, PROP_APPINFO, PROP_ACTIONS, + PROP_DRAWS_ATTENTION, NUM_PROPERTIES }; @@ -74,6 +77,18 @@ static void activate_cb (GSimpleAction *action, gpointer userdata); static void app_section_set_app_info (AppSection *self, GDesktopAppInfo *appinfo); +static gboolean any_action_draws_attention (GActionGroup *group, + const gchar *ignored_action); +static void action_added (GActionGroup *group, + const gchar *action_name, + gpointer user_data); +static void action_state_changed (GActionGroup *group, + const gchar *action_name, + GVariant *value, + gpointer user_data); +static void action_removed (GActionGroup *group, + const gchar *action_name, + gpointer user_data); /* GObject Boilerplate */ G_DEFINE_TYPE (AppSection, app_section, G_TYPE_OBJECT); @@ -101,6 +116,12 @@ app_section_class_init (AppSectionClass *klass) G_TYPE_ACTION_GROUP, G_PARAM_READABLE); + properties[PROP_DRAWS_ATTENTION] = g_param_spec_boolean ("draws-attention", + "Draws attention", + "Whether the section currently draws attention", + FALSE, + G_PARAM_READABLE); + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); } @@ -120,6 +141,8 @@ app_section_init (AppSection *self) priv->menu = g_menu_new (); priv->static_shortcuts = g_simple_action_group_new (); + priv->draws_attention = FALSE; + return; } @@ -137,6 +160,10 @@ app_section_get_property (GObject *object, g_value_set_object (value, app_section_get_app_info (self)); break; + case PROP_DRAWS_ATTENTION: + g_value_set_boolean (value, app_section_get_draws_attention (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -174,7 +201,15 @@ app_section_dispose (GObject *object) priv->name_watch_id = 0; } - g_clear_object (&priv->actions); + if (priv->actions) { + g_object_disconnect (priv->actions, + "any_signal::action-added", action_added, self, + "any_signal::action-state-changed", action_state_changed, self, + "any_signal::action-removed", action_removed, self, + NULL); + g_clear_object (&priv->actions); + } + g_clear_object (&priv->remote_menu); if (priv->ids != NULL) { @@ -334,6 +369,13 @@ app_section_get_app_info (AppSection *self) return G_APP_INFO (priv->appinfo); } +gboolean +app_section_get_draws_attention (AppSection *self) +{ + AppSectionPrivate * priv = self->priv; + return priv->draws_attention; +} + static void application_vanished (GDBusConnection *bus, const gchar *name, @@ -367,6 +409,14 @@ app_section_set_object_path (AppSection *self, app_section_unset_object_path (self); priv->actions = G_ACTION_GROUP (g_dbus_action_group_get (bus, bus_name, object_path)); + + priv->draws_attention = any_action_draws_attention (priv->actions, NULL); + g_object_connect (priv->actions, + "signal::action-added", action_added, self, + "signal::action-state-changed", action_state_changed, self, + "signal::action-removed", action_removed, self, + NULL); + priv->remote_menu = G_MENU_MODEL (g_dbus_menu_model_get (bus, bus_name, object_path)); g_menu_append_section (priv->menu, NULL, priv->remote_menu); @@ -376,6 +426,7 @@ app_section_set_object_path (AppSection *self, self, NULL); 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_thaw_notify (G_OBJECT (self)); } @@ -396,7 +447,15 @@ app_section_unset_object_path (AppSection *self) g_bus_unwatch_name (priv->name_watch_id); priv->name_watch_id = 0; } - g_clear_object (&priv->actions); + + if (priv->actions) { + g_object_disconnect (priv->actions, + "any_signal::action-added", action_added, self, + "any_signal::action-state-changed", action_state_changed, self, + "any_signal::action-removed", action_removed, self, + NULL); + g_clear_object (&priv->actions); + } if (priv->remote_menu) { /* the last menu item points is linked to the app's menumodel */ @@ -405,6 +464,94 @@ app_section_unset_object_path (AppSection *self) g_clear_object (&priv->remote_menu); } + priv->draws_attention = FALSE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); } +static gboolean +action_draws_attention (GVariant *state) +{ + gboolean attention; + + if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)"))) + g_variant_get_child (state, 3, "b", &attention); + else + attention = FALSE; + + return attention; +} + +static gboolean +any_action_draws_attention (GActionGroup *group, + const gchar *ignored_action) +{ + gchar **actions; + gchar **it; + gboolean attention = FALSE; + + actions = g_action_group_list_actions (group); + + for (it = actions; *it && !attention; it++) { + GVariant *state; + + if (ignored_action && g_str_equal (ignored_action, *it)) + continue; + + state = g_action_group_get_action_state (group, *it); + if (state) { + attention = action_draws_attention (state); + g_variant_unref (state); + } + } + + g_strfreev (actions); + return attention; +} + +static void +action_added (GActionGroup *group, + const gchar *action_name, + gpointer user_data) +{ + AppSection *self = user_data; + GVariant *state; + + state = g_action_group_get_action_state (group, action_name); + if (state) { + self->priv->draws_attention |= action_draws_attention (state); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); + g_variant_unref (state); + } +} + +static void +action_state_changed (GActionGroup *group, + const gchar *action_name, + GVariant *value, + gpointer user_data) +{ + AppSection *self = user_data; + + self->priv->draws_attention = any_action_draws_attention (group, NULL); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); +} + +static void +action_removed (GActionGroup *group, + const gchar *action_name, + gpointer user_data) +{ + AppSection *self = user_data; + GVariant *state; + + state = g_action_group_get_action_state (group, action_name); + if (!state) + return; + + self->priv->draws_attention = any_action_draws_attention (group, action_name); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); + + g_variant_unref (state); +} diff --git a/src/app-section.h b/src/app-section.h index 36e091d..935f7a2 100644 --- a/src/app-section.h +++ b/src/app-section.h @@ -56,6 +56,7 @@ const gchar * app_section_get_desktop (AppSection * appitem); GActionGroup * app_section_get_actions (AppSection *self); GMenuModel * app_section_get_menu (AppSection *appitem); GAppInfo * app_section_get_app_info (AppSection *appitem); +gboolean app_section_get_draws_attention (AppSection *appitem); void app_section_set_object_path (AppSection *self, GDBusConnection *bus, const gchar *bus_name, diff --git a/src/messages-service.c b/src/messages-service.c index 8a08423..209abe5 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -73,6 +73,31 @@ actions_changed (GObject *object, g_free (id); } + +static gboolean +app_section_draws_attention (gpointer key, + gpointer value, + gpointer user_data) +{ + AppSection *section = value; + return app_section_get_draws_attention (section); +} + +static void +draws_attention_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GSimpleAction *clear; + gboolean attention; + + clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear")); + g_return_if_fail (clear != NULL); + + attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL; + g_simple_action_set_enabled (clear, attention); +} + static AppSection * add_application (const gchar *desktop_id) { @@ -98,6 +123,8 @@ add_application (const gchar *desktop_id) g_action_muxer_insert (action_muxer, id, app_section_get_actions (section)); g_signal_connect (section, "notify::actions", G_CALLBACK (actions_changed), NULL); + g_signal_connect (section, "notify::draws-attention", + G_CALLBACK (draws_attention_changed), NULL); /* TODO insert it at the right position (alphabetically by application name) */ menuitem = g_menu_item_new_section (NULL, app_section_get_menu (section)); @@ -132,6 +159,9 @@ remove_application (const char *desktop_id) 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); } else { g_warning ("could not remove '%s', it's not registered", desktop_id); @@ -210,15 +240,6 @@ change_status (GSimpleAction *action, g_message ("changing status to %s", g_variant_get_string (value, NULL)); } -static void -clear_action_handler (MessageServiceDbus *msd, - gboolean attention, - gpointer user_data) -{ - GSimpleAction *action = user_data; - g_simple_action_set_enabled (action, attention); -} - static void register_application (MessageServiceDbus *msd, const gchar *sender, @@ -336,10 +357,6 @@ main (int argc, char ** argv) action_muxer = g_action_muxer_new (); g_action_muxer_insert (action_muxer, NULL, G_ACTION_GROUP (actions)); - g_signal_connect (dbus_interface, MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED, - G_CALLBACK(clear_action_handler), - g_action_map_lookup_action (G_ACTION_MAP (actions), "clear")); - g_signal_connect (dbus_interface, MESSAGE_SERVICE_DBUS_SIGNAL_REGISTER_APPLICATION, G_CALLBACK (register_application), NULL); g_signal_connect (dbus_interface, MESSAGE_SERVICE_DBUS_SIGNAL_UNREGISTER_APPLICATION, -- cgit v1.2.3