Composer autosave cleanups.

This simplifies the async autosave logic and improves error handling.

Hoping this will solve bug #616987 but I've yet to reproduce it myself.
This commit is contained in:
Matthew Barnes
2010-08-30 14:23:33 -04:00
parent 680c58a766
commit 035fbcd841
5 changed files with 136 additions and 114 deletions

View File

@ -99,23 +99,30 @@ composer_autosave_state_free (AutosaveState *state)
}
static gboolean
composer_autosave_state_open (AutosaveState *state)
composer_autosave_state_open (AutosaveState *state,
GError **error)
{
const gchar *user_data_dir;
gchar *path;
gint fd;
if (state->file != NULL)
return TRUE;
path = g_build_filename (
e_get_user_data_dir (), AUTOSAVE_SEED, NULL);
user_data_dir = e_get_user_data_dir ();
path = g_build_filename (user_data_dir, AUTOSAVE_SEED, NULL);
/* Since GIO doesn't have support for creating temporary files
* from a template (and in a given directory), we have to use
* g_mkstemp(), which brings a small risk of overwriting another
* autosave file. The risk is, however, miniscule. */
errno = 0;
fd = g_mkstemp (path);
if (fd == -1) {
g_set_error (
error, G_FILE_ERROR,
g_file_error_from_errno (errno),
"%s", g_strerror (errno));
g_free (path);
return FALSE;
}
@ -317,72 +324,31 @@ e_composer_autosave_unregister (EMsgComposer *composer)
g_object_set_data (G_OBJECT (composer), "autosave", NULL);
}
typedef struct {
EMsgComposer *composer;
GSimpleAsyncResult *simple;
AutosaveState *state;
/* Transient data */
GInputStream *input_stream;
} AutosaveData;
static void
autosave_data_free (AutosaveData *data)
{
g_object_unref (data->composer);
g_object_unref (data->simple);
if (data->input_stream != NULL)
g_object_unref (data->input_stream);
g_slice_free (AutosaveData, data);
}
static gboolean
autosave_snapshot_check_for_error (AutosaveData *data,
GError *error)
{
GSimpleAsyncResult *simple;
if (error == NULL)
return FALSE;
simple = data->simple;
g_simple_async_result_set_from_error (simple, error);
g_simple_async_result_set_op_res_gboolean (simple, FALSE);
g_simple_async_result_complete (simple);
g_error_free (error);
autosave_data_free (data);
return TRUE;
}
static void
autosave_snapshot_splice_cb (GOutputStream *output_stream,
GAsyncResult *result,
AutosaveData *data)
GSimpleAsyncResult *simple)
{
GSimpleAsyncResult *simple;
GError *error = NULL;
g_output_stream_splice_finish (output_stream, result, &error);
if (autosave_snapshot_check_for_error (data, error))
return;
if (error != NULL) {
g_simple_async_result_set_from_error (simple, error);
g_error_free (error);
}
simple = data->simple;
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
autosave_data_free (data);
g_object_unref (simple);
}
static void
autosave_snapshot_cb (GFile *file,
GAsyncResult *result,
AutosaveData *data)
GSimpleAsyncResult *simple)
{
GObject *object;
EMsgComposer *composer;
CamelMimeMessage *message;
GFileOutputStream *output_stream;
GInputStream *input_stream;
@ -390,22 +356,26 @@ autosave_snapshot_cb (GFile *file,
GByteArray *buffer;
GError *error = NULL;
object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
output_stream = g_file_replace_finish (file, result, &error);
if (autosave_snapshot_check_for_error (data, error))
if (error != NULL) {
g_simple_async_result_set_from_error (simple, error);
g_simple_async_result_complete (simple);
g_object_unref (simple);
g_error_free (error);
return;
}
/* Extract a MIME message from the composer. */
message = e_msg_composer_get_message_draft (data->composer);
if (message == NULL) {
GSimpleAsyncResult *simple;
/* FIXME Need to set a GError here. */
simple = data->simple;
g_simple_async_result_set_op_res_gboolean (simple, FALSE);
composer = E_MSG_COMPOSER (object);
message = e_msg_composer_get_message_draft (composer, &error);
if (error != NULL) {
g_simple_async_result_set_from_error (simple, error);
g_simple_async_result_complete (simple);
g_object_unref (output_stream);
autosave_data_free (data);
g_object_unref (simple);
return;
}
@ -430,7 +400,6 @@ autosave_snapshot_cb (GFile *file,
buffer->data, (gssize) buffer->len,
(GDestroyNotify) g_free);
g_byte_array_free (buffer, FALSE);
data->input_stream = input_stream;
/* Splice the input and output streams */
g_output_stream_splice_async (
@ -438,9 +407,10 @@ autosave_snapshot_cb (GFile *file,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
G_PRIORITY_DEFAULT, NULL, (GAsyncReadyCallback)
autosave_snapshot_splice_cb, data);
autosave_snapshot_splice_cb, simple);
g_object_unref (output_stream);
g_object_unref (input_stream);
}
void
@ -448,48 +418,39 @@ e_composer_autosave_snapshot_async (EMsgComposer *composer,
GAsyncReadyCallback callback,
gpointer user_data)
{
AutosaveData *data;
AutosaveState *state;
GSimpleAsyncResult *simple;
GError *error = NULL;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
state = g_object_get_data (G_OBJECT (composer), "autosave");
g_return_if_fail (state != NULL);
simple = g_simple_async_result_new (
G_OBJECT (composer), callback, user_data,
e_composer_autosave_snapshot_async);
/* If the contents are unchanged, exit early. */
if (!gtkhtml_editor_get_changed (GTKHTML_EDITOR (composer))) {
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
g_simple_async_result_complete (simple);
g_object_unref (simple);
return;
}
state = g_object_get_data (G_OBJECT (composer), "autosave");
g_return_if_fail (state != NULL);
/* Open the autosave file on-demand. */
errno = 0;
if (!composer_autosave_state_open (state)) {
g_simple_async_result_set_error (
simple, G_FILE_ERROR,
g_file_error_from_errno (errno),
"%s", g_strerror (errno));
g_simple_async_result_set_op_res_gboolean (simple, FALSE);
if (!composer_autosave_state_open (state, &error)) {
g_simple_async_result_set_from_error (simple, error);
g_simple_async_result_complete (simple);
g_object_unref (simple);
return;
}
/* Overwrite the file */
data = g_slice_new (AutosaveData);
data->composer = g_object_ref (composer);
data->simple = simple;
data->state = state;
/* Overwrite the file. */
g_file_replace_async (
state->file, NULL, FALSE, G_FILE_CREATE_PRIVATE,
G_PRIORITY_DEFAULT, NULL, (GAsyncReadyCallback)
autosave_snapshot_cb, data);
autosave_snapshot_cb, simple);
}
gboolean
@ -498,17 +459,15 @@ e_composer_autosave_snapshot_finish (EMsgComposer *composer,
GError **error)
{
GSimpleAsyncResult *simple;
gboolean success;
g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
success = g_simple_async_result_get_op_res_gboolean (simple);
g_simple_async_result_propagate_error (simple, error);
return success;
/* Success is assumed in the absense of a GError. */
return !g_simple_async_result_propagate_error (simple, error);
}
gchar *

View File

@ -596,7 +596,8 @@ account_hash_algo_to_camel_hash (const gchar *hash_algo)
static CamelMimeMessage *
build_message (EMsgComposer *composer,
gboolean html_content,
gboolean save_html_object_data)
gboolean save_html_object_data,
GError **error)
{
GtkhtmlEditor *editor;
EMsgComposerPrivate *p = composer->priv;
@ -1149,20 +1150,14 @@ exception:
g_object_unref (new);
if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
e_alert_run_dialog_for_args (
(GtkWindow *) composer,
"mail-composer:no-build-message",
local_error->message, NULL);
g_error_free (local_error);
if (recipients) {
for (i=0; i<recipients->len; i++)
g_free (recipients->pdata[i]);
g_ptr_array_free (recipients, TRUE);
}
g_propagate_error (error, local_error);
return NULL;
}
@ -3781,7 +3776,8 @@ e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer,
**/
CamelMimeMessage *
e_msg_composer_get_message (EMsgComposer *composer,
gboolean save_html_object_data)
gboolean save_html_object_data,
GError **error)
{
EAttachmentView *view;
EAttachmentStore *store;
@ -3803,7 +3799,9 @@ e_msg_composer_get_message (EMsgComposer *composer,
editor = GTKHTML_EDITOR (composer);
html_content = gtkhtml_editor_get_html_mode (editor);
return build_message (composer, html_content, save_html_object_data);
return build_message (
composer, html_content,
save_html_object_data, error);
}
static gchar *
@ -3859,7 +3857,8 @@ e_msg_composer_get_message_print (EMsgComposer *composer,
editor = GTKHTML_EDITOR (composer);
html_content = gtkhtml_editor_get_html_mode (editor);
msg = build_message (composer, html_content, save_html_object_data);
msg = build_message (
composer, html_content, save_html_object_data, NULL);
if (msg == NULL)
return NULL;
@ -3871,7 +3870,8 @@ e_msg_composer_get_message_print (EMsgComposer *composer,
flags = msg_composer_get_message_print_helper (
temp_composer, html_content);
msg = build_message (temp_composer, TRUE, save_html_object_data);
msg = build_message (
temp_composer, TRUE, save_html_object_data, NULL);
if (msg != NULL)
camel_medium_set_header (
CAMEL_MEDIUM (msg), "X-Evolution-Format", flags);
@ -3883,7 +3883,8 @@ e_msg_composer_get_message_print (EMsgComposer *composer,
}
CamelMimeMessage *
e_msg_composer_get_message_draft (EMsgComposer *composer)
e_msg_composer_get_message_draft (EMsgComposer *composer,
GError **error)
{
GtkhtmlEditor *editor;
EComposerHeaderTable *table;
@ -3917,7 +3918,7 @@ e_msg_composer_get_message_draft (EMsgComposer *composer)
smime_encrypt = gtk_toggle_action_get_active (action);
gtk_toggle_action_set_active (action, FALSE);
msg = build_message (composer, TRUE, TRUE);
msg = build_message (composer, TRUE, TRUE, error);
if (msg == NULL)
return NULL;
@ -3933,9 +3934,6 @@ e_msg_composer_get_message_draft (EMsgComposer *composer)
action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
gtk_toggle_action_set_active (action, smime_encrypt);
if (msg == NULL)
return NULL;
/* Attach account info to the draft. */
account = e_composer_header_table_get_account (table);
if (account && account->name)

View File

@ -114,14 +114,16 @@ void e_msg_composer_add_inline_image_from_mime_part
CamelMimePart *part);
CamelMimeMessage *
e_msg_composer_get_message (EMsgComposer *composer,
gboolean save_html_object_data);
gboolean save_html_object_data,
GError **error);
CamelMimeMessage *
e_msg_composer_get_message_print
(EMsgComposer *composer,
gboolean save_html_object_data);
CamelMimeMessage *
e_msg_composer_get_message_draft
(EMsgComposer *composer);
(EMsgComposer *composer,
GError **error);
void e_msg_composer_show_sig_file (EMsgComposer *composer);
CamelInternetAddress *

View File

@ -299,6 +299,7 @@ composer_get_message (EMsgComposer *composer, gboolean save_html_object_data)
EComposerHeaderTable *table;
EComposerHeader *post_to_header;
GString *invalid_addrs = NULL;
GError *error = NULL;
gconf = mail_config_get_gconf_client ();
table = e_msg_composer_get_header_table (composer);
@ -469,9 +470,27 @@ composer_get_message (EMsgComposer *composer, gboolean save_html_object_data)
goto finished;
/* actually get the message now, this will sign/encrypt etc */
message = e_msg_composer_get_message (composer, save_html_object_data);
if (message == NULL)
message = e_msg_composer_get_message (
composer, save_html_object_data, &error);
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warn_if_fail (message == NULL);
g_error_free (error);
goto finished;
}
if (error != NULL) {
g_warn_if_fail (message == NULL);
e_alert_run_dialog_for_args (
GTK_WINDOW (composer),
"mail-composer:no-build-message",
error->message, NULL);
g_error_free (error);
goto finished;
}
g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
/* Add info about the sending account */
account = e_composer_header_table_get_account (table);
@ -636,6 +655,7 @@ em_utils_composer_save_draft_cb (EMsgComposer *composer)
CamelMimeMessage *msg;
CamelMessageInfo *info;
EAccount *account;
GError *error = NULL;
/* need to get stuff from the composer here, since it could
* get destroyed while we're in mail_msg_wait() a little lower
@ -646,13 +666,32 @@ em_utils_composer_save_draft_cb (EMsgComposer *composer)
local_drafts_folder_uri =
e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS);
g_object_ref (composer);
msg = e_msg_composer_get_message_draft (composer);
msg = e_msg_composer_get_message_draft (composer, &error);
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warn_if_fail (msg == NULL);
g_error_free (error);
return;
}
if (error != NULL) {
g_warn_if_fail (msg == NULL);
e_alert_run_dialog_for_args (
GTK_WINDOW (composer),
"mail-composer:no-build-message",
error->message, NULL);
g_error_free (error);
return;
}
g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg));
table = e_msg_composer_get_header_table (composer);
account = e_composer_header_table_get_account (table);
sdi = g_malloc (sizeof(struct _save_draft_info));
sdi->composer = composer;
sdi->composer = g_object_ref (composer);
sdi->emcs = g_object_get_data (G_OBJECT (composer), "emcs");
if (sdi->emcs)
emcs_ref (sdi->emcs);

View File

@ -38,6 +38,7 @@
#include <mail/mail-session.h>
#include <mail/mail-ops.h>
#include <mail/message-list.h>
#include <e-util/e-alert-dialog.h>
#include <e-util/e-plugin.h>
#include <e-util/e-util.h>
#include <shell/e-shell-view.h>
@ -670,14 +671,37 @@ action_template_cb (GtkAction *action,
CamelMessageInfo *info;
CamelMimeMessage *msg;
CamelFolder *folder;
GError *error = NULL;
/* Get the templates folder and all UIDs of the messages there. */
folder = e_mail_local_get_folder (E_MAIL_FOLDER_TEMPLATES);
msg = e_msg_composer_get_message_draft (composer);
msg = e_msg_composer_get_message_draft (composer, &error);
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warn_if_fail (msg == NULL);
g_error_free (error);
return;
}
if (error != NULL) {
g_warn_if_fail (msg == NULL);
e_alert_run_dialog_for_args (
GTK_WINDOW (composer),
"mail-composer:no-build-message",
error->message, NULL);
g_error_free (error);
return;
}
g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg));
info = camel_message_info_new (NULL);
/* FIXME: what's the ~0 for? :) */
/* The last argument is a bit mask which tells the function
* which flags to modify. In this case, ~0 means all flags.
* So it clears all the flags and then sets SEEN and DRAFT. */
camel_message_info_set_flags (
info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT, ~0);