[evolution/kill-bonobo: 28/54] Add a "quit-requested" signal to the shutdown protocol.



commit 0d6061a4ebf34cbe4f640e755372c38bd698ed25
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sun Jul 12 12:55:10 2009 -0400

    Add a "quit-requested" signal to the shutdown protocol.
    
    The contact and contact-list editors now demonstrate this part of the
    shutdown protocol.  They listen for the "quit-requested" signal from the
    shell and prompt to save changes, discard changes or cancel.  If the user
    cancels, the editor calls e_shell_cancel_quit() to do just that.

 addressbook/gui/contact-editor/e-contact-editor.c  |   40 +--
 addressbook/gui/contact-editor/e-contact-editor.h  |    3 +-
 .../gui/contact-editor/e-contact-quick-add.c       |    7 +-
 addressbook/gui/contact-editor/eab-editor.c        |  352 +++++++++++--------
 addressbook/gui/contact-editor/eab-editor.h        |  100 +++---
 .../contact-list-editor/e-contact-list-editor.c    |   64 +++--
 .../contact-list-editor/e-contact-list-editor.h    |    3 +-
 doc/reference/shell/tmpl/e-shell.sgml              |    9 +-
 doc/reference/shell/tmpl/eshell-unused.sgml        |    8 +
 modules/addressbook/e-book-shell-backend.c         |   70 +++--
 modules/addressbook/e-book-shell-view-actions.c    |   18 +-
 modules/addressbook/e-book-shell-view-private.c    |   15 +-
 shell/e-shell-window.c                             |    3 +-
 shell/e-shell.c                                    |  104 +++++--
 shell/e-shell.h                                    |    1 +
 15 files changed, 489 insertions(+), 308 deletions(-)
