From 3fbcc8ef800b2c0ef74fcdea17f6a1e5ec01cdff Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 13 Oct 2010 15:37:12 -0400 Subject: EShellBackend: Respond to EShell::prepare-for-quit signals. Listen for "prepare-for-quit" signals from the shell and inhibit shutdown until all the activities we're tracking are finalized. Also, add a couple supporting functions: gboolean e_shell_backend_is_busy (EShellBackend *shell_backend); void e_shell_backend_cancel_all (EShellBackend *shell_backend); These will eventually replace mail_msg_active() and mail_cancel_all(). --- doc/reference/shell/eshell-sections.txt | 2 + doc/reference/shell/tmpl/e-shell-backend.sgml | 5 + modules/mail/e-mail-shell-view-actions.c | 14 +++ shell/e-shell-backend.c | 155 ++++++++++++++++++++++++-- shell/e-shell-backend.h | 2 + 5 files changed, 170 insertions(+), 8 deletions(-) diff --git a/doc/reference/shell/eshell-sections.txt b/doc/reference/shell/eshell-sections.txt index 3dc6eb1bc4..5ef1b146d9 100644 --- a/doc/reference/shell/eshell-sections.txt +++ b/doc/reference/shell/eshell-sections.txt @@ -60,6 +60,8 @@ e_shell_backend_get_config_dir e_shell_backend_get_data_dir e_shell_backend_get_shell e_shell_backend_add_activity +e_shell_backend_cancel_all +e_shell_backend_is_busy e_shell_backend_start e_shell_backend_migrate diff --git a/doc/reference/shell/tmpl/e-shell-backend.sgml b/doc/reference/shell/tmpl/e-shell-backend.sgml index 20df66ac75..f0c2d3ebd4 100644 --- a/doc/reference/shell/tmpl/e-shell-backend.sgml +++ b/doc/reference/shell/tmpl/e-shell-backend.sgml @@ -34,6 +34,11 @@ EShellBackend @eshellbackend: the object which received the signal. @arg1: + + + + + diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c index 852edbd147..3c4871bffe 100644 --- a/modules/mail/e-mail-shell-view-actions.c +++ b/modules/mail/e-mail-shell-view-actions.c @@ -830,7 +830,21 @@ static void action_mail_stop_cb (GtkAction *action, EMailShellView *mail_shell_view) { + EShellView *shell_view; + EShellBackend *shell_backend; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + /* XXX There's some overlap here: activities submitted through + * the legacy MailMsg system might be cancelled twice, but + * it shouldn't cause problems. */ + + /* the old way */ mail_cancel_all (); + + /* the new way */ + e_shell_backend_cancel_all (shell_backend); } static void diff --git a/shell/e-shell-backend.c b/shell/e-shell-backend.c index d679c05ed5..0d7ff020c7 100644 --- a/shell/e-shell-backend.c +++ b/shell/e-shell-backend.c @@ -58,6 +58,11 @@ struct _EShellBackendPrivate { guint started : 1; }; +enum { + PROP_0, + PROP_BUSY +}; + enum { ACTIVITY_ADDED, ACTIVITY_REMOVED, @@ -73,35 +78,94 @@ shell_backend_activity_finalized_cb (EShellBackend *shell_backend, EActivity *finalized_activity) { g_queue_remove (shell_backend->priv->activities, finalized_activity); + + /* Only emit "notify::busy" when switching from busy to idle. */ + if (g_queue_is_empty (shell_backend->priv->activities)) + g_object_notify (G_OBJECT (shell_backend), "busy"); + g_object_unref (shell_backend); } +static void +shell_backend_notify_busy_cb (EShellBackend *shell_backend, + GParamSpec *pspec, + EActivity *activity) +{ + /* Unreferencing the EActivity allows the shell to + * proceed with shutdown. */ + if (!e_shell_backend_is_busy (shell_backend)) { + g_signal_handlers_disconnect_by_func ( + shell_backend, + shell_backend_notify_busy_cb, + activity); + g_object_unref (activity); + } +} + +static void +shell_backend_prepare_for_quit_cb (EShell *shell, + EActivity *activity, + EShellBackend *shell_backend) +{ + /* Referencing the EActivity delays shutdown; the + * reference count acts like a counting semaphore. */ + if (e_shell_backend_is_busy (shell_backend)) + g_signal_connect ( + shell_backend, "notify::busy", + G_CALLBACK (shell_backend_notify_busy_cb), + g_object_ref (activity)); +} + static GObject * shell_backend_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { - EShellBackendPrivate *priv; + EShellBackend *shell_backend; EShellBackendClass *class; EShellViewClass *shell_view_class; + EShell *shell; GObject *object; /* Chain up to parent's construct() method. */ object = G_OBJECT_CLASS (e_shell_backend_parent_class)->constructor ( type, n_construct_properties, construct_properties); - class = E_SHELL_BACKEND_GET_CLASS (object); - priv = E_SHELL_BACKEND_GET_PRIVATE (object); + shell_backend = E_SHELL_BACKEND (object); + shell = e_shell_backend_get_shell (shell_backend); /* Install a reference to ourselves in the * corresponding EShellViewClass structure. */ + class = E_SHELL_BACKEND_GET_CLASS (shell_backend); shell_view_class = g_type_class_ref (class->shell_view_type); - shell_view_class->shell_backend = g_object_ref (object); - priv->shell_view_class = shell_view_class; + shell_view_class->shell_backend = g_object_ref (shell_backend); + shell_backend->priv->shell_view_class = shell_view_class; + + g_signal_connect ( + shell, "prepare-for-quit", + G_CALLBACK (shell_backend_prepare_for_quit_cb), + shell_backend); return object; } +static void +shell_backend_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BUSY: + g_value_set_boolean ( + value, e_shell_backend_is_busy ( + E_SHELL_BACKEND (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + static void shell_backend_dispose (GObject *object) { @@ -185,6 +249,7 @@ e_shell_backend_class_init (EShellBackendClass *class) object_class = G_OBJECT_CLASS (class); object_class->constructor = shell_backend_constructor; + object_class->get_property = shell_backend_get_property; object_class->dispose = shell_backend_dispose; object_class->finalize = shell_backend_finalize; @@ -194,6 +259,21 @@ e_shell_backend_class_init (EShellBackendClass *class) class->get_config_dir = shell_backend_get_config_dir; class->get_data_dir = shell_backend_get_data_dir; + /** + * EShellBackend:busy + * + * Whether any activities are still in progress. + **/ + g_object_class_install_property ( + object_class, + PROP_BUSY, + g_param_spec_boolean ( + "busy", + "Busy", + "Whether any activities are still in progress", + FALSE, + G_PARAM_READABLE)); + /** * EShellBackend::activity-added * @shell_backend: the #EShellBackend that emitted the signal @@ -311,17 +391,22 @@ e_shell_backend_get_shell (EShellBackend *shell_backend) * @shell_backend: an #EShellBackend * @activity: an #EActivity * - * Emits an #EShellBackend::activity-added signal. + * Emits an #EShellBackend::activity-added signal and tracks the @activity + * until it is finalized. **/ void e_shell_backend_add_activity (EShellBackend *shell_backend, EActivity *activity) { + GCancellable *cancellable; + g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend)); g_return_if_fail (E_IS_ACTIVITY (activity)); - /* skip already cancelled activities */ - if (g_cancellable_is_cancelled (e_activity_get_cancellable (activity))) + cancellable = e_activity_get_cancellable (activity); + + /* Skip cancelled activities. */ + if (g_cancellable_is_cancelled (cancellable)) return; g_queue_push_tail (shell_backend->priv->activities, activity); @@ -334,6 +419,60 @@ e_shell_backend_add_activity (EShellBackend *shell_backend, g_object_ref (shell_backend)); g_signal_emit (shell_backend, signals[ACTIVITY_ADDED], 0, activity); + + /* Only emit "notify::busy" when switching from idle to busy. */ + if (g_queue_get_length (shell_backend->priv->activities) == 1) + g_object_notify (G_OBJECT (shell_backend), "busy"); +} + +/** + * e_shell_backend_is_busy: + * @shell_backend: an #EShellBackend + * + * Returns %TRUE if any activities passed to e_shell_backend_add_activity() + * are still in progress, %FALSE if the @shell_backend is currently idle. + * + * Returns: %TRUE if activities are still in progress + **/ +gboolean +e_shell_backend_is_busy (EShellBackend *shell_backend) +{ + g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE); + + return !g_queue_is_empty (shell_backend->priv->activities); +} + +/** + * e_shell_backend_cancel_all: + * @shell_backend: an #EShellBackend + * + * Cancels all activities passed to e_shell_backend_add_activity() that + * have not already been finalized. Note that an #EActivity can only be + * cancelled if it was given a #GCancellable object. + * + * Also, assuming all activities are cancellable, there may still be a + * delay before e_shell_backend_is_busy() returns %FALSE, because some + * activities may not be able to respond to the cancellation request + * immediately. Connect to the "notify::busy" signal if you need + * notification of @shell_backend becoming idle. + **/ +void +e_shell_backend_cancel_all (EShellBackend *shell_backend) +{ + GList *list, *iter; + + g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend)); + + list = g_queue_peek_head_link (shell_backend->priv->activities); + + for (iter = list; iter != NULL; iter = g_list_next (iter)) { + EActivity *activity; + GCancellable *cancellable; + + activity = E_ACTIVITY (iter->data); + cancellable = e_activity_get_cancellable (activity); + g_cancellable_cancel (cancellable); + } } /** diff --git a/shell/e-shell-backend.h b/shell/e-shell-backend.h index 055a2a3322..1810c8c6e5 100644 --- a/shell/e-shell-backend.h +++ b/shell/e-shell-backend.h @@ -124,6 +124,8 @@ const gchar * e_shell_backend_get_data_dir (EShellBackend *shell_backend); struct _EShell *e_shell_backend_get_shell (EShellBackend *shell_backend); void e_shell_backend_add_activity (EShellBackend *shell_backend, EActivity *activity); +gboolean e_shell_backend_is_busy (EShellBackend *shell_backend); +void e_shell_backend_cancel_all (EShellBackend *shell_backend); void e_shell_backend_start (EShellBackend *shell_backend); gboolean e_shell_backend_migrate (EShellBackend *shell_backend, gint major, -- cgit v1.2.3