[evolution] Convert some "Save As" actions to run asynchronously.



commit 86ecfc50539ddef82205551c11a6a13b135bbab4
Author: Matthew Barnes <mbarnes redhat com>
Date:   Sat Nov 7 12:44:44 2009 -0500

    Convert some "Save As" actions to run asynchronously.
    
    This introduces e-shell-utils for miscellaneous utility functions
    that integrate with the shell or shell settings.  First function
    is e_shell_run_save_dialog(), which automatically remembers the
    selected folder in the file chooser dialog.
    
    Also, kill some redundant save dialog functions, as well as some
    write-this-string-to-disk functions that block.

 addressbook/gui/widgets/e-addressbook-view.c    |   27 ---
 addressbook/gui/widgets/e-addressbook-view.h    |    2 -
 addressbook/gui/widgets/eab-gui-util.c          |  204 ++--------------------
 addressbook/gui/widgets/eab-gui-util.h          |    9 +-
 doc/reference/shell/eshell-sections.txt         |    2 -
 doc/reference/shell/tmpl/e-dialog-utils.sgml    |   11 --
 doc/reference/shell/tmpl/e-util.sgml            |   10 -
 doc/reference/shell/tmpl/eshell-unused.sgml     |   19 ++
 e-util/e-dialog-utils.c                         |   32 ----
 e-util/e-dialog-utils.h                         |    3 -
 e-util/e-file-utils.c                           |   39 ++++-
 e-util/e-util.c                                 |   59 -------
 e-util/e-util.h                                 |    2 -
 modules/addressbook/e-book-shell-view-actions.c |  118 +++++++++++++-
 modules/addressbook/e-book-shell-view-private.h |    3 +
 modules/calendar/e-cal-shell-view-actions.c     |   32 +++-
 modules/calendar/e-cal-shell-view-memopad.c     |   31 +++-
 modules/calendar/e-cal-shell-view-private.h     |    2 +
 modules/calendar/e-cal-shell-view-taskpad.c     |   29 +++-
 modules/calendar/e-memo-shell-view-actions.c    |   29 +++-
 modules/calendar/e-memo-shell-view-private.h    |    2 +
 modules/calendar/e-task-shell-view-actions.c    |   30 +++-
 modules/calendar/e-task-shell-view-private.h    |    2 +
 shell/Makefile.am                               |    5 +-
 shell/apps_evolution_shell.schemas.in           |    4 +-
 shell/e-shell-utils.c                           |  100 +++++++++++
 shell/e-shell-utils.h                           |   42 +++++
 shell/e-shell.c                                 |   14 +-
 widgets/misc/e-attachment-store.c               |    2 +-
 29 files changed, 467 insertions(+), 397 deletions(-)
---
diff --git a/addressbook/gui/widgets/e-addressbook-view.c b/addressbook/gui/widgets/e-addressbook-view.c
index 1583f26..280c4d3 100644
--- a/addressbook/gui/widgets/e-addressbook-view.c
+++ b/addressbook/gui/widgets/e-addressbook-view.c
@@ -1289,33 +1289,6 @@ e_addressbook_view_delete_selection(EAddressbookView *view, gboolean is_delete)
 }
 
 void
-e_addressbook_view_save_as (EAddressbookView *view,
-                            gboolean all)
-{
-	GList *list = NULL;
-	EBook *book;
-
-	g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (view));
-
-	book = e_addressbook_model_get_book (view->priv->model);
-
-	if (all) {
-		EBookQuery *query;
-
-		query = e_book_query_any_field_contains ("");
-		e_book_get_contacts (book, query, &list, NULL);
-		e_book_query_unref (query);
-	} else
-		list = e_addressbook_view_get_selected (view);
-
-	if (list != NULL) {
-		eab_contact_list_save (_("Save as vCard..."), list, NULL);
-		g_list_foreach (list, (GFunc) g_object_unref, NULL);
-		g_list_free (list);
-	}
-}
-
-void
 e_addressbook_view_view (EAddressbookView *view)
 {
 	EAddressbookModel *model;
diff --git a/addressbook/gui/widgets/e-addressbook-view.h b/addressbook/gui/widgets/e-addressbook-view.h
index 4e0f82e..ca709ff 100644
--- a/addressbook/gui/widgets/e-addressbook-view.h
+++ b/addressbook/gui/widgets/e-addressbook-view.h
@@ -97,8 +97,6 @@ ESelectionModel *
 EShellView *	e_addressbook_view_get_shell_view
 						(EAddressbookView *view);
 ESource *	e_addressbook_view_get_source	(EAddressbookView *view);
-void		e_addressbook_view_save_as	(EAddressbookView *view,
-						 gboolean all);
 void		e_addressbook_view_view		(EAddressbookView *view);
 void		e_addressbook_view_print	(EAddressbookView *view,
 						 GtkPrintOperationAction action);
diff --git a/addressbook/gui/widgets/eab-gui-util.c b/addressbook/gui/widgets/eab-gui-util.c
index 0aa6385..24fce0a 100644
--- a/addressbook/gui/widgets/eab-gui-util.c
+++ b/addressbook/gui/widgets/eab-gui-util.c
@@ -196,105 +196,6 @@ eab_prompt_save_dialog (GtkWindow *parent)
 	return e_error_run (parent, "addressbook:prompt-save", NULL);
 }
 
