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 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 16 deletions(-) (limited to 'src/app-section.c') 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) { -- 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 ---------- 1 file changed, 10 deletions(-) (limited to 'src/app-section.c') 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) { -- 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(-) (limited to 'src/app-section.c') 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 ----------- 1 file changed, 11 deletions(-) (limited to 'src/app-section.c') 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) { -- 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. --- src/app-section.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'src/app-section.c') 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]); +} -- cgit v1.2.3