---
diff --git a/addressbook/gui/contact-editor/e-contact-editor.c b/addressbook/gui/contact-editor/e-contact-editor.c
index 69d11b0..d4768ae 100644
--- a/addressbook/gui/contact-editor/e-contact-editor.c
+++ b/addressbook/gui/contact-editor/e-contact-editor.c
@@ -2253,7 +2253,7 @@ fill_in_simple_field (EContactEditor *editor, GtkWidget *widget, gint field_id)
 			editor->image_set = TRUE;
 		}
 		else {
-			gchar *file_name = e_icon_factory_get_icon_filename ("stock_person", 48);
+			gchar *file_name = e_icon_factory_get_icon_filename ("stock_person", GTK_ICON_SIZE_DIALOG);
 			e_image_chooser_set_from_file (E_IMAGE_CHOOSER (widget), file_name);
 			editor->image_set = FALSE;
 			g_free (file_name);
@@ -3518,7 +3518,7 @@ static void
 supported_fields_cb (EBook *book, EBookStatus status,
 		     EList *fields, EContactEditor *ce)
 {
-	if (!g_slist_find ((GSList*)eab_editor_get_all_editors (), ce)) {
+	if (!g_slist_find (eab_editor_get_all_editors (), ce)) {
 		g_warning ("supported_fields_cb called for book that's still around, but contact editor that's been destroyed.");
 		return;
 	}
@@ -3537,7 +3537,7 @@ required_fields_cb (EBook *book, EBookStatus status,
 		    EList *fields, EContactEditor *ce)
 {
 
-	if (!g_slist_find ((GSList*)eab_editor_get_all_editors (), ce)) {
+	if (!g_slist_find (eab_editor_get_all_editors (), ce)) {
 		g_warning ("supported_fields_cb called for book that's still around, but contact editor that's been destroyed.");
 		return;
 	}
@@ -3549,31 +3549,22 @@ required_fields_cb (EBook *book, EBookStatus status,
 
 }
 
-
-static void
-contact_editor_destroy_notify (gpointer data,
-			       GObject *where_the_object_was)
+EABEditor *
+e_contact_editor_new (EShell *shell,
+                      EBook *book,
+                      EContact *contact,
+                      gboolean is_new_contact,
+                      gboolean editable)
 {
-	eab_editor_remove (EAB_EDITOR (data));
-}
-
-GtkWidget *
-e_contact_editor_new (EBook *book,
-		      EContact *contact,
-		      gboolean is_new_contact,
-		      gboolean editable)
-{
-	EContactEditor *ce;
+	EABEditor *editor;
 
+	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
 	g_return_val_if_fail (E_IS_BOOK (book), NULL);
 	g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
 
-	ce = g_object_new (E_TYPE_CONTACT_EDITOR, NULL);
+	editor = g_object_new (E_TYPE_CONTACT_EDITOR, "shell", shell, NULL);
 
-	eab_editor_add (EAB_EDITOR (ce));
-	g_object_weak_ref (G_OBJECT (ce), contact_editor_destroy_notify, ce);
-
-	g_object_set (ce,
+	g_object_set (editor,
 		      "source_book", book,
 		      "contact", contact,
 		      "is_new_contact", is_new_contact,
@@ -3581,9 +3572,10 @@ e_contact_editor_new (EBook *book,
 		      NULL);
 
 	if (book)
-		e_book_async_get_supported_fields (book, (EBookEListCallback)supported_fields_cb, ce);
+		e_book_async_get_supported_fields (
+			book, (EBookEListCallback)supported_fields_cb, editor);
 
-	return GTK_WIDGET (ce);
+	return editor;
 }
 
 static void
diff --git a/addressbook/gui/contact-editor/e-contact-editor.h b/addressbook/gui/contact-editor/e-contact-editor.h
index 1000cdc..6228dc3 100644
--- a/addressbook/gui/contact-editor/e-contact-editor.h
+++ b/addressbook/gui/contact-editor/e-contact-editor.h
@@ -108,7 +108,8 @@ struct _EContactEditorClass
 };
 
 GType		e_contact_editor_get_type	(void);
-GtkWidget	*e_contact_editor_new		(EBook *book,
+EABEditor	*e_contact_editor_new		(EShell *shell,
+						 EBook *book,
 						 EContact *contact,
 						 gboolean is_new_contact,
 						 gboolean editable);
diff --git a/addressbook/gui/contact-editor/e-contact-quick-add.c b/addressbook/gui/contact-editor/e-contact-quick-add.c
index cd81d28..df3eb6e 100644
--- a/addressbook/gui/contact-editor/e-contact-quick-add.c
+++ b/addressbook/gui/contact-editor/e-contact-quick-add.c
@@ -194,7 +194,12 @@ ce_have_book (EBook *book, EBookStatus status, gpointer closure)
 		g_warning ("Couldn't open local address book.");
 		quick_add_unref (qa);
 	} else {
-		GtkWidget *contact_editor = e_contact_editor_new (book, qa->contact, TRUE, TRUE /* XXX */);
+		EShell *shell;
+		EABEditor *contact_editor;
+
+		shell = e_shell_get_default ();
+		contact_editor = e_contact_editor_new (
+			shell, book, qa->contact, TRUE, TRUE /* XXX */);
 
 		/* mark it as changed so the Save buttons are enabled when we bring up the dialog. */
 		g_object_set (contact_editor,
diff --git a/addressbook/gui/contact-editor/eab-editor.c b/addressbook/gui/contact-editor/eab-editor.c
index b5a4683..554db9a 100644
--- a/addressbook/gui/contact-editor/eab-editor.c
+++ b/addressbook/gui/contact-editor/eab-editor.c
@@ -31,13 +31,19 @@
 #include "e-util/e-util.h"
 #include "addressbook/gui/widgets/eab-gui-util.h"
 
-static void eab_editor_default_show  (EABEditor *editor);
-static void eab_editor_default_raise (EABEditor *editor);
-static void eab_editor_default_close (EABEditor *editor);
-static void eab_editor_class_init    (EABEditorClass *klass);
-static void eab_editor_init          (EABEditor *editor);
+#define EAB_EDITOR_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), EAB_TYPE_EDITOR, EABEditorPrivate))
+
+struct _EABEditorPrivate {
+	EShell *shell;
+};
+
+enum {
+	PROP_0,
+	PROP_SHELL
+};
 
-/* Signal IDs */
 enum {
 	CONTACT_ADDED,
 	CONTACT_MODIFIED,
@@ -46,97 +52,125 @@ enum {
 	LAST_SIGNAL
 };
 
-static GSList *all_editors = NULL;
-
-static GtkObjectClass *parent_class = NULL;
-
-static guint editor_signals[LAST_SIGNAL];
+static GSList *all_editors;
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
 
-GType
-eab_editor_get_type (void)
+static void
+eab_editor_quit_requested_cb (EABEditor *editor,
+                              EShell *shell)
 {
-	static GType editor_type = 0;
-
-	if (!editor_type) {
-		static const GTypeInfo editor_info =  {
-			sizeof (EABEditorClass),
-			NULL,           /* base_init */
-			NULL,           /* base_finalize */
-			(GClassInitFunc) eab_editor_class_init,
-			NULL,           /* class_finalize */
-			NULL,           /* class_data */
-			sizeof (EABEditor),
-			0,             /* n_preallocs */
-			(GInstanceInitFunc) eab_editor_init,
-		};
+	GtkWindow *window;
 
-		editor_type = g_type_register_static (G_TYPE_OBJECT, "EABEditor", &editor_info, 0);
-	}
+	window = eab_editor_get_window (editor);
 
-	return editor_type;
+	eab_editor_raise (editor);
+	if (!eab_editor_prompt_to_save_changes (editor, window))
+		e_shell_cancel_quit (shell);
 }
 
 static void
-eab_editor_default_show (EABEditor *editor)
+eab_editor_set_shell (EABEditor *editor,
+                      EShell *shell)
 {
-	g_warning ("abstract eab_editor_show called");
+	g_return_if_fail (editor->priv->shell == NULL);
+	g_return_if_fail (E_IS_SHELL (shell));
+
+	editor->priv->shell = g_object_ref (shell);
+
+	g_signal_connect_swapped (
+		shell, "quit-requested",
+		G_CALLBACK (eab_editor_quit_requested_cb), editor);
 }
 
 static void
-eab_editor_default_close (EABEditor *editor)
+eab_editor_set_property (GObject *object,
+                         guint property_id,
+                         const GValue *value,
+                         GParamSpec *pspec)
 {
-	g_warning ("abstract eab_editor_close called");
+	switch (property_id) {
+		case PROP_SHELL:
+			eab_editor_set_shell (
+				EAB_EDITOR (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
-eab_editor_default_raise (EABEditor *editor)
+eab_editor_get_property (GObject *object,
+                         guint property_id,
+                         GValue *value,
+                         GParamSpec *pspec)
 {
-	g_warning ("abstract eab_editor_raise called");
+	switch (property_id) {
+		case PROP_SHELL:
+			g_value_set_object (
+				value, eab_editor_get_shell (
+				EAB_EDITOR (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
-eab_editor_default_save_contact (EABEditor *editor, gboolean should_close)
+eab_editor_dispose (GObject *object)
 {
-	g_warning ("abstract eab_editor_save_contact called");
-}
+	EABEditorPrivate *priv;
 
-static gboolean
-eab_editor_default_is_valid (EABEditor *editor)
-{
-	g_warning ("abstract eab_editor_is_valid called");
-	return FALSE;
-}
+	priv = EAB_EDITOR_GET_PRIVATE (object);
 
-static gboolean
-eab_editor_default_is_changed (EABEditor *editor)
-{
-	g_warning ("abstract eab_editor_is_changed called");
-	return FALSE;
-}
+	if (priv->shell != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->shell, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->shell);
+		priv->shell = NULL;
+	}
 
-static GtkWindow*
-eab_editor_default_get_window (EABEditor *editor)
-{
-	g_warning ("abstract eab_editor_get_window called");
-	return NULL;
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
-eab_editor_class_init (EABEditorClass *klass)
+eab_editor_finalize (GObject *object)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	all_editors = g_slist_remove (all_editors, object);
 
-	parent_class = g_type_class_ref (G_TYPE_OBJECT);
-
-	klass->show = eab_editor_default_show;
-	klass->close = eab_editor_default_close;
-	klass->raise = eab_editor_default_raise;
-	klass->save_contact = eab_editor_default_save_contact;
-	klass->is_valid = eab_editor_default_is_valid;
-	klass->is_changed = eab_editor_default_is_changed;
-	klass->get_window = eab_editor_default_get_window;
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
 
-	editor_signals[CONTACT_ADDED] =
+static void
+eab_editor_class_init (EABEditorClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EABEditorPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = eab_editor_set_property;
+	object_class->get_property = eab_editor_get_property;
+	object_class->dispose = eab_editor_dispose;
+	object_class->finalize = eab_editor_finalize;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SHELL,
+		g_param_spec_object (
+			"shell",
+			_("Shell"),
+			_("The EShell singleton"),
+			E_TYPE_SHELL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY));
+
+	signals[CONTACT_ADDED] =
 		g_signal_new ("contact_added",
 			      G_OBJECT_CLASS_TYPE (object_class),
 			      G_SIGNAL_RUN_FIRST,
@@ -146,7 +180,7 @@ eab_editor_class_init (EABEditorClass *klass)
 			      G_TYPE_NONE, 2,
 			      G_TYPE_INT, G_TYPE_OBJECT);
 
-	editor_signals[CONTACT_MODIFIED] =
+	signals[CONTACT_MODIFIED] =
 		g_signal_new ("contact_modified",
 			      G_OBJECT_CLASS_TYPE (object_class),
 			      G_SIGNAL_RUN_FIRST,
@@ -156,7 +190,7 @@ eab_editor_class_init (EABEditorClass *klass)
 			      G_TYPE_NONE, 2,
 			      G_TYPE_INT, G_TYPE_OBJECT);
 
-	editor_signals[CONTACT_DELETED] =
+	signals[CONTACT_DELETED] =
 		g_signal_new ("contact_deleted",
 			      G_OBJECT_CLASS_TYPE (object_class),
 			      G_SIGNAL_RUN_FIRST,
@@ -166,7 +200,7 @@ eab_editor_class_init (EABEditorClass *klass)
 			      G_TYPE_NONE, 2,
 			      G_TYPE_INT, G_TYPE_OBJECT);
 
-	editor_signals[EDITOR_CLOSED] =
+	signals[EDITOR_CLOSED] =
 		g_signal_new ("editor_closed",
 			      G_OBJECT_CLASS_TYPE (object_class),
 			      G_SIGNAL_RUN_FIRST,
@@ -179,78 +213,143 @@ eab_editor_class_init (EABEditorClass *klass)
 static void
 eab_editor_init (EABEditor *editor)
 {
+	editor->priv = EAB_EDITOR_GET_PRIVATE (editor);
+
+	all_editors = g_slist_prepend (all_editors, editor);
+}
+
+GType
+eab_editor_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info =  {
+			sizeof (EABEditorClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eab_editor_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EABEditor),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) eab_editor_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			G_TYPE_OBJECT, "EABEditor", &type_info,
+			G_TYPE_FLAG_ABSTRACT);
+	}
+
+	return type;
+}
+
+EShell *
+eab_editor_get_shell (EABEditor *editor)
+{
+	g_return_val_if_fail (EAB_IS_EDITOR (editor), NULL);
+
+	return E_SHELL (editor->priv->shell);
 }
 
-
+GSList *
+eab_editor_get_all_editors (void)
+{
+	return all_editors;
+}
 
 void
 eab_editor_show (EABEditor *editor)
 {
+	EABEditorClass *class;
+
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 
-	if (EAB_EDITOR_GET_CLASS(editor)->show)
-		EAB_EDITOR_GET_CLASS(editor)->show (editor);
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_if_fail (class->show != NULL);
+
+	class->show (editor);
 }
 
 void
 eab_editor_close (EABEditor *editor)
 {
+	EABEditorClass *class;
+
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 
-	if (EAB_EDITOR_GET_CLASS(editor)->close)
-		EAB_EDITOR_GET_CLASS(editor)->close (editor);
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_if_fail (class->close != NULL);
+
+	class->close (editor);
 }
 
 void
 eab_editor_raise (EABEditor *editor)
 {
+	EABEditorClass *class;
+
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 
-	if (EAB_EDITOR_GET_CLASS(editor)->raise)
-		EAB_EDITOR_GET_CLASS(editor)->raise (editor);
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_if_fail (class->raise != NULL);
+
+	class->raise (editor);
 }
 
 void
 eab_editor_save_contact (EABEditor *editor, gboolean should_close)
 {
+	EABEditorClass *class;
+
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 
-	if (EAB_EDITOR_GET_CLASS(editor)->save_contact)
-		EAB_EDITOR_GET_CLASS(editor)->save_contact (editor, should_close);
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_if_fail (class->save_contact != NULL);
+
+	class->save_contact (editor, should_close);
 }
 
 gboolean
 eab_editor_is_changed (EABEditor *editor)
 {
+	EABEditorClass *class;
+
 	g_return_val_if_fail (EAB_IS_EDITOR (editor), FALSE);
 
-	if (EAB_EDITOR_GET_CLASS(editor)->is_changed)
-		return EAB_EDITOR_GET_CLASS(editor)->is_changed (editor);
-	else
-		return FALSE;
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_val_if_fail (class->is_changed != NULL, FALSE);
+
+	return class->is_changed (editor);
 }
 
 gboolean
 eab_editor_is_valid (EABEditor *editor)
 {
+	EABEditorClass *class;
+
 	g_return_val_if_fail (EAB_IS_EDITOR (editor), FALSE);
 
-	if (EAB_EDITOR_GET_CLASS(editor)->is_valid)
-		return EAB_EDITOR_GET_CLASS(editor)->is_valid (editor);
-	else
-		return FALSE;
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_val_if_fail (class->is_valid != NULL, FALSE);
+
+	return class->is_valid (editor);
 }
 
 GtkWindow*
 eab_editor_get_window (EABEditor *editor)
 {
+	EABEditorClass *class;
+
 	g_return_val_if_fail (EAB_IS_EDITOR (editor), NULL);
 
-	if (EAB_EDITOR_GET_CLASS(editor)->get_window)
-		return EAB_EDITOR_GET_CLASS(editor)->get_window (editor);
-	else
-		return NULL;
+	class = EAB_EDITOR_GET_CLASS (editor);
+	g_return_val_if_fail (class->get_window != NULL, NULL);
+
+	return class->get_window (editor);
 }
+
 /* This function prompts for saving if editor conents are in changed state and
    save or discards or cancels(just returns with out doing anything) according to user input.
    Editor gets destoryed in case of save and discard case.
@@ -280,64 +379,37 @@ eab_editor_prompt_to_save_changes (EABEditor *editor, GtkWindow *window)
 	}
 }
 
-gboolean
-eab_editor_request_close_all (void)
-{
-	GSList *p;
-	GSList *pnext;
-	gboolean retval;
-
-	retval = TRUE;
-	for (p = all_editors; p != NULL; p = pnext) {
-		EABEditor *editor = EAB_EDITOR (p->data);
-		GtkWindow *window = eab_editor_get_window (editor);
-
-		pnext = p->next;
-
-		eab_editor_raise (editor);
-		if (! eab_editor_prompt_to_save_changes (editor, window)) {
-			retval = FALSE;
-			break;
-		}
-	}
-
-	return retval;
-}
-
-const GSList*
-eab_editor_get_all_editors (void)
-{
-	return all_editors;
-}
-
 void
-eab_editor_contact_added (EABEditor *editor, EBookStatus status, EContact *contact)
+eab_editor_contact_added (EABEditor *editor,
+                          EBookStatus status,
+                          EContact *contact)
 {
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 	g_return_if_fail (E_IS_CONTACT (contact));
 
-	g_signal_emit (editor, editor_signals[CONTACT_ADDED], 0,
-		       status, contact);
+	g_signal_emit (editor, signals[CONTACT_ADDED], 0, status, contact);
 }
 
 void
-eab_editor_contact_modified (EABEditor *editor, EBookStatus status, EContact *contact)
+eab_editor_contact_modified (EABEditor *editor,
+                             EBookStatus status,
+                             EContact *contact)
 {
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 	g_return_if_fail (E_IS_CONTACT (contact));
 
-	g_signal_emit (editor, editor_signals[CONTACT_MODIFIED], 0,
-		       status, contact);
+	g_signal_emit (editor, signals[CONTACT_MODIFIED], 0, status, contact);
 }
 
 void
-eab_editor_contact_deleted (EABEditor *editor, EBookStatus status, EContact *contact)
+eab_editor_contact_deleted (EABEditor *editor,
+                            EBookStatus status,
+                            EContact *contact)
 {
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 	g_return_if_fail (E_IS_CONTACT (contact));
 
-	g_signal_emit (editor, editor_signals[CONTACT_DELETED], 0,
-		       status, contact);
+	g_signal_emit (editor, signals[CONTACT_DELETED], 0, status, contact);
 }
 
 void
@@ -345,21 +417,5 @@ eab_editor_closed (EABEditor *editor)
 {
 	g_return_if_fail (EAB_IS_EDITOR (editor));
 
-	g_signal_emit (editor, editor_signals[EDITOR_CLOSED], 0);
-}
-
-void
-eab_editor_add (EABEditor *editor)
-{
-	g_return_if_fail (EAB_IS_EDITOR (editor));
-
-	all_editors = g_slist_prepend (all_editors, editor);
-}
-
-void
-eab_editor_remove (EABEditor *editor)
-{
-	g_return_if_fail (EAB_IS_EDITOR (editor));
-
-	all_editors = g_slist_remove (all_editors, editor);
+	g_signal_emit (editor, signals[EDITOR_CLOSED], 0);
 }
diff --git a/addressbook/gui/contact-editor/eab-editor.h b/addressbook/gui/contact-editor/eab-editor.h
index 1b3de4b..f3803e7 100644
--- a/addressbook/gui/contact-editor/eab-editor.h
+++ b/addressbook/gui/contact-editor/eab-editor.h
@@ -24,42 +24,42 @@
 #ifndef __EAB_EDITOR_H__
 #define __EAB_EDITOR_H__
 
-#include <glade/glade.h>
-
+#include <gtk/gtk.h>
 #include <libebook/e-book.h>
 #include <libebook/e-contact.h>
-
-#include <gtk/gtk.h>
+#include <shell/e-shell.h>
+
+/* Standard GObject macros */
+#define EAB_TYPE_EDITOR \
+	(eab_editor_get_type ())
+#define EAB_EDITOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), EAB_TYPE_EDITOR, EABEditor))
+#define EAB_EDITOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), EAB_TYPE_EDITOR, EABEditorClass))
+#define EAB_IS_EDITOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), EAB_TYPE_EDITOR))
+#define EAB_IS_EDITOR_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), EAB_TYPE_EDITOR))
+#define EAB_EDITOR_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), EAB_EDITOR_TYPE, EABEditorClass))
 
 G_BEGIN_DECLS
 
-/* EABEditor - A dialog displaying information about a contact.
- *
- * The following arguments are available:
- *
- * name		type		read/write	description
- * --------------------------------------------------------------------------------
- * card         ECard *         RW              The card currently being edited
- */
-
-#define EAB_TYPE_EDITOR			(eab_editor_get_type ())
-#define EAB_EDITOR(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), EAB_TYPE_EDITOR, EABEditor))
-#define EAB_EDITOR_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), EAB_TYPE_EDITOR, EABEditorClass))
-#define EAB_IS_EDITOR(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EAB_TYPE_EDITOR))
-#define EAB_IS_EDITOR_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((obj), EAB_TYPE_EDITOR))
-#define EAB_EDITOR_GET_CLASS(o)         (G_TYPE_INSTANCE_GET_CLASS ((o), EAB_EDITOR_TYPE, EABEditorClass))
-
+typedef struct _EABEditor EABEditor;
+typedef struct _EABEditorClass EABEditorClass;
+typedef struct _EABEditorPrivate EABEditorPrivate;
 
-typedef struct _EABEditor       EABEditor;
-typedef struct _EABEditorClass  EABEditorClass;
-
-struct _EABEditor
-{
+struct _EABEditor {
 	GObject parent;
+	EABEditorPrivate *priv;
 };
 
-struct _EABEditorClass
-{
+struct _EABEditorClass {
 	GObjectClass parent_class;
 
 	/* virtual functions */
@@ -78,31 +78,35 @@ struct _EABEditorClass
 	void (* editor_closed)    (EABEditor *editor);
 };
 
-GType           eab_editor_get_type          (void);
+GType		eab_editor_get_type		(void);
+EShell *	eab_editor_get_shell		(EABEditor *editor);
+GSList *	eab_editor_get_all_editors	(void);
 
 /* virtual functions */
-void            eab_editor_show              (EABEditor *editor);
-void            eab_editor_close             (EABEditor *editor);
-void            eab_editor_raise             (EABEditor *editor);
-void            eab_editor_save_contact      (EABEditor *editor, gboolean should_close);
-gboolean        eab_editor_is_valid          (EABEditor *editor);
-gboolean        eab_editor_is_changed        (EABEditor *editor);
-GtkWindow*      eab_editor_get_window        (EABEditor *editor);
-
-gboolean        eab_editor_prompt_to_save_changes (EABEditor *editor, GtkWindow *window);
+void		eab_editor_show			(EABEditor *editor);
+void		eab_editor_close		(EABEditor *editor);
+void		eab_editor_raise		(EABEditor *editor);
+void		eab_editor_save_contact		(EABEditor *editor,
+						 gboolean should_close);
+gboolean	eab_editor_is_valid		(EABEditor *editor);
+gboolean	eab_editor_is_changed		(EABEditor *editor);
+GtkWindow *	eab_editor_get_window		(EABEditor *editor);
+
+gboolean	eab_editor_prompt_to_save_changes
+						(EABEditor *editor,
+						 GtkWindow *window);
 
 /* these four generate EABEditor signals */
-void		eab_editor_contact_added     (EABEditor *editor, EBookStatus status, EContact *contact);
-void		eab_editor_contact_modified  (EABEditor *editor, EBookStatus status, EContact *contact);
-void		eab_editor_contact_deleted   (EABEditor *editor, EBookStatus status, EContact *contact);
-void		eab_editor_closed            (EABEditor *editor);
-
-/* these maintain the global list of editors so we can prompt the user
-   if there are unsaved changes when they exit. */
-void            eab_editor_add               (EABEditor *editor);
-void            eab_editor_remove            (EABEditor *editor);
-gboolean        eab_editor_request_close_all (void);
-const GSList*   eab_editor_get_all_editors   (void);
+void		eab_editor_contact_added	(EABEditor *editor,
+						 EBookStatus status,
+						 EContact *contact);
+void		eab_editor_contact_modified	(EABEditor *editor,
+						 EBookStatus status,
+						 EContact *contact);
+void		eab_editor_contact_deleted	(EABEditor *editor,
+						 EBookStatus status,
+						 EContact *contact);
+void		eab_editor_closed		(EABEditor *editor);
 
 G_END_DECLS
 
diff --git a/addressbook/gui/contact-list-editor/e-contact-list-editor.c b/addressbook/gui/contact-list-editor/e-contact-list-editor.c
index cc64cd5..77d9135 100644
--- a/addressbook/gui/contact-list-editor/e-contact-list-editor.c
+++ b/addressbook/gui/contact-list-editor/e-contact-list-editor.c
@@ -881,6 +881,36 @@ contact_list_editor_create_name_selector (gchar *name,
                                           gint int1,
                                           gint int2);
 
+static gpointer
+contact_editor_fudge_new (EBook *book,
+                          EContact *contact,
+                          gboolean is_new,
+                          gboolean editable)
+{
+	EShell *shell = e_shell_get_default ();
+
+	/* XXX Putting this function signature in libedataserverui
+	 *     was a terrible idea.  Now we're stuck with it. */
+
+	return e_contact_editor_new (
+		shell, book, contact, is_new, editable);
+}
+
+static gpointer
+contact_list_editor_fudge_new (EBook *book,
+                               EContact *contact,
+                               gboolean is_new,
+                               gboolean editable)
+{
+	EShell *shell = e_shell_get_default ();
+
+	/* XXX Putting this function signature in libedataserverui
+	 *     was a terrible idea.  Now we're stuck with it. */
+
+	return e_contact_list_editor_new (
+		shell, book, contact, is_new, editable);
+}
+
 GtkWidget *
 contact_list_editor_create_name_selector (gchar *name,
                                           gchar *string1,
@@ -901,9 +931,9 @@ contact_list_editor_create_name_selector (gchar *name,
 		name_selector, "Members");
 
 	e_name_selector_entry_set_contact_editor_func (
-		name_selector_entry, e_contact_editor_new);
+		name_selector_entry, contact_editor_fudge_new);
 	e_name_selector_entry_set_contact_list_editor_func (
-		name_selector_entry, e_contact_list_editor_new);
+		name_selector_entry, contact_list_editor_fudge_new);
 	gtk_widget_show (GTK_WIDGET (name_selector_entry));
 
 	g_signal_connect (
@@ -1319,26 +1349,20 @@ e_contact_list_editor_get_type (void)
 	return type;
 }
 
-static void
-contact_list_editor_destroy_notify (gpointer data,
-				    GObject *where_the_object_was)
-{
-	eab_editor_remove (EAB_EDITOR (data));
-}
-
-GtkWidget *
-e_contact_list_editor_new (EBook *book,
-			   EContact *list_contact,
-			   gboolean is_new_list,
-			   gboolean editable)
+EABEditor *
+e_contact_list_editor_new (EShell *shell,
+                           EBook *book,
+                           EContact *list_contact,
+                           gboolean is_new_list,
+                           gboolean editable)
 {
-	EContactListEditor *editor;
+	EABEditor *editor;
 
-	editor = g_object_new (E_TYPE_CONTACT_LIST_EDITOR, NULL);
+	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
 
-	eab_editor_add (EAB_EDITOR (editor));
-	g_object_weak_ref (
-		G_OBJECT (editor), contact_list_editor_destroy_notify, editor);
+	editor = g_object_new (
+		E_TYPE_CONTACT_LIST_EDITOR,
+		"shell", shell, NULL);
 
 	g_object_set (editor,
 		      "book", book,
@@ -1347,7 +1371,7 @@ e_contact_list_editor_new (EBook *book,
 		      "editable", editable,
 		      NULL);
 
-	return GTK_WIDGET (editor);
+	return editor;
 }
 
 EBook *
diff --git a/addressbook/gui/contact-list-editor/e-contact-list-editor.h b/addressbook/gui/contact-list-editor/e-contact-list-editor.h
index 5b67fe0..8e25859 100644
--- a/addressbook/gui/contact-list-editor/e-contact-list-editor.h
+++ b/addressbook/gui/contact-list-editor/e-contact-list-editor.h
@@ -68,7 +68,8 @@ struct _EContactListEditorClass
 };
 
 GType		e_contact_list_editor_get_type	(void);
-GtkWidget *	e_contact_list_editor_new	(EBook *book,
+EABEditor *	e_contact_list_editor_new	(EShell *shell,
+						 EBook *book,
 						 EContact *list_contact,
 						 gboolean is_new_list,
 						 gboolean editable);
diff --git a/doc/reference/shell/tmpl/e-shell.sgml b/doc/reference/shell/tmpl/e-shell.sgml
index 42d9348..1f8f3b0 100644
--- a/doc/reference/shell/tmpl/e-shell.sgml
+++ b/doc/reference/shell/tmpl/e-shell.sgml
@@ -56,7 +56,7 @@ EShell
 @eshell: the object which received the signal.
 @arg1: 
 
-<!-- ##### SIGNAL EShell::prepare-for-shutdown ##### -->
+<!-- ##### SIGNAL EShell::prepare-for-quit ##### -->
 <para>
 
 </para>
@@ -64,6 +64,13 @@ EShell
 @eshell: the object which received the signal.
 @arg1: 
 
+<!-- ##### SIGNAL EShell::quit-requested ##### -->
+<para>
+
+</para>
+
+ eshell: the object which received the signal.
+
 <!-- ##### SIGNAL EShell::send-receive ##### -->
 <para>
 
diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml
index 7d19de5..58ddf10 100644
--- a/doc/reference/shell/tmpl/eshell-unused.sgml
+++ b/doc/reference/shell/tmpl/eshell-unused.sgml
@@ -457,6 +457,14 @@ intelligent
 @minor: 
 @revision: 
 
+<!-- ##### SIGNAL EShell::prepare-for-shutdown ##### -->
+<para>
+
+</para>
+
+ eshell: the object which received the signal.
+ arg1: 
+
 <!-- ##### ARG EShell:online-mode ##### -->
 <para>
 
diff --git a/modules/addressbook/e-book-shell-backend.c b/modules/addressbook/e-book-shell-backend.c
index 53737b5..9fe000a 100644
--- a/modules/addressbook/e-book-shell-backend.c
+++ b/modules/addressbook/e-book-shell-backend.c
@@ -226,30 +226,50 @@ book_shell_backend_init_importers (void)
 }
 
 static void
-book_shell_backend_book_loaded_cb (EBook *book,
+book_shell_backend_new_contact_cb (EBook *book,
                                    EBookStatus status,
                                    gpointer user_data)
 {
+	EShell *shell;
 	EContact *contact;
-	GtkAction *action;
-	GtkWidget *editor;
-	const gchar *action_name;
+	EABEditor *editor;
 
 	/* XXX Handle errors better. */
 	if (status != E_BOOK_ERROR_OK)
 		return;
 
 	contact = e_contact_new ();
-	action = GTK_ACTION (user_data);
-	action_name = gtk_action_get_name (action);
+	shell = E_SHELL (user_data);
 
-	if (strcmp (action_name, "contact-new") == 0)
-		editor = e_contact_editor_new (book, contact, TRUE, TRUE);
+	editor = e_contact_editor_new (
+		shell, book, contact, TRUE, TRUE);
+
+	eab_editor_show (editor);
+
+	g_object_unref (contact);
+	g_object_unref (book);
+}
+
+static void
+book_shell_backend_new_contact_list_cb (EBook *book,
+                                        EBookStatus status,
+                                        gpointer user_data)
+{
+	EShell *shell;
+	EContact *contact;
+	EABEditor *editor;
+
+	/* XXX Handle errors better. */
+	if (status != E_BOOK_ERROR_OK)
+		return;
+
+	contact = e_contact_new ();
+	shell = E_SHELL (user_data);
 
-	if (strcmp (action_name, "contact-new-list") == 0)
-		editor = e_contact_list_editor_new (book, contact, TRUE, TRUE);
+	editor = e_contact_list_editor_new (
+		shell, book, contact, TRUE, TRUE);
 
-	eab_editor_show (EAB_EDITOR (editor));
+	eab_editor_show (editor);
 
 	g_object_unref (contact);
 	g_object_unref (book);
@@ -263,6 +283,7 @@ action_contact_new_cb (GtkAction *action,
 	EBook *book = NULL;
 	GConfClient *client;
 	ESourceList *source_list;
+	const gchar *action_name;
 	const gchar *key;
 	gchar *uid;
 
@@ -275,6 +296,7 @@ action_contact_new_cb (GtkAction *action,
 
 	shell = e_shell_window_get_shell (shell_window);
 	client = e_shell_get_gconf_client (shell);
+	action_name = gtk_action_get_name (action);
 
 	key = "/apps/evolution/addressbook/display/primary_addressbook";
 	uid = gconf_client_get_string (client, key, NULL);
@@ -291,8 +313,15 @@ action_contact_new_cb (GtkAction *action,
 	if (book == NULL)
 		book = e_book_new_default_addressbook (NULL);
 
-	e_book_async_open (
-		book, FALSE, book_shell_backend_book_loaded_cb, action);
+	if (strcmp (action_name, "contact-new") == 0)
+		e_book_async_open (
+			book, FALSE,
+			book_shell_backend_new_contact_cb, shell);
+
+	if (strcmp (action_name, "contact-list-new") == 0)
+		e_book_async_open (
+			book, FALSE,
+			book_shell_backend_new_contact_list_cb, shell);
 }
 
 static void
@@ -396,16 +425,6 @@ book_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
 }
 
 static void
-book_shell_backend_prepare_for_shutdown_cb (EShellBackend *shell_backend,
-                                            EActivity *activity)
-{
-	/* FIXME Should specify whether Cancel is allowed.  Currently,
-	 *       clicking Cancel when prompted to save during shutdown
-	 *       just discards changes. */
-	eab_editor_request_close_all ();
-}
-
-static void
 book_shell_backend_window_created_cb (EShellBackend *shell_backend,
                                       GtkWindow *window)
 {
@@ -486,11 +505,6 @@ book_shell_backend_constructed (GObject *object)
 		shell_backend);
 
 	g_signal_connect_swapped (
-		shell, "prepare-for-shutdown",
-		G_CALLBACK (book_shell_backend_prepare_for_shutdown_cb),
-		shell_backend);
-
-	g_signal_connect_swapped (
 		shell, "window-created",
 		G_CALLBACK (book_shell_backend_window_created_cb),
 		shell_backend);
diff --git a/modules/addressbook/e-book-shell-view-actions.c b/modules/addressbook/e-book-shell-view-actions.c
index 773eb3f..9886e59 100644
--- a/modules/addressbook/e-book-shell-view-actions.c
+++ b/modules/addressbook/e-book-shell-view-actions.c
@@ -332,6 +332,9 @@ static void
 action_contact_new_cb (GtkAction *action,
                        EBookShellView *book_shell_view)
 {
+	EShell *shell;
+	EShellView *shell_view;
+	EShellWindow *shell_window;
 	EBookShellContent *book_shell_content;
 	EAddressbookView *view;
 	EAddressbookModel *model;
@@ -339,6 +342,10 @@ action_contact_new_cb (GtkAction *action,
 	GtkWidget *editor;
 	EBook *book;
 
+	shell_view = E_SHELL_VIEW (book_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
+
 	book_shell_content = book_shell_view->priv->book_shell_content;
 	view = e_book_shell_content_get_current_view (book_shell_content);
 	g_return_if_fail (view != NULL);
@@ -348,7 +355,7 @@ action_contact_new_cb (GtkAction *action,
 	g_return_if_fail (book != NULL);
 
 	contact = e_contact_new ();
-	editor = e_contact_editor_new (book, contact, TRUE, TRUE);
+	editor = e_contact_editor_new (shell, book, contact, TRUE, TRUE);
 	eab_editor_show (EAB_EDITOR (editor));
 	g_object_unref (contact);
 }
@@ -357,6 +364,9 @@ static void
 action_contact_new_list_cb (GtkAction *action,
                             EBookShellView *book_shell_view)
 {
+	EShell *shell;
+	EShellView *shell_view;
+	EShellWindow *shell_window;
 	EBookShellContent *book_shell_content;
 	EAddressbookView *view;
 	EAddressbookModel *model;
@@ -364,6 +374,10 @@ action_contact_new_list_cb (GtkAction *action,
 	GtkWidget *editor;
 	EBook *book;
 
+	shell_view = E_SHELL_VIEW (book_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
+
 	book_shell_content = book_shell_view->priv->book_shell_content;
 	view = e_book_shell_content_get_current_view (book_shell_content);
 	g_return_if_fail (view != NULL);
@@ -373,7 +387,7 @@ action_contact_new_list_cb (GtkAction *action,
 	g_return_if_fail (book != NULL);
 
 	contact = e_contact_new ();
-	editor = e_contact_list_editor_new (book, contact, TRUE, TRUE);
+	editor = e_contact_list_editor_new (shell, book, contact, TRUE, TRUE);
 	eab_editor_show (EAB_EDITOR (editor));
 	g_object_unref (contact);
 }
diff --git a/modules/addressbook/e-book-shell-view-private.c b/modules/addressbook/e-book-shell-view-private.c
index ec3562e..95e4980 100644
--- a/modules/addressbook/e-book-shell-view-private.c
+++ b/modules/addressbook/e-book-shell-view-private.c
@@ -32,23 +32,30 @@ open_contact (EBookShellView *book_shell_view,
               gboolean is_new_contact,
               EAddressbookView *view)
 {
+	EShell *shell;
+	EShellView *shell_view;
+	EShellWindow *shell_window;
 	EAddressbookModel *model;
-	GtkWidget *editor;
+	EABEditor *editor;
 	EBook *book;
 	gboolean editable;
 
+	shell_view = E_SHELL_VIEW (book_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
+
 	model = e_addressbook_view_get_model (view);
 	book = e_addressbook_model_get_book (model);
 	editable = e_addressbook_model_get_editable (model);
 
 	if (e_contact_get (contact, E_CONTACT_IS_LIST))
 		editor = e_contact_list_editor_new (
-			book, contact, is_new_contact, editable);
+			shell, book, contact, is_new_contact, editable);
 	else
 		editor = e_contact_editor_new (
-			book, contact, is_new_contact, editable);
+			shell, book, contact, is_new_contact, editable);
 
-	eab_editor_show (EAB_EDITOR (editor));
+	eab_editor_show (editor);
 }
 
 static void
diff --git a/shell/e-shell-window.c b/shell/e-shell-window.c
index f22d30e..c838235 100644
--- a/shell/e-shell-window.c
+++ b/shell/e-shell-window.c
@@ -143,8 +143,7 @@ shell_window_set_shell (EShellWindow *shell_window,
 	shell_window->priv->shell = shell;
 
 	g_object_add_weak_pointer (
-		G_OBJECT (shell_window),
-		&shell_window->priv->shell);
+		G_OBJECT (shell), &shell_window->priv->shell);
 
 	/* Need to disconnect these when the window is closing. */
 
diff --git a/shell/e-shell.c b/shell/e-shell.c
index 15aed43..83fc6dc 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -32,8 +32,6 @@
 #include "e-shell-migrate.h"
 #include "e-shell-window.h"
 
-#define SHUTDOWN_TIMEOUT	500  /* milliseconds */
-
 #define E_SHELL_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_SHELL, EShellPrivate))
@@ -50,11 +48,12 @@ struct _EShellPrivate {
 	GHashTable *backends_by_scheme;
 
 	gpointer preparing_for_line_change;  /* weak pointer */
-	gpointer preparing_for_shutdown;     /* weak pointer */
+	gpointer preparing_for_quit;         /* weak pointer */
 
 	guint auto_reconnect	: 1;
 	guint network_available	: 1;
 	guint online		: 1;
+	guint quit_cancelled	: 1;
 	guint safe_mode		: 1;
 };
 
@@ -70,7 +69,8 @@ enum {
 	HANDLE_URI,
 	PREPARE_FOR_OFFLINE,
 	PREPARE_FOR_ONLINE,
-	PREPARE_FOR_SHUTDOWN,
+	PREPARE_FOR_QUIT,
+	QUIT_REQUESTED,
 	SEND_RECEIVE,
 	WINDOW_CREATED,
 	WINDOW_DESTROYED,
@@ -123,7 +123,7 @@ shell_window_delete_event_cb (EShell *shell,
 	if (g_list_length (shell->priv->watched_windows) > 1)
 		return FALSE;
 
-	/* Otherwise we initiate application shutdown. */
+	/* Otherwise we initiate application quit. */
 	e_shell_quit (shell);
 
 	return TRUE;
@@ -276,9 +276,9 @@ shell_prepare_for_online (EShell *shell)
 }
 
 static void
-shell_ready_for_shutdown (EShell *shell,
-                          EActivity *activity,
-                          gboolean is_last_ref)
+shell_ready_for_quit (EShell *shell,
+                      EActivity *activity,
+                      gboolean is_last_ref)
 {
 	GList *list;
 
@@ -293,12 +293,12 @@ shell_ready_for_shutdown (EShell *shell,
 
 	g_object_remove_toggle_ref (
 		G_OBJECT (activity), (GToggleNotify)
-		shell_ready_for_shutdown, shell);
+		shell_ready_for_quit, shell);
 
 	/* Finalize the activity. */
 	g_object_ref (activity);
 
-	g_message ("Shutdown preparations complete.");
+	g_message ("Quit preparations complete.");
 
 	/* Destroy all watched windows.  Note, we iterate over a -copy-
 	 * of the watched windows list because the act of destroying a
@@ -750,13 +750,13 @@ shell_class_init (EShellClass *class)
 		E_TYPE_ACTIVITY);
 
 	/**
-	 * EShell::prepare-for-shutdown
+	 * EShell::prepare-for-quit
 	 * @shell: the #EShell which emitted the signal
-	 * @activity: the #EActivity for shutdown preparations
+	 * @activity: the #EActivity for quit preparations
 	 *
-	 * Emitted when the user elects to quit the application.  An
-	 * #EShellBackend should listen for this signal and make
-	 * preparations for shutting down.
+	 * Emitted when the user elects to quit the application, after
+	 * #EShell::quit-requested.  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
@@ -765,8 +765,8 @@ shell_class_init (EShellClass *class)
 	 * delay Evolution from actually shutting down until all backends
 	 * have unreferenced @activity.
 	 **/
-	signals[PREPARE_FOR_SHUTDOWN] = g_signal_new (
-		"prepare-for-shutdown",
+	signals[PREPARE_FOR_QUIT] = g_signal_new (
+		"prepare-for-quit",
 		G_OBJECT_CLASS_TYPE (object_class),
 		G_SIGNAL_RUN_FIRST,
 		0, NULL, NULL,
@@ -775,6 +775,27 @@ shell_class_init (EShellClass *class)
 		E_TYPE_ACTIVITY);
 
 	/**
+	 * EShell::quit-requested
+	 * @shell: the #EShell which emitted the signal
+	 *
+	 * Emitted when the user elects to quit the application, before
+	 * #EShell::prepare-for-quit.
+	 *
+	 * #EShellBackend<!-- -->s and editor windows can listen for
+	 * this signal to prompt the user to save changes or finish
+	 * scheduled operations immediately (such as sending mail in
+	 * Outbox).  If the user elects to cancel, the signal handler
+	 * should call e_shell_cancel_quit() to abort the quit.
+	 **/
+	signals[QUIT_REQUESTED] = g_signal_new (
+		"quit-requested",
+		G_OBJECT_CLASS_TYPE (object_class),
+		G_SIGNAL_RUN_FIRST,
+		0, NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	/**
 	 * EShell::send-receive
 	 * @shell: the #EShell which emitted the signal
 	 * @parent: a parent #GtkWindow
@@ -1433,30 +1454,57 @@ e_shell_quit (EShell *shell)
 	g_return_if_fail (E_IS_SHELL (shell));
 
 	/* Are preparations already in progress? */
-	if (shell->priv->preparing_for_shutdown != NULL)
+	if (shell->priv->preparing_for_quit != NULL)
+		return;
+
+	/* First give backends a chance to cancel quit. */
+	shell->priv->quit_cancelled = FALSE;
+	g_signal_emit (shell, signals[QUIT_REQUESTED], 0);
+	if (shell->priv->quit_cancelled)
 		return;
 
-	g_message ("Preparing for shutdown...");
+	g_message ("Preparing to quit...");
 
-	shell->priv->preparing_for_shutdown =
-		e_activity_new (_("Preparing to shut down..."));
+	shell->priv->preparing_for_quit =
+		e_activity_new (_("Preparing to quit..."));
 
 	g_object_add_toggle_ref (
-		G_OBJECT (shell->priv->preparing_for_shutdown),
-		(GToggleNotify) shell_ready_for_shutdown, shell);
+		G_OBJECT (shell->priv->preparing_for_quit),
+		(GToggleNotify) shell_ready_for_quit, shell);
 
 	g_object_add_weak_pointer (
-		G_OBJECT (shell->priv->preparing_for_shutdown),
-		&shell->priv->preparing_for_shutdown);
+		G_OBJECT (shell->priv->preparing_for_quit),
+		&shell->priv->preparing_for_quit);
 
 	g_signal_emit (
-		shell, signals[PREPARE_FOR_SHUTDOWN], 0,
-		shell->priv->preparing_for_shutdown);
+		shell, signals[PREPARE_FOR_QUIT], 0,
+		shell->priv->preparing_for_quit);
 
-	g_object_unref (shell->priv->preparing_for_shutdown);
+	g_object_unref (shell->priv->preparing_for_quit);
 
 	/* 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);
 }
+
+/**
+ * e_shell_cancel_quit:
+ * @shell: an #EShell
+ *
+ * This function may only be called from #EShell::quit-requested signal
+ * handlers to prevent Evolution from quitting.  Calling this will stop
+ * further emission of the #EShell::quit-requested signal.
+ *
+ * Note: This function has no effect during a #EShell::prepare-for-quit
+ * signal emission.
+ **/
+void
+e_shell_cancel_quit (EShell *shell)
+{
+	g_return_if_fail (E_IS_SHELL (shell));
+
+	shell->priv->quit_cancelled = TRUE;
+
+	g_signal_stop_emission (shell, signals[QUIT_REQUESTED], 0);
+}
diff --git a/shell/e-shell.h b/shell/e-shell.h
index 3b414e6..818607e 100644
--- a/shell/e-shell.h
+++ b/shell/e-shell.h
@@ -105,6 +105,7 @@ void		e_shell_event			(EShell *shell,
 						 const gchar *event_name,
 						 gpointer event_data);
 void		e_shell_quit			(EShell *shell);
+void		e_shell_cancel_quit		(EShell *shell);
 
 G_END_DECLS
 



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