-static gint
-file_exists(GtkWindow *window, const gchar *filename)
-{
-	GtkWidget *dialog;
-	gint response;
-	gchar * utf8_filename;
-
-	utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
-	dialog = gtk_message_dialog_new (window,
-					 0,
-					 GTK_MESSAGE_QUESTION,
-					 GTK_BUTTONS_NONE,
-					 /* For Translators only: "it" refers to the filename %s. */
-					 _("%s already exists\nDo you want to overwrite it?"), utf8_filename);
-	g_free (utf8_filename);
-	gtk_dialog_add_buttons (GTK_DIALOG (dialog),
-				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-				_("Overwrite"), GTK_RESPONSE_ACCEPT,
-				NULL);
-
-	response = gtk_dialog_run (GTK_DIALOG (dialog));
-	gtk_widget_destroy (dialog);
-	return response;
-}
-
-typedef struct {
-	GtkWidget *filesel;
-	gchar *vcard;
-	gboolean has_multiple_contacts;
-} SaveAsInfo;
-
-static void
-save_it(GtkWidget *widget, SaveAsInfo *info)
-{
-	const gchar *filename;
-	gchar *uri;
-	gint response = 0;
-
-	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (info->filesel));
-	uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (info->filesel));
-
-	if (filename && g_file_test (filename, G_FILE_TEST_EXISTS)) {
-		response = file_exists(GTK_WINDOW (info->filesel), filename);
-		switch (response) {
-			case GTK_RESPONSE_ACCEPT : /* Overwrite */
-				break;
-			case GTK_RESPONSE_CANCEL : /* cancel */
-				return;
-		}
-	}
-
-	if (!e_write_file_uri (uri, info->vcard)) {
-		gchar *err_str_ext;
-		if (info->has_multiple_contacts) {
-			/* more than one, finding the total number of contacts might
-			 * hit performance while saving large number of contacts
-			 */
-			err_str_ext = ngettext ("contact", "contacts", 2);
-		}
-		else {
-			err_str_ext = ngettext ("contact", "contacts", 1);
-		}
-
-		/* translators: Arguments, err_str_ext (item to be saved: "contact"/"contacts"),
-		 * destination file name, and error code will fill the placeholders
-		 * {0}, {1} and {2}, respectively in the error message formed
-		 */
-		e_error_run (GTK_WINDOW (info->filesel), "addressbook:save-error",
-					 err_str_ext, filename, g_strerror (errno), NULL);
-		gtk_widget_destroy(GTK_WIDGET(info->filesel));
-		return;
-	}
-
-	gtk_widget_destroy(GTK_WIDGET(info->filesel));
-}
-
-static void
-close_it(GtkWidget *widget, SaveAsInfo *info)
-{
-	gtk_widget_destroy (GTK_WIDGET (info->filesel));
-}
-
-static void
-destroy_it(gpointer data, GObject *where_the_object_was)
-{
-	SaveAsInfo *info = data;
-	g_free (info->vcard);
-	g_free (info);
-}
-
-static void
-filechooser_response (GtkWidget *widget, gint response_id, SaveAsInfo *info)
-{
-	if (response_id == GTK_RESPONSE_ACCEPT)
-		save_it  (widget, info);
-	else
-		close_it (widget, info);
-}
-
 static gchar *
 make_safe_filename (gchar *name)
 {
@@ -381,99 +282,32 @@ eab_select_source (const gchar *title, const gchar *message, const gchar *select
 }
 
 void
-eab_contact_save (gchar *title, EContact *contact, GtkWindow *parent_window)
+eab_suggest_filename (GtkFileChooser *file_chooser,
+                      GList *contact_list)
 {
-	GtkWidget *filesel;
-	gchar *file;
-	gchar *name;
-	SaveAsInfo *info = g_new(SaveAsInfo, 1);
-
-	name = e_contact_get (contact, E_CONTACT_FILE_AS);
-	file = make_safe_filename (name);
-
-	info->has_multiple_contacts = FALSE;
-
-	filesel = gtk_file_chooser_dialog_new (title,
-					       parent_window,
-					       GTK_FILE_CHOOSER_ACTION_SAVE,
-					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-					       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-					       NULL);
-	gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_ACCEPT);
-
-	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filesel), g_get_home_dir ());
-	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filesel), file);
-	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filesel), FALSE);
-
-	info->filesel = filesel;
-	info->vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
-
-	g_signal_connect (G_OBJECT (filesel), "response",
-			  G_CALLBACK (filechooser_response), info);
-	g_object_weak_ref (G_OBJECT (filesel), destroy_it, info);
-
-	if (parent_window) {
-		gtk_window_set_transient_for (GTK_WINDOW (filesel),
-					      parent_window);
-		gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
-	}
+	gchar *current_name = NULL;
 
-	gtk_widget_show(GTK_WIDGET(filesel));
-	g_free (file);
-}
+	g_return_if_fail (GTK_IS_FILE_CHOOSER (file_chooser));
+	g_return_if_fail (contact_list != NULL);
 
-void
-eab_contact_list_save (gchar *title, GList *list, GtkWindow *parent_window)
-{
-	GtkWidget *filesel;
-	SaveAsInfo *info = g_new(SaveAsInfo, 1);
-	gchar *file;
-
-	filesel = gtk_file_chooser_dialog_new (title,
-					       parent_window,
-					       GTK_FILE_CHOOSER_ACTION_SAVE,
-					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-					       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
-					       NULL);
-	gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_ACCEPT);
-	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filesel), FALSE);
-
-	/* Check if the list has more than one contact */
-	if (g_list_next (list))
-		info->has_multiple_contacts = TRUE;
-	else
-		info->has_multiple_contacts = FALSE;
+	if (g_list_length (contact_list) == 1) {
+		EContact *contact = E_CONTACT (contact_list->data);
+		gchar *string;
 
-	/* This is a filename. Translators take note. */
-	if (list && list->data && list->next == NULL) {
-		gchar *name;
-		name = e_contact_get (E_CONTACT (list->data), E_CONTACT_FILE_AS);
-		if (!name)
-			name = e_contact_get (E_CONTACT (list->data), E_CONTACT_FULL_NAME);
-
-		file = make_safe_filename (name);
-	} else {
-		file = make_safe_filename (_("list"));
+		string = e_contact_get (contact, E_CONTACT_FILE_AS);
+		if (string == NULL)
+			string = e_contact_get (contact, E_CONTACT_FULL_NAME);
+		if (string != NULL)
+			current_name = make_safe_filename (string);
+		g_free (string);
 	}
 
-	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filesel), g_get_home_dir ());
-	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filesel), file);
-
-	info->filesel = filesel;
-	info->vcard = eab_contact_list_to_string (list);
+	if (current_name == NULL)
+		current_name = make_safe_filename (_("list"));
 
