/* * e-mail-signature-script-dialog.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 * */ #include "e-mail-signature-script-dialog.h" #include #include #define E_MAIL_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_SIGNATURE_SCRIPT_DIALOG, \ EMailSignatureScriptDialogPrivate)) typedef struct _AsyncContext AsyncContext; struct _EMailSignatureScriptDialogPrivate { ESourceRegistry *registry; ESource *source; GtkWidget *entry; /* not referenced */ GtkWidget *file_chooser; /* not referenced */ GtkWidget *alert; /* not referenced */ gchar *symlink_target; }; struct _AsyncContext { ESource *source; GCancellable *cancellable; gchar *symlink_target; }; enum { PROP_0, PROP_REGISTRY, PROP_SOURCE, PROP_SYMLINK_TARGET }; G_DEFINE_TYPE ( EMailSignatureScriptDialog, e_mail_signature_script_dialog, GTK_TYPE_DIALOG) 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->symlink_target); g_slice_free (AsyncContext, async_context); } static gboolean mail_signature_script_dialog_filter_cb (const GtkFileFilterInfo *filter_info) { return g_file_test (filter_info->filename, G_FILE_TEST_IS_EXECUTABLE); } static void mail_signature_script_dialog_update_status (EMailSignatureScriptDialog *dialog) { ESource *source; const gchar *display_name; const gchar *symlink_target; gboolean show_alert; gboolean sensitive; source = e_mail_signature_script_dialog_get_source (dialog); display_name = e_source_get_display_name (source); sensitive = (display_name != NULL && *display_name != '\0'); symlink_target = e_mail_signature_script_dialog_get_symlink_target (dialog); if (symlink_target != NULL) { gboolean executable; executable = g_file_test ( symlink_target, G_FILE_TEST_IS_EXECUTABLE); show_alert = !executable; sensitive &= executable; } else { sensitive = FALSE; show_alert = FALSE; } if (show_alert) gtk_widget_show (dialog->priv->alert); else gtk_widget_hide (dialog->priv->alert); gtk_dialog_set_response_sensitive ( GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive); } static void mail_signature_script_dialog_file_set_cb (GtkFileChooserButton *button, EMailSignatureScriptDialog *dialog) { ESource *source; ESourceMailSignature *extension; GtkFileChooser *file_chooser; const gchar *extension_name; gchar *filename; file_chooser = GTK_FILE_CHOOSER (button); filename = gtk_file_chooser_get_filename (file_chooser); g_free (dialog->priv->symlink_target); dialog->priv->symlink_target = filename; /* takes ownership */ /* Invalidate the saved MIME type. */ extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; source = e_mail_signature_script_dialog_get_source (dialog); extension = e_source_get_extension (source, extension_name); e_source_mail_signature_set_mime_type (extension, NULL); g_object_notify (G_OBJECT (dialog), "symlink-target"); mail_signature_script_dialog_update_status (dialog); } static void mail_signature_script_dialog_query_cb (GFile *file, GAsyncResult *result, EMailSignatureScriptDialog *dialog) { GFileInfo *file_info; const gchar *symlink_target; GError *error = NULL; file_info = g_file_query_info_finish (file, result, &error); /* Ignore cancellations. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warn_if_fail (file_info == NULL); g_object_unref (dialog); g_error_free (error); return; } else if (error != NULL) { g_warn_if_fail (file_info == NULL); g_warning ("%s", error->message); g_object_unref (dialog); g_error_free (error); return; } g_return_if_fail (G_IS_FILE_INFO (file_info)); symlink_target = g_file_info_get_symlink_target (file_info); e_mail_signature_script_dialog_set_symlink_target ( dialog, symlink_target); g_object_unref (file_info); g_object_unref (dialog); } static void mail_signature_script_dialog_set_registry (EMailSignatureScriptDialog *dialog, ESourceRegistry *registry) { g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); g_return_if_fail (dialog->priv->registry == NULL); dialog->priv->registry = g_object_ref (registry); } static void mail_signature_script_dialog_set_source (EMailSignatureScriptDialog *dialog, 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 (dialog->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. */ dialog->priv->source = e_source_new (dbus_object, NULL, &error); /* 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 (dialog->priv->source, extension_name); /* If we're editing an existing signature, query the symbolic * link target of the signature file so we can initialize the * file chooser button. Note: The asynchronous callback will * run after the dialog initialization is complete. */ if (dbus_object != NULL) { ESourceMailSignature *extension; const gchar *extension_name; GFile *file; extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; extension = e_source_get_extension (source, extension_name); file = e_source_mail_signature_get_file (extension); g_file_query_info_async ( file, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, NULL, (GAsyncReadyCallback) mail_signature_script_dialog_query_cb, g_object_ref (dialog)); g_object_unref (dbus_object); } } static void mail_signature_script_dialog_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_REGISTRY: mail_signature_script_dialog_set_registry ( E_MAIL_SIGNATURE_SCRIPT_DIALOG (object), g_value_get_object (value)); return; case PROP_SOURCE: mail_signature_script_dialog_set_source ( E_MAIL_SIGNATURE_SCRIPT_DIALOG (object), g_value_get_object (value)); return; case PROP_SYMLINK_TARGET: e_mail_signature_script_dialog_set_symlink_target ( E_MAIL_SIGNATURE_SCRIPT_DIALOG (object), g_value_get_string (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_signature_script_dialog_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_REGISTRY: g_value_set_object ( value, e_mail_signature_script_dialog_get_registry ( E_MAIL_SIGNATURE_SCRIPT_DIALOG (object))); return; case PROP_SOURCE: g_value_set_object ( value, e_mail_signature_script_dialog_get_source ( E_MAIL_SIGNATURE_SCRIPT_DIALOG (object))); return; case PROP_SYMLINK_TARGET: g_value_set_string ( value, e_mail_signature_script_dialog_get_symlink_target ( E_MAIL_SIGNATURE_SCRIPT_DIALOG (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_signature_script_dialog_dispose (GObject *object) { EMailSignatureScriptDialogPrivate *priv; priv = E_MAIL_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE (object); 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_script_dialog_parent_class)-> dispose (object); } static void mail_signature_script_dialog_finalize (GObject *object) { EMailSignatureScriptDialogPrivate *priv; priv = E_MAIL_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE (object); g_free (priv->symlink_target); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_mail_signature_script_dialog_parent_class)-> finalize (object); } static void mail_signature_script_dialog_constructed (GObject *object) { EMailSignatureScriptDialog *dialog; GtkFileFilter *filter; GtkWidget *container; GtkWidget *widget; ESource *source; const gchar *display_name; gchar *markup; /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_mail_signature_script_dialog_parent_class)-> constructed (object); dialog = E_MAIL_SIGNATURE_SCRIPT_DIALOG (object); source = e_mail_signature_script_dialog_get_source (dialog); display_name = e_source_get_display_name (source); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_dialog_add_button ( GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); gtk_dialog_add_button ( GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_OK); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); widget = gtk_table_new (4, 2, FALSE); gtk_table_set_col_spacings (GTK_TABLE (widget), 6); gtk_table_set_row_spacings (GTK_TABLE (widget), 6); gtk_table_set_row_spacing (GTK_TABLE (widget), 0, 12); gtk_container_set_border_width (GTK_CONTAINER (widget), 5); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); gtk_widget_show (widget); container = widget; widget = gtk_image_new_from_stock ( GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); gtk_table_attach ( GTK_TABLE (container), widget, 0, 1, 0, 1, 0, 0, 0, 0); gtk_widget_show (widget); widget = gtk_label_new (_( "The output of this script will be used as your\n" "signature. The name you specify will be used\n" "for display purposes only.")); gtk_table_attach ( GTK_TABLE (container), widget, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); gtk_widget_show (widget); widget = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (widget), display_name); gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE); gtk_table_attach ( GTK_TABLE (container), widget, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0); dialog->priv->entry = widget; /* not referenced */ gtk_widget_show (widget); g_object_bind_property ( widget, "text", source, "display-name", G_BINDING_DEFAULT); widget = gtk_label_new_with_mnemonic (_("_Name:")); gtk_label_set_mnemonic_widget ( GTK_LABEL (widget), dialog->priv->entry); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_table_attach ( GTK_TABLE (container), widget, 0, 1, 1, 2, GTK_FILL, 0, 0, 0); gtk_widget_show (widget); widget = gtk_file_chooser_button_new ( NULL, GTK_FILE_CHOOSER_ACTION_OPEN); gtk_table_attach ( GTK_TABLE (container), widget, 1, 2, 2, 3, GTK_FILL | GTK_EXPAND, 0, 0, 0); dialog->priv->file_chooser = widget; /* not referenced */ gtk_widget_show (widget); /* Restrict file selection to executable files. */ filter = gtk_file_filter_new (); gtk_file_filter_add_custom ( filter, GTK_FILE_FILTER_FILENAME, (GtkFileFilterFunc) mail_signature_script_dialog_filter_cb, NULL, NULL); gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (widget), filter); /* We create symbolic links to script files from the "signatures" * directory, so restrict the selection to local files only. */ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE); widget = gtk_label_new_with_mnemonic (_("S_cript:")); gtk_label_set_mnemonic_widget ( GTK_LABEL (widget), dialog->priv->file_chooser); gtk_table_attach ( GTK_TABLE (container), widget, 0, 1, 2, 3, GTK_FILL, 0, 0, 0); gtk_widget_show (widget); /* This is just a placeholder. */ widget = gtk_label_new (NULL); gtk_table_attach ( GTK_TABLE (container), widget, 0, 1, 3, 4, GTK_FILL, 0, 0, 0); gtk_widget_show (widget); widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_table_attach ( GTK_TABLE (container), widget, 1, 2, 3, 4, 0, 0, 0, 0); dialog->priv->alert = widget; /* not referenced */ gtk_widget_show (widget); container = widget; widget = gtk_image_new_from_stock ( GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_MENU); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); gtk_widget_show (widget); markup = g_markup_printf_escaped ( "%s", _("Script file must be executable.")); widget = gtk_label_new (markup); gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); gtk_widget_show (widget); g_free (markup); g_signal_connect ( dialog->priv->file_chooser, "file-set", G_CALLBACK (mail_signature_script_dialog_file_set_cb), dialog); g_signal_connect_swapped ( dialog->priv->entry, "changed", G_CALLBACK (mail_signature_script_dialog_update_status), dialog); mail_signature_script_dialog_update_status (dialog); } static void e_mail_signature_script_dialog_class_init (EMailSignatureScriptDialogClass *class) { GObjectClass *object_class; g_type_class_add_private ( class, sizeof (EMailSignatureScriptDialogPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = mail_signature_script_dialog_set_property; object_class->get_property = mail_signature_script_dialog_get_property; object_class->dispose = mail_signature_script_dialog_dispose; object_class->finalize = mail_signature_script_dialog_finalize; object_class->constructed = mail_signature_script_dialog_constructed; 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", "Source", NULL, E_TYPE_SOURCE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_SYMLINK_TARGET, g_param_spec_string ( "symlink-target", "Symlink Target", NULL, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void e_mail_signature_script_dialog_init (EMailSignatureScriptDialog *dialog) { dialog->priv = E_MAIL_SIGNATURE_SCRIPT_DIALOG_GET_PRIVATE (dialog); } GtkWidget * e_mail_signature_script_dialog_new (ESourceRegistry *registry, GtkWindow *parent, 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_SCRIPT_DIALOG, "registry", registry, "transient-for", parent, "source", source, NULL); } ESourceRegistry * e_mail_signature_script_dialog_get_registry (EMailSignatureScriptDialog *dialog) { g_return_val_if_fail ( E_IS_MAIL_SIGNATURE_SCRIPT_DIALOG (dialog), NULL); return dialog->priv->registry; } ESource * e_mail_signature_script_dialog_get_source (EMailSignatureScriptDialog *dialog) { g_return_val_if_fail ( E_IS_MAIL_SIGNATURE_SCRIPT_DIALOG (dialog), NULL); return dialog->priv->source; } const gchar * e_mail_signature_script_dialog_get_symlink_target (EMailSignatureScriptDialog *dialog) { g_return_val_if_fail ( E_IS_MAIL_SIGNATURE_SCRIPT_DIALOG (dialog), NULL); return dialog->priv->symlink_target; } void e_mail_signature_script_dialog_set_symlink_target (EMailSignatureScriptDialog *dialog, const gchar *symlink_target) { GtkFileChooser *file_chooser; g_return_if_fail (E_IS_MAIL_SIGNATURE_SCRIPT_DIALOG (dialog)); g_return_if_fail (symlink_target != NULL); g_free (dialog->priv->symlink_target); dialog->priv->symlink_target = g_strdup (symlink_target); file_chooser = GTK_FILE_CHOOSER (dialog->priv->file_chooser); gtk_file_chooser_set_filename (file_chooser, symlink_target); g_object_notify (G_OBJECT (dialog), "symlink-target"); mail_signature_script_dialog_update_status (dialog); } /****************** e_mail_signature_script_dialog_commit() ******************/ static void mail_signature_script_dialog_symlink_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple; GError *error = NULL; simple = G_SIMPLE_ASYNC_RESULT (user_data); e_source_mail_signature_symlink_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_script_dialog_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_symlink ( async_context->source, async_context->symlink_target, G_PRIORITY_DEFAULT, async_context->cancellable, mail_signature_script_dialog_symlink_cb, simple); } void e_mail_signature_script_dialog_commit (EMailSignatureScriptDialog *dialog, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; AsyncContext *async_context; ESourceRegistry *registry; ESource *source; const gchar *symlink_target; g_return_if_fail (E_IS_MAIL_SIGNATURE_SCRIPT_DIALOG (dialog)); registry = e_mail_signature_script_dialog_get_registry (dialog); source = e_mail_signature_script_dialog_get_source (dialog); symlink_target = e_mail_signature_script_dialog_get_symlink_target (dialog); async_context = g_slice_new0 (AsyncContext); async_context->source = g_object_ref (source); async_context->symlink_target = g_strdup (symlink_target); if (G_IS_CANCELLABLE (cancellable)) async_context->cancellable = g_object_ref (cancellable); simple = g_simple_async_result_new ( G_OBJECT (dialog), callback, user_data, e_mail_signature_script_dialog_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_script_dialog_commit_cb, simple); } gboolean e_mail_signature_script_dialog_commit_finish (EMailSignatureScriptDialog *dialog, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (dialog), e_mail_signature_script_dialog_commit), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); }