/* * e-mail-signature-editor.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. * * 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 General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . * */ #include "e-mail-signature-editor.h" #include #include #include "e-alert-bar.h" #include "e-alert-dialog.h" #include "e-alert-sink.h" #include "e-web-view-gtkhtml.h" #define E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_SIGNATURE_EDITOR, EMailSignatureEditorPrivate)) typedef struct _AsyncContext AsyncContext; struct _EMailSignatureEditorPrivate { GtkActionGroup *action_group; EFocusTracker *focus_tracker; GCancellable *cancellable; ESourceRegistry *registry; ESource *source; gchar *original_name; GtkWidget *entry; /* not referenced */ GtkWidget *alert_bar; /* not referenced */ }; struct _AsyncContext { ESource *source; GCancellable *cancellable; gchar *contents; gsize length; }; enum { PROP_0, PROP_FOCUS_TRACKER, PROP_REGISTRY, PROP_SOURCE }; static const gchar *ui = "\n" " \n" " \n" " \n" " \n" " " " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; /* Forward Declarations */ static void e_mail_signature_editor_alert_sink_init (EAlertSinkInterface *interface); G_DEFINE_TYPE_WITH_CODE ( EMailSignatureEditor, e_mail_signature_editor, GTKHTML_TYPE_EDITOR, G_IMPLEMENT_INTERFACE ( E_TYPE_ALERT_SINK, e_mail_signature_editor_alert_sink_init)) static void async_context_free (AsyncContext *async_context) { if (async_context->source != NULL) g_object_unref (async_context->source); if (async_context->cancellable != NULL) g_object_unref (async_context->cancellable); g_free (async_context->contents); g_slice_free (AsyncContext, async_context); } static void mail_signature_editor_loaded_cb (GObject *object, GAsyncResult *result, gpointer user_data) { ESource *source; EMailSignatureEditor *editor; ESourceMailSignature *extension; const gchar *extension_name; const gchar *mime_type; gchar *contents = NULL; gboolean is_html; GError *error = NULL; source = E_SOURCE (object); editor = E_MAIL_SIGNATURE_EDITOR (user_data); e_source_mail_signature_load_finish ( source, result, &contents, NULL, &error); /* Ignore cancellations. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warn_if_fail (contents == NULL); g_object_unref (editor); g_error_free (error); return; } else if (error != NULL) { g_warn_if_fail (contents == NULL); e_alert_submit ( E_ALERT_SINK (editor), "widgets:no-load-signature", error->message, NULL); g_object_unref (editor); g_error_free (error); return; } g_return_if_fail (contents != NULL); /* The load operation should have set the MIME type. */ extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; extension = e_source_get_extension (source, extension_name); mime_type = e_source_mail_signature_get_mime_type (extension); is_html = (g_strcmp0 (mime_type, "text/html") == 0); gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (editor), is_html); if (is_html) { gtkhtml_editor_insert_html ( GTKHTML_EDITOR (editor), contents); } else { gtkhtml_editor_insert_text ( GTKHTML_EDITOR (editor), contents); gtkhtml_editor_run_command ( GTKHTML_EDITOR (editor), "cursor-position-save"); gtkhtml_editor_run_command ( GTKHTML_EDITOR (editor), "select-all"); gtkhtml_editor_run_command ( GTKHTML_EDITOR (editor), "style-pre"); gtkhtml_editor_run_command ( GTKHTML_EDITOR (editor), "unselect-all"); gtkhtml_editor_run_command ( GTKHTML_EDITOR (editor), "cursor-position-restore"); } g_free (contents); g_object_unref (editor); } static gboolean mail_signature_editor_delete_event_cb (EMailSignatureEditor *editor, GdkEvent *event) { GtkActionGroup *action_group; GtkAction *action; action_group = editor->priv->action_group; action = gtk_action_group_get_action (action_group, "close"); gtk_action_activate (action); return TRUE; } static void action_close_cb (GtkAction *action, EMailSignatureEditor *editor) { gboolean something_changed = FALSE; const gchar *original_name; const gchar *signature_name; original_name = editor->priv->original_name; signature_name = gtk_entry_get_text (GTK_ENTRY (editor->priv->entry)); something_changed |= gtkhtml_editor_has_undo (GTKHTML_EDITOR (editor)); something_changed |= (strcmp (signature_name, original_name) != 0); if (something_changed) { gint response; response = e_alert_run_dialog_for_args ( GTK_WINDOW (editor), "widgets:ask-signature-changed", NULL); if (response == GTK_RESPONSE_YES) { GtkActionGroup *action_group; action_group = editor->priv->action_group; action = gtk_action_group_get_action ( action_group, "save-and-close"); gtk_action_activate (action); return; } else if (response == GTK_RESPONSE_CANCEL) return; } gtk_widget_destroy (GTK_WIDGET (editor)); } static void action_save_and_close_cb (GtkAction *action, EMailSignatureEditor *editor) { GtkEntry *entry; EAsyncClosure *closure; GAsyncResult *result; ESource *source; gchar *display_name; GError *error = NULL; entry = GTK_ENTRY (editor->priv->entry); source = e_mail_signature_editor_get_source (editor); display_name = g_strstrip (g_strdup (gtk_entry_get_text (entry))); /* Make sure the signature name is not blank. */ if (*display_name == '\0') { e_alert_submit ( E_ALERT_SINK (editor), "widgets:blank-signature", NULL); gtk_widget_grab_focus (GTK_WIDGET (entry)); g_free (display_name); return; } e_source_set_display_name (source, display_name); g_free (display_name); /* Cancel any ongoing load or save operations. */ if (editor->priv->cancellable != NULL) { g_cancellable_cancel (editor->priv->cancellable); g_object_unref (editor->priv->cancellable); } editor->priv->cancellable = g_cancellable_new (); closure = e_async_closure_new (); e_mail_signature_editor_commit ( editor, editor->priv->cancellable, e_async_closure_callback, closure); result = e_async_closure_wait (closure); e_mail_signature_editor_commit_finish (editor, result, &error); e_async_closure_free (closure); /* Ignore cancellations. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); } else if (error != NULL) { e_alert_submit ( E_ALERT_SINK (editor), "widgets:no-save-signature", error->message, NULL); g_error_free (error); /* Only destroy the editor if the save was successful. */ } else { ESourceRegistry *registry; registry = e_mail_signature_editor_get_registry (editor); /* Only make sure that the 'source-changed' is called, * thus the preview of the signature is updated on save. * It is not called when only signature body is changed * (and ESource properties are left unchanged). */ g_signal_emit_by_name (registry, "source-changed", source); gtk_widget_destroy (GTK_WIDGET (editor)); } } static GtkActionEntry entries[] = { { "close", GTK_STOCK_CLOSE, N_("_Close"), "w", N_("Close"), G_CALLBACK (action_close_cb) }, { "save-and-close", GTK_STOCK_SAVE, N_("_Save and Close"), "Return", N_("Save and Close"), G_CALLBACK (action_save_and_close_cb) }, { "file-menu", NULL, N_("_File"), NULL, NULL, NULL } }; static void mail_signature_editor_set_registry (EMailSignatureEditor *editor, ESourceRegistry *registry) { g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); g_return_if_fail (editor->priv->registry == NULL); editor->priv->registry = g_object_ref (registry); } static void mail_signature_editor_set_source (EMailSignatureEditor *editor, ESource *source) { GDBusObject *dbus_object = NULL; const gchar *extension_name; GError *error = NULL; g_return_if_fail (source == NULL || E_IS_SOURCE (source)); g_return_if_fail (editor->priv->source == NULL); if (source != NULL) dbus_object = e_source_ref_dbus_object (source); /* Clone the source so we can make changes to it freely. */ editor->priv->source = e_source_new (dbus_object, NULL, &error); if (dbus_object != NULL) g_object_unref (dbus_object); /* This should rarely fail. If the file was loaded successfully * once then it should load successfully here as well, unless an * I/O error occurs. */ if (error != NULL) { g_warning ("%s: %s", G_STRFUNC, error->message); g_error_free (error); } /* Make sure the source has a mail signature extension. */ extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; e_source_get_extension (editor->priv->source, extension_name); } static void mail_signature_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_REGISTRY: mail_signature_editor_set_registry ( E_MAIL_SIGNATURE_EDITOR (object), g_value_get_object (value)); return; case PROP_SOURCE: mail_signature_editor_set_source ( E_MAIL_SIGNATURE_EDITOR (object), g_value_get_object (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_signature_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_FOCUS_TRACKER: g_value_set_object ( value, e_mail_signature_editor_get_focus_tracker ( E_MAIL_SIGNATURE_EDITOR (object))); return; case PROP_REGISTRY: g_value_set_object ( value, e_mail_signature_editor_get_registry ( E_MAIL_SIGNATURE_EDITOR (object))); return; case PROP_SOURCE: g_value_set_object ( value, e_mail_signature_editor_get_source ( E_MAIL_SIGNATURE_EDITOR (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_signature_editor_dispose (GObject *object) { EMailSignatureEditorPrivate *priv; priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (object); if (priv->action_group != NULL) { g_object_unref (priv->action_group); priv->action_group = NULL; } if (priv->focus_tracker != NULL) { g_object_unref (priv->focus_tracker); priv->focus_tracker = NULL; } if (priv->cancellable != NULL) { g_cancellable_cancel (priv->cancellable); g_object_unref (priv->cancellable); priv->cancellable = NULL; } if (priv->registry != NULL) { g_object_unref (priv->registry); priv->registry = NULL; } if (priv->source != NULL) { g_object_unref (priv->source); priv->source = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_mail_signature_editor_parent_class)-> dispose (object); } static void mail_signature_editor_finalize (GObject *object) { EMailSignatureEditorPrivate *priv; priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (object); g_free (priv->original_name); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_mail_signature_editor_parent_class)-> finalize (object); } static void mail_signature_editor_constructed (GObject *object) { EMailSignatureEditor *editor; GtkActionGroup *action_group; EFocusTracker *focus_tracker; GtkhtmlEditor *gtkhtml_editor; GtkUIManager *ui_manager; GDBusObject *dbus_object; ESource *source; GtkAction *action; GtkWidget *container; GtkWidget *widget; const gchar *display_name; GError *error = NULL; /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_mail_signature_editor_parent_class)-> constructed (object); editor = E_MAIL_SIGNATURE_EDITOR (object); gtkhtml_editor = GTKHTML_EDITOR (editor); ui_manager = gtkhtml_editor_get_ui_manager (gtkhtml_editor); /* Because we are loading from a hard-coded string, there is * no chance of I/O errors. Failure here implies a malformed * UI definition. Full stop. */ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); if (error != NULL) g_error ("%s", error->message); action_group = gtk_action_group_new ("signature"); gtk_action_group_set_translation_domain ( action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions ( action_group, entries, G_N_ELEMENTS (entries), editor); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); editor->priv->action_group = g_object_ref (action_group); /* Hide page properties because it is not inherited in the mail. */ action = gtkhtml_editor_get_action (gtkhtml_editor, "properties-page"); gtk_action_set_visible (action, FALSE); action = gtkhtml_editor_get_action ( gtkhtml_editor, "context-properties-page"); gtk_action_set_visible (action, FALSE); gtk_ui_manager_ensure_update (ui_manager); gtk_window_set_title (GTK_WINDOW (editor), _("Edit Signature")); /* Construct the signature name entry. */ container = gtkhtml_editor->vbox; widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_container_set_border_width (GTK_CONTAINER (widget), 6); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); /* Position 2 should be between the main and style toolbars. */ gtk_box_reorder_child (GTK_BOX (container), widget, 2); gtk_widget_show (widget); container = widget; widget = gtk_entry_new (); gtk_box_pack_end (GTK_BOX (container), widget, TRUE, TRUE, 0); editor->priv->entry = widget; /* not referenced */ gtk_widget_show (widget); widget = gtk_label_new_with_mnemonic (_("_Signature Name:")); gtk_label_set_mnemonic_widget (GTK_LABEL (widget), editor->priv->entry); gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0); gtk_widget_show (widget); g_signal_connect ( editor, "delete-event", G_CALLBACK (mail_signature_editor_delete_event_cb), NULL); /* Construct the alert bar for errors. */ container = gtkhtml_editor->vbox; widget = e_alert_bar_new (); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); /* Position 5 should be between the style toolbar and editing area. */ gtk_box_reorder_child (GTK_BOX (container), widget, 5); editor->priv->alert_bar = widget; /* not referenced */ /* EAlertBar controls its own visibility. */ /* Configure an EFocusTracker to manage selection actions. * * XXX GtkhtmlEditor does not manage its own selection actions, * which is technically a bug but works in our favor here * because it won't cause any conflicts with EFocusTracker. */ focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor)); action = gtkhtml_editor_get_action (gtkhtml_editor, "cut"); e_focus_tracker_set_cut_clipboard_action (focus_tracker, action); action = gtkhtml_editor_get_action (gtkhtml_editor, "copy"); e_focus_tracker_set_copy_clipboard_action (focus_tracker, action); action = gtkhtml_editor_get_action (gtkhtml_editor, "paste"); e_focus_tracker_set_paste_clipboard_action (focus_tracker, action); action = gtkhtml_editor_get_action (gtkhtml_editor, "select-all"); e_focus_tracker_set_select_all_action (focus_tracker, action); editor->priv->focus_tracker = focus_tracker; source = e_mail_signature_editor_get_source (editor); display_name = e_source_get_display_name (source); if (display_name == NULL || *display_name == '\0') display_name = _("Unnamed"); /* Set the entry text before we grab focus. */ g_free (editor->priv->original_name); editor->priv->original_name = g_strdup (display_name); gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), display_name); /* Set the focus appropriately. If this is a new signature, draw * the user's attention to the signature name entry. Otherwise go * straight to the editing area. */ if (source == NULL) { gtk_widget_grab_focus (editor->priv->entry); } else { GtkHTML *html; html = gtkhtml_editor_get_html (gtkhtml_editor); gtk_widget_grab_focus (GTK_WIDGET (html)); } /* Load file content only for an existing signature. * (A new signature will not yet have a GDBusObject.) */ dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { GCancellable *cancellable; cancellable = g_cancellable_new (); e_source_mail_signature_load ( source, G_PRIORITY_DEFAULT, cancellable, mail_signature_editor_loaded_cb, g_object_ref (editor)); g_warn_if_fail (editor->priv->cancellable == NULL); editor->priv->cancellable = cancellable; g_object_unref (dbus_object); } } static void mail_signature_editor_cut_clipboard (GtkhtmlEditor *editor) { /* Do nothing. EFocusTracker handles this. */ } static void mail_signature_editor_copy_clipboard (GtkhtmlEditor *editor) { /* Do nothing. EFocusTracker handles this. */ } static void mail_signature_editor_paste_clipboard (GtkhtmlEditor *editor) { /* Do nothing. EFocusTracker handles this. */ } static void mail_signature_editor_select_all (GtkhtmlEditor *editor) { /* Do nothing. EFocusTracker handles this. */ } static void mail_signature_editor_submit_alert (EAlertSink *alert_sink, EAlert *alert) { EMailSignatureEditorPrivate *priv; EAlertBar *alert_bar; GtkWidget *dialog; GtkWindow *parent; priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (alert_sink); switch (e_alert_get_message_type (alert)) { case GTK_MESSAGE_INFO: case GTK_MESSAGE_WARNING: case GTK_MESSAGE_ERROR: alert_bar = E_ALERT_BAR (priv->alert_bar); e_alert_bar_add_alert (alert_bar, alert); break; default: parent = GTK_WINDOW (alert_sink); dialog = e_alert_dialog_new (parent, alert); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); break; } } static void e_mail_signature_editor_class_init (EMailSignatureEditorClass *class) { GObjectClass *object_class; GtkhtmlEditorClass *editor_class; g_type_class_add_private (class, sizeof (EMailSignatureEditorPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = mail_signature_editor_set_property; object_class->get_property = mail_signature_editor_get_property; object_class->dispose = mail_signature_editor_dispose; object_class->finalize = mail_signature_editor_finalize; object_class->constructed = mail_signature_editor_constructed; editor_class = GTKHTML_EDITOR_CLASS (class); editor_class->cut_clipboard = mail_signature_editor_cut_clipboard; editor_class->copy_clipboard = mail_signature_editor_copy_clipboard; editor_class->paste_clipboard = mail_signature_editor_paste_clipboard; editor_class->select_all = mail_signature_editor_select_all; g_object_class_install_property ( object_class, PROP_FOCUS_TRACKER, g_param_spec_object ( "focus-tracker", NULL, NULL, E_TYPE_FOCUS_TRACKER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_REGISTRY, g_param_spec_object ( "registry", "Registry", "Data source registry", E_TYPE_SOURCE_REGISTRY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_SOURCE, g_param_spec_object ( "source", NULL, NULL, E_TYPE_SOURCE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void e_mail_signature_editor_alert_sink_init (EAlertSinkInterface *interface) { interface->submit_alert = mail_signature_editor_submit_alert; } static void e_mail_signature_editor_init (EMailSignatureEditor *editor) { editor->priv = E_MAIL_SIGNATURE_EDITOR_GET_PRIVATE (editor); } GtkWidget * e_mail_signature_editor_new (ESourceRegistry *registry, ESource *source) { g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); if (source != NULL) g_return_val_if_fail (E_IS_SOURCE (source), NULL); return g_object_new ( E_TYPE_MAIL_SIGNATURE_EDITOR, "html", e_web_view_gtkhtml_new (), "registry", registry, "source", source, NULL); } EFocusTracker * e_mail_signature_editor_get_focus_tracker (EMailSignatureEditor *editor) { g_return_val_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor), NULL); return editor->priv->focus_tracker; } ESourceRegistry * e_mail_signature_editor_get_registry (EMailSignatureEditor *editor) { g_return_val_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor), NULL); return editor->priv->registry; } ESource * e_mail_signature_editor_get_source (EMailSignatureEditor *editor) { g_return_val_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor), NULL); return editor->priv->source; } /********************** e_mail_signature_editor_commit() *********************/ static void mail_signature_editor_replace_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple; GError *error = NULL; simple = G_SIMPLE_ASYNC_RESULT (user_data); e_source_mail_signature_replace_finish ( E_SOURCE (object), result, &error); if (error != NULL) g_simple_async_result_take_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); } static void mail_signature_editor_commit_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple; AsyncContext *async_context; GError *error = NULL; simple = G_SIMPLE_ASYNC_RESULT (user_data); async_context = g_simple_async_result_get_op_res_gpointer (simple); e_source_registry_commit_source_finish ( E_SOURCE_REGISTRY (object), result, &error); if (error != NULL) { g_simple_async_result_take_error (simple, error); g_simple_async_result_complete (simple); g_object_unref (simple); return; } /* We can call this on our scratch source because only its UID is * really needed, which even a new scratch source already knows. */ e_source_mail_signature_replace ( async_context->source, async_context->contents, async_context->length, G_PRIORITY_DEFAULT, async_context->cancellable, mail_signature_editor_replace_cb, simple); } void e_mail_signature_editor_commit (EMailSignatureEditor *editor, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; AsyncContext *async_context; ESourceMailSignature *extension; ESourceRegistry *registry; ESource *source; const gchar *extension_name; const gchar *mime_type; gchar *contents; gboolean is_html; gsize length; g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (editor)); registry = e_mail_signature_editor_get_registry (editor); source = e_mail_signature_editor_get_source (editor); is_html = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (editor)); if (is_html) { mime_type = "text/html"; contents = gtkhtml_editor_get_text_html ( GTKHTML_EDITOR (editor), &length); } else { mime_type = "text/plain"; contents = gtkhtml_editor_get_text_plain ( GTKHTML_EDITOR (editor), &length); } extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; extension = e_source_get_extension (source, extension_name); e_source_mail_signature_set_mime_type (extension, mime_type); async_context = g_slice_new0 (AsyncContext); async_context->source = g_object_ref (source); async_context->contents = contents; /* takes ownership */ async_context->length = length; if (G_IS_CANCELLABLE (cancellable)) async_context->cancellable = g_object_ref (cancellable); simple = g_simple_async_result_new ( G_OBJECT (editor), callback, user_data, e_mail_signature_editor_commit); g_simple_async_result_set_op_res_gpointer ( simple, async_context, (GDestroyNotify) async_context_free); e_source_registry_commit_source ( registry, source, async_context->cancellable, mail_signature_editor_commit_cb, simple); } gboolean e_mail_signature_editor_commit_finish (EMailSignatureEditor *editor, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (editor), e_mail_signature_editor_commit), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); }