-	g_signal_connect (G_OBJECT (filesel), "response",
-			  G_CALLBACK (filechooser_response), info);
-	g_object_weak_ref (G_OBJECT (filesel), destroy_it, info);
-
-	if (parent_window) {
-		gtk_window_set_transient_for (GTK_WINDOW (filesel),
-					      parent_window);
-		gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
-	}
+	gtk_file_chooser_set_current_name (file_chooser, current_name);
 
-	gtk_widget_show(GTK_WIDGET(filesel));
-	g_free (file);
+	g_free (current_name);
 }
 
 typedef struct ContactCopyProcess_ ContactCopyProcess;
diff --git a/addressbook/gui/widgets/eab-gui-util.h b/addressbook/gui/widgets/eab-gui-util.h
index 7422eab..838343a 100644
--- a/addressbook/gui/widgets/eab-gui-util.h
+++ b/addressbook/gui/widgets/eab-gui-util.h
@@ -43,13 +43,8 @@ void                eab_transfer_contacts         (EBook       *source,
 						   gboolean     delete_from_source,
 						   GtkWindow   *parent_window);
 
-void                eab_contact_save              (gchar *title,
-						   EContact *contact,
-						   GtkWindow *parent_window);
-
-void                eab_contact_list_save         (gchar *title,
-						   GList *list,
-						   GtkWindow *parent_window);
+void                eab_suggest_filename          (GtkFileChooser *file_chooser,
+						   GList *contact_list);
 
 GtkWidget *eab_create_image_chooser_widget (gchar *name, gchar *string1, gchar *string2, gint int1, gint int2);
 
diff --git a/doc/reference/shell/eshell-sections.txt b/doc/reference/shell/eshell-sections.txt
index b69e65f..ae9ecaf 100644
--- a/doc/reference/shell/eshell-sections.txt
+++ b/doc/reference/shell/eshell-sections.txt
@@ -360,7 +360,6 @@ e_datetime_format_format_tm
 <FILE>e-dialog-utils</FILE>
 <TITLE>Dialog Window Functions (Legacy)</TITLE>
 e_notice
-e_file_dialog_save
 e_file_get_save_filesel
 e_file_can_save
 e_file_check_local
@@ -554,7 +553,6 @@ e_str_compare
 e_str_case_compare
 e_collate_compare
 e_int_compare
-e_write_file_uri
 e_color_to_value
 e_format_number
 ESortCompareFunc
diff --git a/doc/reference/shell/tmpl/e-dialog-utils.sgml b/doc/reference/shell/tmpl/e-dialog-utils.sgml
index 55b595a..233bac2 100644
--- a/doc/reference/shell/tmpl/e-dialog-utils.sgml
+++ b/doc/reference/shell/tmpl/e-dialog-utils.sgml
@@ -28,17 +28,6 @@ Dialog Window Functions (Legacy)
 @Varargs: 
 
 
-<!-- ##### FUNCTION e_file_dialog_save ##### -->
-<para>
-
-</para>
-
- parent: 
- title: 
- fname: 
- Returns: 
-
-
 <!-- ##### FUNCTION e_file_get_save_filesel ##### -->
 <para>
 
diff --git a/doc/reference/shell/tmpl/e-util.sgml b/doc/reference/shell/tmpl/e-util.sgml
index 50af745..15d7d5f 100644
--- a/doc/reference/shell/tmpl/e-util.sgml
+++ b/doc/reference/shell/tmpl/e-util.sgml
@@ -176,16 +176,6 @@ Miscellaneous Utility Functions
 @Returns: 
 
 
-<!-- ##### FUNCTION e_write_file_uri ##### -->
-<para>
-
-</para>
-
- filename: 
- data: 
- Returns: 
-
-
 <!-- ##### FUNCTION e_color_to_value ##### -->
 <para>
 
diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml
index 99ad9d4..2f908a7 100644
--- a/doc/reference/shell/tmpl/eshell-unused.sgml
+++ b/doc/reference/shell/tmpl/eshell-unused.sgml
@@ -1850,6 +1850,16 @@ intelligent
 @revision: 
 @Returns: 
 
+<!-- ##### FUNCTION e_file_dialog_save ##### -->
+<para>
+
+</para>
+
+ parent: 
+ title: 
+ fname: 
+ Returns: 
+
 <!-- ##### FUNCTION e_shell_backend_get_filename ##### -->
 <para>
 
@@ -2254,6 +2264,15 @@ intelligent
 @uri: 
 @Returns: 
 
+<!-- ##### FUNCTION e_write_file_uri ##### -->
+<para>
+
+</para>
+
+ filename: 
+ data: 
+ Returns: 
+
 <!-- ##### FUNCTION es_event_get_type ##### -->
 <para>
 
diff --git a/e-util/e-dialog-utils.c b/e-util/e-dialog-utils.c
index 952d73f..df0d8f5 100644
--- a/e-util/e-dialog-utils.c
+++ b/e-util/e-dialog-utils.c
@@ -72,38 +72,6 @@ e_notice (gpointer parent, GtkMessageType type, const gchar *format, ...)
 	gtk_widget_destroy (dialog);
 }
 
