[evolution] EShellBackend: Respond to EShell::prepare-for-quit signals.



commit 3fbcc8ef800b2c0ef74fcdea17f6a1e5ec01cdff
Author: Matthew Barnes <mbarnes redhat com>
Date:   Wed Oct 13 15:37:12 2010 -0400

    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 3dc6eb1..5ef1b14 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
 <SUBSECTION Standard>
diff --git a/doc/reference/shell/tmpl/e-shell-backend.sgml b/doc/reference/shell/tmpl/e-shell-backend.sgml
index 20df66a..f0c2d3e 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: 
 
+<!-- ##### ARG EShellBackend:busy ##### -->
+<para>
+
+</para>
+
 <!-- ##### FUNCTION e_shell_backend_compare ##### -->
 <para>
 
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index 852edbd..3c4871b 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 d679c05..0d7ff02 100644
--- a/shell/e-shell-backend.c
+++ b/shell/e-shell-backend.c
@@ -59,6 +59,11 @@ struct _EShellBackendPrivate {
 };
 
 enum {
+	PROP_0,
+	PROP_BUSY
+};
+
+enum {
 	ACTIVITY_ADDED,
 	ACTIVITY_REMOVED,
 	LAST_SIGNAL
@@ -73,36 +78,95 @@ 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)
 {
 	EShellBackendPrivate *priv;
@@ -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;
 
@@ -195,6 +260,21 @@ e_shell_backend_class_init (EShellBackendClass *class)
 	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
 	 * @activity: an #EActivity
@@ -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 055a2a3..1810c8c 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,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]