aboutsummaryrefslogtreecommitdiffstats
path: root/shell/e-shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/e-shell.c')
-rw-r--r--shell/e-shell.c160
1 files changed, 95 insertions, 65 deletions
diff --git a/shell/e-shell.c b/shell/e-shell.c
index c9e8d46317..35908e0855 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -50,6 +50,7 @@ struct _EShellPrivate {
GHashTable *backends_by_scheme;
gpointer preparing_for_line_change; /* weak pointer */
+ gpointer preparing_for_shutdown; /* weak pointer */
guint auto_reconnect : 1;
guint network_available : 1;
@@ -69,6 +70,7 @@ enum {
HANDLE_URI,
PREPARE_FOR_OFFLINE,
PREPARE_FOR_ONLINE,
+ PREPARE_FOR_SHUTDOWN,
SEND_RECEIVE,
WINDOW_CREATED,
WINDOW_DESTROYED,
@@ -122,7 +124,9 @@ shell_window_delete_event_cb (EShell *shell,
return FALSE;
/* Otherwise we initiate application shutdown. */
- return !e_shell_quit (shell);
+ e_shell_quit (shell);
+
+ return TRUE;
}
static gboolean
@@ -272,6 +276,40 @@ shell_prepare_for_online (EShell *shell)
}
static void
+shell_ready_for_shutdown (EShell *shell,
+ EActivity *activity,
+ gboolean is_last_ref)
+{
+ GList *list;
+
+ if (!is_last_ref)
+ return;
+
+ /* Increment the reference count so we can safely emit
+ * a signal without triggering the toggle reference. */
+ g_object_ref (activity);
+
+ e_activity_complete (activity);
+
+ g_object_remove_toggle_ref (
+ G_OBJECT (activity), (GToggleNotify)
+ shell_ready_for_shutdown, shell);
+
+ /* Finalize the activity. */
+ g_object_ref (activity);
+
+ g_message ("Shutdown preparations complete.");
+
+ /* Destroy all watched windows. Note, we iterate over a -copy-
+ * of the watched windows list because the act of destroying a
+ * watched window will modify the watched windows list, which
+ * would derail the iteration. */
+ list = g_list_copy (e_shell_get_watched_windows (shell));
+ g_list_foreach (list, (GFunc) gtk_widget_destroy, NULL);
+ g_list_free (list);
+}
+
+static void
shell_load_modules (EShell *shell)
{
GList *modules;
@@ -358,54 +396,6 @@ shell_create_backends (EShell *shell)
g_free (children);
}
-static gboolean
-shell_shutdown_timeout (EShell *shell)
-{
- GList *list, *iter;
- gboolean proceed = TRUE;
- static guint source_id = 0;
- static guint message_timer = 1;
-
- /* Module list is read-only; do not free. */
- list = e_shell_get_shell_backends (shell);
-
- /* Any backend can defer shutdown if it's still busy. */
- for (iter = list; proceed && iter != NULL; iter = iter->next) {
- EShellBackend *shell_backend = iter->data;
- proceed = e_shell_backend_shutdown (shell_backend);
-
- /* Emit a message every few seconds to indicate
- * which backend(s) we're still waiting on. */
- if (proceed || message_timer == 0)
- continue;
-
- g_message (
- _("Waiting for the \"%s\" backend to finish..."),
- E_SHELL_BACKEND_GET_CLASS (shell_backend)->name);
- }
-
- message_timer = (message_timer + 1) % 10;
-
- /* If we're go for shutdown, destroy all shell windows. Note,
- * we iterate over a /copy/ of the active windows list because
- * the act of destroying a shell window will modify the active
- * windows list, which would otherwise derail the iteration. */
- if (proceed) {
- list = g_list_copy (shell->priv->watched_windows);
- g_list_foreach (list, (GFunc) gtk_widget_destroy, NULL);
- g_list_free (list);
-
- /* If a backend is still busy, try again after a short delay. */
- } else if (source_id == 0)
- source_id = g_timeout_add (
- SHUTDOWN_TIMEOUT, (GSourceFunc)
- shell_shutdown_timeout, shell);
-
- /* Return TRUE to repeat the timeout, FALSE to stop it. This
- * may seem backwards if the function was called directly. */
- return !proceed;
-}
-
static void
shell_set_property (GObject *object,
guint property_id,
@@ -761,6 +751,31 @@ shell_class_init (EShellClass *class)
E_TYPE_ACTIVITY);
/**
+ * EShell::prepare-for-shutdown
+ * @shell: the #EShell which emitted the signal
+ * @activity: the #EActivity for shutdown preparations
+ *
+ * Emitted when the user elects to quit the application. An
+ * #EShellBackend should listen for this signal and make
+ * preparations for shutting down.
+ *
+ * If preparations for shutting down cannot immediately be completed
+ * (such as when there are uncompleted network operations), the
+ * #EShellBackend should reference the @activity until preparations
+ * are complete, and then unreference the @activity. This will
+ * delay Evolution from actually shutting down until all backends
+ * have unreferenced @activity.
+ **/
+ signals[PREPARE_FOR_SHUTDOWN] = g_signal_new (
+ "prepare-for-shutdown",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_ACTIVITY);
+
+ /**
* EShell::send-receive
* @shell: the #EShell which emitted the signal
* @parent: a parent #GtkWindow
@@ -1411,23 +1426,38 @@ e_shell_event (EShell *shell,
g_signal_emit (shell, signals[EVENT], detail, event_data);
}
-gboolean
-e_shell_is_busy (EShell *shell)
+void
+e_shell_quit (EShell *shell)
{
- /* FIXME */
- return FALSE;
-}
+ GList *list, *iter;
-gboolean
-e_shell_do_quit (EShell *shell)
-{
- /* FIXME */
- return TRUE;
-}
+ g_return_if_fail (E_IS_SHELL (shell));
-gboolean
-e_shell_quit (EShell *shell)
-{
- /* FIXME */
- return TRUE;
+ /* Are preparations already in progress? */
+ if (shell->priv->preparing_for_shutdown != NULL)
+ return;
+
+ g_message ("Preparing for shutdown...");
+
+ shell->priv->preparing_for_shutdown =
+ e_activity_new (_("Preparing to shut down..."));
+
+ g_object_add_toggle_ref (
+ G_OBJECT (shell->priv->preparing_for_shutdown),
+ (GToggleNotify) shell_ready_for_shutdown, shell);
+
+ g_object_add_weak_pointer (
+ G_OBJECT (shell->priv->preparing_for_shutdown),
+ &shell->priv->preparing_for_shutdown);
+
+ g_signal_emit (
+ shell, signals[PREPARE_FOR_SHUTDOWN], 0,
+ shell->priv->preparing_for_shutdown);
+
+ g_object_unref (shell->priv->preparing_for_shutdown);
+
+ /* Desensitize all watched windows to prevent user action. */
+ list = e_shell_get_watched_windows (shell);
+ for (iter = list; iter != NULL; iter = iter->next)
+ gtk_widget_set_sensitive (GTK_WIDGET (iter->data), FALSE);
}