-gchar *
-e_file_dialog_save (GtkWindow *parent,
-                    const gchar *title,
-                    const gchar *fname)
-{
-	GtkWidget *dialog;
-	gchar *filename = NULL;
-	gchar *uri;
-
-	g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
-
-	dialog = e_file_get_save_filesel (
-		parent, title, fname, GTK_FILE_CHOOSER_ACTION_SAVE);
-
-	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
-		goto exit;
-
-	uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
-
-	if (e_file_can_save (GTK_WINDOW (dialog), uri)) {
-		e_file_update_save_path (
-			gtk_file_chooser_get_current_folder_uri (
-			GTK_FILE_CHOOSER (dialog)), TRUE);
-		filename = uri;  /* FIXME This looks wrong. */
-	}
-
-exit:
-	gtk_widget_destroy (dialog);
-
-	return filename;
-}
-
 /**
  * e_file_get_save_filesel:
  * @parent: parent window
diff --git a/e-util/e-dialog-utils.h b/e-util/e-dialog-utils.h
index b4f305b..2457f1c 100644
--- a/e-util/e-dialog-utils.h
+++ b/e-util/e-dialog-utils.h
@@ -29,9 +29,6 @@ void		e_notice			(gpointer parent,
 						 GtkMessageType type,
 						 const gchar *format,
 						 ...);
-gchar *		e_file_dialog_save		(GtkWindow *parent,
-						 const gchar *title,
-						 const gchar *fname);
 GtkWidget *	e_file_get_save_filesel		(GtkWindow *parent,
 						 const gchar *title,
 						 const gchar *name,
diff --git a/e-util/e-file-utils.c b/e-util/e-file-utils.c
index f8adcc7..015da07 100644
--- a/e-util/e-file-utils.c
+++ b/e-util/e-file-utils.c
@@ -86,6 +86,24 @@ file_replace_contents_cb (GFile *file,
 	g_object_unref (activity);
 }
 
+/**
+ * e_file_replace_contents_async:
+ * @file: input #GFile
+ * @contents: string of contents to replace the file with
+ * @length: the length of @contents in bytes
+ * @etag: a new entity tag for the @file, or %NULL
+ * @make_backup: %TRUE if a backup should be created
+ * @flags: a set of #GFileCreateFlags
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to the callback function
+ *
+ * This is a wrapper for g_file_replace_contents_async() that also returns
+ * an #EActivity to track the file operation.  Cancelling the activity will
+ * cancel the file operation.  See g_file_replace_contents_async() for more
+ * details.
+ *
+ * Returns: an #EActivity for the file operation
+ **/
 EActivity *
 e_file_replace_contents_async (GFile *file,
                                const gchar *contents,
@@ -111,9 +129,12 @@ e_file_replace_contents_async (GFile *file,
 
 	uri = g_file_get_uri (file);
 	filename = g_filename_from_uri (uri, &hostname, NULL);
-	basename = g_filename_display_basename (filename);
+	if (filename != NULL)
+		basename = g_filename_display_basename (filename);
+	else
+		basename = g_strdup (_("(Unknown Filename)"));
 
-	if (hostname != NULL) {
+	if (hostname == NULL) {
 		/* Translators: The string value is the basename of a file. */
 		format = _("Writing \"%s\"");
 		description = g_strdup_printf (format, basename);
@@ -151,6 +172,20 @@ e_file_replace_contents_async (GFile *file,
 	return activity;
 }
 
+/**
+ * e_file_replace_contents_finish:
+ * @file: input #GFile
+ * @result: a #GAsyncResult
+ * @new_etag: return location for a new entity tag
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an asynchronous replace of the given @file.  See
+ * e_file_replace_contents_async().  Sets @new_etag to the new entity
+ * tag for the document, if present.  Free it with g_free() when it is
+ * no longer needed.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ **/
 gboolean
 e_file_replace_contents_finish (GFile *file,
                                 GAsyncResult *result,
diff --git a/e-util/e-util.c b/e-util/e-util.c
index bf1f387..9ad252a 100644
--- a/e-util/e-util.c
+++ b/e-util/e-util.c
@@ -618,65 +618,6 @@ e_int_compare (gconstpointer x, gconstpointer y)
 	return (nx == ny) ? 0 : (nx < ny) ? -1 : 1;
 }
 
-gboolean
-e_write_file_uri (const gchar *filename,
-                  const gchar *data)
-{
-	gboolean res;
-	gsize length;
-	GFile *file;
-	GOutputStream *stream;
-	GError *error = NULL;
-
-	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (data != NULL, FALSE);
-
-	length = strlen (data);
-
-	/* if it is uri, then create file for uri, otherwise for path */
-	if (strstr (filename, "://"))
-		file = g_file_new_for_uri (filename);
-	else
-		file = g_file_new_for_path (filename);
-
-	if (!file) {
-		g_warning ("Couldn't save item");
-		return FALSE;
-	}
-
-	stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
-	g_object_unref (file);
-
-	if (!stream || error) {
-		g_warning ("Couldn't save item%s%s", error ? ": " : "", error ? error->message : "");
-
-		if (stream)
-			g_object_unref (stream);
-
-		if (error)
-			g_error_free (error);
-
-		return FALSE;
-	}
-
-	res = g_output_stream_write_all (stream, data, length, NULL, NULL, &error);
-
-	if (error) {
-		g_warning ("Couldn't save item: %s", error->message);
-		g_clear_error (&error);
-	}
-
-	g_output_stream_close (stream, NULL, &error);
-	g_object_unref (stream);
-
-	if (error) {
-		g_warning ("Couldn't close output stream: %s", error->message);
-		g_error_free (error);
-	}
-
-	return res;
-}
-
 /**
  * e_color_to_value:
  * color: a #GdkColor
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 88ebaa2..81ca053 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -81,8 +81,6 @@ gint		e_collate_compare		(gconstpointer x,
 						 gconstpointer y);
 gint		e_int_compare                   (gconstpointer x,
 						 gconstpointer y);
-gboolean	e_write_file_uri		(const gchar *filename,
-						 const gchar *data);
 guint32		e_color_to_value		(GdkColor *color);
 
 /* This only makes a filename safe for usage as a filename.
diff --git a/modules/addressbook/e-book-shell-view-actions.c b/modules/addressbook/e-book-shell-view-actions.c
index c680c64..6882f29 100644
--- a/modules/addressbook/e-book-shell-view-actions.c
+++ b/modules/addressbook/e-book-shell-view-actions.c
@@ -192,14 +192,75 @@ static void
 action_address_book_save_as_cb (GtkAction *action,
                                 EBookShellView *book_shell_view)
 {
+	EShell *shell;
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	EBookShellContent *book_shell_content;
+	EAddressbookModel *model;
 	EAddressbookView *view;
+	EActivity *activity;
+	EBookQuery *query;
+	EBook *book;
+	GList *list = NULL;
+	GFile *file;
+	gchar *string;
+
+	shell_view = E_SHELL_VIEW (book_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (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);
 
-	e_addressbook_view_save_as (view, TRUE);
+	model = e_addressbook_view_get_model (view);
+	book = e_addressbook_model_get_book (model);
+
+	query = e_book_query_any_field_contains ("");
+	e_book_get_contacts (book, query, &list, NULL);
+	e_book_query_unref (query);
+
+	if (list == NULL)
+		goto exit;
+
+	file = e_shell_run_save_dialog (
+		shell, _("Save as vCard"),
+		(GtkCallback) eab_suggest_filename, list);
+	if (file == NULL)
+		goto exit;
+
+	string = eab_contact_list_to_string (list);
+	if (string == NULL) {
+		g_warning ("Could not convert contact list to a string");
+		g_object_unref (file);
+		goto exit;
+	}
+
+	/* XXX No callback means errors are discarded.
+	 *
+	 *     There's an EError for this which I'm not using
+	 *     until I figure out a better way to display errors:
+	 *
+	 *     "addressbook:save-error"
+	 */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
+
+	g_object_unref (file);
+
+exit:
+	g_list_foreach (list, (GFunc) g_object_unref, NULL);
+	g_list_free (list);
 }
 
 static void
@@ -454,14 +515,67 @@ static void
 action_contact_save_as_cb (GtkAction *action,
                            EBookShellView *book_shell_view)
 {
+	EShell *shell;
+	EShellView *shell_view;
+	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	EBookShellContent *book_shell_content;
 	EAddressbookView *view;
+	EActivity *activity;
+	GList *list;
+	GFile *file;
+	gchar *string;
+
+	shell_view = E_SHELL_VIEW (book_shell_view);
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (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);
 
-	e_addressbook_view_save_as (view, FALSE);
+	list = e_addressbook_view_get_selected (view);
+
+	if (list == NULL)
+		goto exit;
+
+	file = e_shell_run_save_dialog (
+		shell, _("Save as vCard"),
+		(GtkCallback) eab_suggest_filename, list);
+	if (file == NULL)
+		goto exit;
+
+	string = eab_contact_list_to_string (list);
+	if (string == NULL) {
+		g_warning ("Could not convert contact list to a string");
+		g_object_unref (file);
+		goto exit;
+	}
+
+	/* XXX No callback means errors are discarded.
+	 *
+	 *     There an EError for this which I'm not using
+	 *     until I figure out a better way to display errors:
+	 *
+	 *     "addressbook:save-error"
+	 */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
+
+	g_object_unref (file);
+
+exit:
+	g_list_foreach (list, (GFunc) g_object_unref, NULL);
+	g_list_free (list);
 }
 
 static void
diff --git a/modules/addressbook/e-book-shell-view-private.h b/modules/addressbook/e-book-shell-view-private.h
index fa86246..c1aa300 100644
--- a/modules/addressbook/e-book-shell-view-private.h
+++ b/modules/addressbook/e-book-shell-view-private.h
@@ -34,11 +34,14 @@
 
 #include "e-util/e-util.h"
 #include "e-util/e-binding.h"
+#include "e-util/e-file-utils.h"
 #include "e-util/gconf-bridge.h"
 #include "shell/e-shell-content.h"
 #include "shell/e-shell-sidebar.h"
+#include "shell/e-shell-utils.h"
 #include "misc/e-popup-action.h"
 
+#include "addressbook/util/eab-book-util.h"
 #include "addressbook/gui/contact-editor/e-contact-editor.h"
 #include "addressbook/gui/contact-list-editor/e-contact-list-editor.h"
 #include "addressbook/gui/widgets/eab-gui-util.h"
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
index 408d02f..4fa8b07 100644
--- a/modules/calendar/e-cal-shell-view-actions.c
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -1065,8 +1065,10 @@ static void
 action_event_save_as_cb (GtkAction *action,
                          ECalShellView *cal_shell_view)
 {
+	EShell *shell;
 	EShellView *shell_view;
 	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	ECalShellContent *cal_shell_content;
 	GnomeCalendarViewType view_type;
 	GnomeCalendar *calendar;
@@ -1074,12 +1076,15 @@ action_event_save_as_cb (GtkAction *action,
 	ECalendarViewEvent *event;
 	ECal *client;
 	icalcomponent *icalcomp;
+	EActivity *activity;
 	GList *selected;
-	gchar *filename = NULL;
+	GFile *file;
 	gchar *string = NULL;
 
 	shell_view = E_SHELL_VIEW (cal_shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
 
 	cal_shell_content = cal_shell_view->priv->cal_shell_content;
 	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
@@ -1093,10 +1098,10 @@ action_event_save_as_cb (GtkAction *action,
 	client = event->comp_data->client;
 	icalcomp = event->comp_data->icalcomp;
 
-	filename = e_file_dialog_save (
-		GTK_WINDOW (shell_window), _("Save As..."), NULL);
-	if (filename == NULL)
-		goto exit;
+	file = e_shell_run_save_dialog (
+		shell, _("Save as iCalendar"), NULL, NULL);
+	if (file == NULL)
+		return;
 
 	string = e_cal_get_component_as_string (client, icalcomp);
 	if (string == NULL) {
@@ -1104,11 +1109,20 @@ action_event_save_as_cb (GtkAction *action,
 		goto exit;
 	}
 
-	e_write_file_uri (filename, string);
+	/* XXX No callbacks means errors are discarded. */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
 
 exit:
-	g_free (filename);
-	g_free (string);
+	g_object_unref (file);
 
 	g_list_free (selected);
 }
@@ -1372,7 +1386,7 @@ static GtkActionEntry calendar_entries[] = {
 
 	{ "event-save-as",
 	  GTK_STOCK_SAVE_AS,
-	  NULL,
+	  N_("Save as iCalendar..."),
 	  NULL,
 	  NULL,  /* XXX Add a tooltip! */
 	  G_CALLBACK (action_event_save_as_cb) },
diff --git a/modules/calendar/e-cal-shell-view-memopad.c b/modules/calendar/e-cal-shell-view-memopad.c
index 7d4c1a5..88cb53f 100644
--- a/modules/calendar/e-cal-shell-view-memopad.c
+++ b/modules/calendar/e-cal-shell-view-memopad.c
@@ -233,17 +233,22 @@ static void
 action_calendar_memopad_save_as_cb (GtkAction *action,
                                     ECalShellView *cal_shell_view)
 {
+	EShell *shell;
 	EShellView *shell_view;
 	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	ECalShellContent *cal_shell_content;
 	EMemoTable *memo_table;
 	ECalModelComponent *comp_data;
+	EActivity *activity;
 	GSList *list;
-	gchar *filename;
+	GFile *file;
 	gchar *string;
 
 	shell_view = E_SHELL_VIEW (cal_shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
 
 	cal_shell_content = cal_shell_view->priv->cal_shell_content;
 	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
@@ -253,9 +258,9 @@ action_calendar_memopad_save_as_cb (GtkAction *action,
 	comp_data = list->data;
 	g_slist_free (list);
 
-	filename = e_file_dialog_save (
-		GTK_WINDOW (shell_window), _("Save as..."), NULL);
-	if (filename == NULL)
+	file = e_shell_run_save_dialog (
+		shell, _("Save as iCalendar"), NULL, NULL);
+	if (file == NULL)
 		return;
 
 	/* XXX We only save the first selected memo. */
@@ -263,13 +268,23 @@ action_calendar_memopad_save_as_cb (GtkAction *action,
 		comp_data->client, comp_data->icalcomp);
 	if (string == NULL) {
 		g_warning ("Could not convert memo to a string.");
+		g_object_unref (file);
 		return;
 	}
 
-	e_write_file_uri (filename, string);
+	/* XXX No callback means errors are discarded. */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
 
-	g_free (filename);
-	g_free (string);
+	g_object_unref (file);
 }
 
 static GtkActionEntry calendar_memopad_entries[] = {
@@ -332,7 +347,7 @@ static GtkActionEntry calendar_memopad_entries[] = {
 
 	{ "calendar-memopad-save-as",
 	  GTK_STOCK_SAVE_AS,
-	  NULL,
+	  N_("Save as iCalendar..."),
 	  NULL,
 	  NULL,  /* XXX Add a tooltip! */
 	  G_CALLBACK (action_calendar_memopad_save_as_cb) }
diff --git a/modules/calendar/e-cal-shell-view-private.h b/modules/calendar/e-cal-shell-view-private.h
index 6900311..2ee0cff 100644
--- a/modules/calendar/e-cal-shell-view-private.h
+++ b/modules/calendar/e-cal-shell-view-private.h
@@ -33,8 +33,10 @@
 
 #include "e-util/e-binding.h"
 #include "e-util/e-dialog-utils.h"
+#include "e-util/e-file-utils.h"
 #include "e-util/e-error.h"
 #include "e-util/e-util.h"
+#include "shell/e-shell-utils.h"
 #include "widgets/misc/e-popup-action.h"
 
 #include "calendar/common/authentication.h"
diff --git a/modules/calendar/e-cal-shell-view-taskpad.c b/modules/calendar/e-cal-shell-view-taskpad.c
index 3e83e04..5f2fd3a 100644
--- a/modules/calendar/e-cal-shell-view-taskpad.c
+++ b/modules/calendar/e-cal-shell-view-taskpad.c
@@ -301,17 +301,22 @@ static void
 action_calendar_taskpad_save_as_cb (GtkAction *action,
                                     ECalShellView *cal_shell_view)
 {
+	EShell *shell;
 	EShellView *shell_view;
 	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	ECalShellContent *cal_shell_content;
 	ECalendarTable *task_table;
 	ECalModelComponent *comp_data;
+	EActivity *activity;
 	GSList *list;
-	gchar *filename;
+	GFile *file;
 	gchar *string;
 
 	shell_view = E_SHELL_VIEW (cal_shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
 
 	cal_shell_content = cal_shell_view->priv->cal_shell_content;
 	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
@@ -321,22 +326,32 @@ action_calendar_taskpad_save_as_cb (GtkAction *action,
 	comp_data = list->data;
 	g_slist_free (list);
 
-	filename = e_file_dialog_save (
-		GTK_WINDOW (shell_window), _("Save as..."), NULL);
-	if (filename == NULL)
+	file = e_shell_run_save_dialog (
+		shell, _("Save as iCalendar"), NULL, NULL);
+	if (file == NULL)
 		return;
 
 	string = e_cal_get_component_as_string (
 		comp_data->client, comp_data->icalcomp);
 	if (string == NULL) {
 		g_warning ("Could not convert task to a string");
+		g_object_unref (file);
 		return;
 	}
 
-	e_write_file_uri (filename, string);
+	/* XXX No callback means errors are discarded. */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
 
-	g_free (filename);
-	g_free (string);
+	g_object_unref (file);
 }
 
 static GtkActionEntry calendar_taskpad_entries[] = {
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
index ab8be53..31f12b3 100644
--- a/modules/calendar/e-memo-shell-view-actions.c
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -471,17 +471,22 @@ static void
 action_memo_save_as_cb (GtkAction *action,
                         EMemoShellView *memo_shell_view)
 {
+	EShell *shell;
 	EShellView *shell_view;
 	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	EMemoShellContent *memo_shell_content;
 	EMemoTable *memo_table;
 	ECalModelComponent *comp_data;
+	EActivity *activity;
 	GSList *list;
-	gchar *filename;
+	GFile *file;
 	gchar *string;
 
 	shell_view = E_SHELL_VIEW (memo_shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
 
 	memo_shell_content = memo_shell_view->priv->memo_shell_content;
 	memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
@@ -491,9 +496,9 @@ action_memo_save_as_cb (GtkAction *action,
 	comp_data = list->data;
 	g_slist_free (list);
 
-	filename = e_file_dialog_save (
-		GTK_WINDOW (shell_window), _("Save as..."), NULL);
-	if (filename == NULL)
+	file = e_shell_run_save_dialog (
+		shell, _("Save as iCalendar"), NULL, NULL);
+	if (file == NULL)
 		return;
 
 	/* XXX We only save the first selected memo. */
@@ -501,13 +506,23 @@ action_memo_save_as_cb (GtkAction *action,
 		comp_data->client, comp_data->icalcomp);
 	if (string == NULL) {
 		g_warning ("Could not convert memo to a string");
+		g_object_unref (file);
 		return;
 	}
 
-	e_write_file_uri (filename, string);
+	/* XXX No callback means errors are discarded. */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
 
-	g_free (filename);
-	g_free (string);
+	g_object_unref (file);
 }
 
 static void
diff --git a/modules/calendar/e-memo-shell-view-private.h b/modules/calendar/e-memo-shell-view-private.h
index 276e6e5..3755694 100644
--- a/modules/calendar/e-memo-shell-view-private.h
+++ b/modules/calendar/e-memo-shell-view-private.h
@@ -31,9 +31,11 @@
 
 #include "e-util/e-binding.h"
 #include "e-util/e-dialog-utils.h"
+#include "e-util/e-file-utils.h"
 #include "e-util/e-error.h"
 #include "e-util/e-util.h"
 #include "e-util/gconf-bridge.h"
+#include "shell/e-shell-utils.h"
 #include "widgets/misc/e-popup-action.h"
 
 #include "calendar/gui/comp-util.h"
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
index fb7b89b..282d5a3 100644
--- a/modules/calendar/e-task-shell-view-actions.c
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -598,17 +598,22 @@ static void
 action_task_save_as_cb (GtkAction *action,
                         ETaskShellView *task_shell_view)
 {
+	EShell *shell;
 	EShellView *shell_view;
 	EShellWindow *shell_window;
+	EShellBackend *shell_backend;
 	ETaskShellContent *task_shell_content;
 	ECalendarTable *task_table;
 	ECalModelComponent *comp_data;
+	EActivity *activity;
 	GSList *list;
-	gchar *filename;
+	GFile *file;
 	gchar *string;
 
 	shell_view = E_SHELL_VIEW (task_shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
+	shell_backend = e_shell_view_get_shell_backend (shell_view);
+	shell = e_shell_window_get_shell (shell_window);
 
 	task_shell_content = task_shell_view->priv->task_shell_content;
 	task_table = e_task_shell_content_get_task_table (task_shell_content);
@@ -618,22 +623,33 @@ action_task_save_as_cb (GtkAction *action,
 	comp_data = list->data;
 	g_slist_free (list);
 
-	filename = e_file_dialog_save (
-		GTK_WINDOW (shell_window), _("Save as..."), NULL);
-	if (filename == NULL)
+	file = e_shell_run_save_dialog (
+		shell, _("Save as iCalendar"), NULL, NULL);
+	if (file == NULL)
 		return;
 
+	/* XXX We only save the first selected task. */
 	string = e_cal_get_component_as_string (
 		comp_data->client, comp_data->icalcomp);
 	if (string == NULL) {
 		g_warning ("Could not convert task to a string");
+		g_object_unref (file);
 		return;
 	}
 
-	e_write_file_uri (filename, string);
+	/* XXX No callback means errors are discarded. */
+	activity = e_file_replace_contents_async (
+		file, string, strlen (string), NULL, FALSE,
+		G_FILE_CREATE_NONE, (GAsyncReadyCallback) NULL, NULL);
+	e_shell_backend_add_activity (shell_backend, activity);
+
+	/* Free the string when the activity is finalized. */
+	g_object_set_data_full (
+		G_OBJECT (activity),
+		"file-content", string,
+		(GDestroyNotify) g_free);
 
-	g_free (filename);
-	g_free (string);
+	g_object_unref (file);
 }
 
 static void
diff --git a/modules/calendar/e-task-shell-view-private.h b/modules/calendar/e-task-shell-view-private.h
index b369b4c..38b03ae 100644
--- a/modules/calendar/e-task-shell-view-private.h
+++ b/modules/calendar/e-task-shell-view-private.h
@@ -32,9 +32,11 @@
 
 #include "e-util/e-binding.h"
 #include "e-util/e-dialog-utils.h"
+#include "e-util/e-file-utils.h"
 #include "e-util/e-error.h"
 #include "e-util/e-util.h"
 #include "e-util/gconf-bridge.h"
+#include "shell/e-shell-utils.h"
 #include "widgets/misc/e-popup-action.h"
 
 #include "calendar/common/authentication.h"
diff --git a/shell/Makefile.am b/shell/Makefile.am
index d8eea3c..608ecd3 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -26,6 +26,7 @@ eshellinclude_HEADERS = 			\
 	e-shell-sidebar.h			\
 	e-shell-switcher.h			\
 	e-shell-taskbar.h			\
+	e-shell-utils.h				\
 	e-shell-view.h				\
 	e-shell-window.h			\
 	e-shell-window-actions.h		\
@@ -59,7 +60,7 @@ libeshell_la_CPPFLAGS =						\
 
 libeshell_la_SOURCES =				\
 	$(NM_SUPPORT_FILES)			\
-	$(IDL_GENERATED)			\
+	$(eshellinclude_HEADERS)		\
 	e-shell.c				\
 	e-shell-backend.c			\
 	e-shell-content.c			\
@@ -67,11 +68,11 @@ libeshell_la_SOURCES =				\
 	e-shell-sidebar.c			\
 	e-shell-switcher.c			\
 	e-shell-taskbar.c			\
+	e-shell-utils.c				\
 	e-shell-view.c				\
 	e-shell-window.c			\
 	e-shell-window-private.c		\
 	e-shell-window-private.h		\
-	$(eshellinclude_HEADERS)		\
 	e-shell-migrate.c			\
 	e-shell-migrate.h			\
 	e-shell-window-actions.c		\
diff --git a/shell/apps_evolution_shell.schemas.in b/shell/apps_evolution_shell.schemas.in
index 157df8c..b5aa3e6 100644
--- a/shell/apps_evolution_shell.schemas.in
+++ b/shell/apps_evolution_shell.schemas.in
@@ -59,8 +59,8 @@
     <!-- Initial GtkFileChooser Folder -->
 
     <schema>
-      <key>/schemas/apps/evolution/shell/current_folder</key>
-      <applyto>/apps/evolution/shell/current_folder</applyto>
+      <key>/schemas/apps/evolution/shell/file_chooser_folder</key>
+      <applyto>/apps/evolution/shell/file_chooser_folder</applyto>
       <owner>evolution</owner>
       <type>string</type>
       <locale name="C">
diff --git a/shell/e-shell-utils.c b/shell/e-shell-utils.c
new file mode 100644
index 0000000..9d3d6f5
--- /dev/null
+++ b/shell/e-shell-utils.c
@@ -0,0 +1,100 @@
+/*
+ * e-shell-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-shell-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+/**
+ * e_shell_run_save_dialog:
+ * @shell: an #EShell
+ * @title: file chooser dialog title
+ * @customize_func: optional dialog customization function
+ * @customize_data: optional data to pass to @customize_func
+ *
+ * Runs a #GtkFileChooserDialog in save mode with the given title and
+ * returns the selected #GFile.  It automatically remembers the selected
+ * folder.  If @customize_func is provided, the function is called just
+ * prior to running the dialog (the file chooser is the first argument,
+ * @customize_data is the second).  If the user cancels the dialog the
+ * function will return %NULL.
+ *
+ * Returns: the #GFile to save to, or %NULL
+ **/
+GFile *
+e_shell_run_save_dialog (EShell *shell,
+                         const gchar *title,
+                         GtkCallback customize_func,
+                         gpointer customize_data)
+{
+	EShellSettings *shell_settings;
+	GtkFileChooser *file_chooser;
+	GFile *chosen_file = NULL;
+	GtkWidget *dialog;
+	GtkWindow *parent;
+	const gchar *property_name;
+	gchar *uri;
+
+	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+
+	property_name = "file-chooser-folder";
+	shell_settings = e_shell_get_shell_settings (shell);
+
+	parent = e_shell_get_active_window (shell);
+
+	dialog = gtk_file_chooser_dialog_new (
+		title, parent,
+		GTK_FILE_CHOOSER_ACTION_SAVE,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
+
+	file_chooser = GTK_FILE_CHOOSER (dialog);
+
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	gtk_file_chooser_set_local_only (file_chooser, FALSE);
+	gtk_file_chooser_set_do_overwrite_confirmation (file_chooser, TRUE);
+
+	/* Restore the current folder from the previous file chooser. */
+	uri = e_shell_settings_get_string (shell_settings, property_name);
+	if (uri != NULL)
+		gtk_file_chooser_set_current_folder_uri (file_chooser, uri);
+	g_free (uri);
+
+	/* Allow further customizations before running the dialog. */
+	if (customize_func != NULL)
+		customize_func (dialog, customize_data);
+
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+		goto exit;
+
+	chosen_file = gtk_file_chooser_get_file (file_chooser);
+
+	/* Save the current folder for subsequent file choosers. */
+	uri = gtk_file_chooser_get_current_folder_uri (file_chooser);
+	e_shell_settings_set_string (shell_settings, property_name, uri);
+	g_free (uri);
+
+exit:
+	gtk_widget_destroy (dialog);
+
+	return chosen_file;
+}
diff --git a/shell/e-shell-utils.h b/shell/e-shell-utils.h
new file mode 100644
index 0000000..6fedca8
--- /dev/null
+++ b/shell/e-shell-utils.h
@@ -0,0 +1,42 @@
+/*
+ * e-shell-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/**
+ * SECTION: e-shell-utils
+ * @short_description: high-level utilities with shell integration
+ * @include: shell/e-shell-utils.h
+ **/
+
+#ifndef E_SHELL_UTILS_H
+#define E_SHELL_UTILS_H
+
+#include <shell/e-shell.h>
+
+G_BEGIN_DECLS
+
+GFile *		e_shell_run_save_dialog		(EShell *shell,
+						 const gchar *title,
+						 GtkCallback customize_func,
+						 gpointer customize_data);
+
+G_END_DECLS
+
+#endif /* E_SHELL_UTILS_H */
diff --git a/shell/e-shell.c b/shell/e-shell.c
index 61ce8d7..b0a1490 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -903,16 +903,6 @@ shell_class_init (EShellClass *class)
 		0, NULL, NULL,
 		g_cclosure_marshal_VOID__VOID,
 		G_TYPE_NONE, 0);
-
-	/* Install some application-wide settings. */
-
-	e_shell_settings_install_property (
-		g_param_spec_string (
-			"file-chooser-folder",
-			NULL,
-			NULL,
-			NULL,
-			G_PARAM_READWRITE));
 }
 
 static void
@@ -950,6 +940,10 @@ shell_init (EShell *shell)
 	 *     otherwise the GConf bindings will not get set up. */
 
 	e_shell_settings_install_property_for_key (
+		"file-chooser-folder",
+		"/apps/evolution/shell/file_chooser_folder");
+
+	e_shell_settings_install_property_for_key (
 		"start-offline",
 		"/apps/evolution/shell/start_offline");
 
diff --git a/widgets/misc/e-attachment-store.c b/widgets/misc/e-attachment-store.c
index c99bdea..4c87d65 100644
--- a/widgets/misc/e-attachment-store.c
+++ b/widgets/misc/e-attachment-store.c
@@ -142,7 +142,7 @@ attachment_store_constructed (GObject *object)
 
 	bridge = gconf_bridge_get ();
 
-	key = "/apps/evolution/shell/current_folder";
+	key = "/apps/evolution/shell/file_chooser_folder";
 	gconf_bridge_bind_property (bridge, key, object, "current-folder");
 }
 



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