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().
This commit is contained in:
Matthew Barnes
2010-10-13 15:37:12 -04:00
parent 3f529345a9
commit 3fbcc8ef80
5 changed files with 170 additions and 8 deletions

View File

@ -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>

View File

@ -34,6 +34,11 @@ EShellBackend
@eshellbackend: the object which received the signal.
@arg1:
<!-- ##### ARG EShellBackend:busy ##### -->
<para>
</para>
<!-- ##### FUNCTION e_shell_backend_compare ##### -->
<para>

View File

@ -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

View File

@ -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);
}
}
/**

View File

@ -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,