/* * e-mail-config-assistant.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-config-assistant.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "em-folder-tree.h" #define E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_CONFIG_ASSISTANT, EMailConfigAssistantPrivate)) /* GtkAssistant's back button label. */ #define BACK_BUTTON_LABEL N_("Go _Back") typedef struct _AutoconfigContext AutoconfigContext; struct _EMailConfigAssistantPrivate { EMailSession *session; ESource *identity_source; GPtrArray *account_sources; GPtrArray *transport_sources; EMailConfigServicePage *receiving_page; EMailConfigServicePage *sending_page; EMailConfigSummaryPage *summary_page; EMailConfigPage *lookup_page; GHashTable *visited_pages; gboolean auto_configured; /* GtkAssistant owns this. */ GtkButton *back_button; /* not referenced */ }; struct _AutoconfigContext { GtkAssistant *assistant; GCancellable *cancellable; GtkWidget *skip_button; /* not referenced */ }; enum { PROP_0, PROP_ACCOUNT_BACKEND, PROP_ACCOUNT_SOURCE, PROP_IDENTITY_SOURCE, PROP_SESSION, PROP_TRANSPORT_BACKEND, PROP_TRANSPORT_SOURCE }; /* XXX We implement EAlertSink but don't implement a custom submit_alert() * method. So any alert results in a pop-up message dialog, which is a * fashion faux pas these days. But it's only used when submitting the * the newly-configured account fails, so should rarely be seen. */ G_DEFINE_TYPE_WITH_CODE ( EMailConfigAssistant, e_mail_config_assistant, GTK_TYPE_ASSISTANT, G_IMPLEMENT_INTERFACE ( E_TYPE_ALERT_SINK, NULL) G_IMPLEMENT_INTERFACE ( E_TYPE_EXTENSIBLE, NULL)) static void autoconfig_skip_button_clicked_cb (GtkButton *button, GCancellable *cancellable) { g_cancellable_cancel (cancellable); } static AutoconfigContext * autoconfig_context_new (GtkAssistant *assistant) { AutoconfigContext *context; const gchar *text; context = g_slice_new0 (AutoconfigContext); context->assistant = g_object_ref (assistant); context->cancellable = g_cancellable_new (); /* GtkAssistant sinks the floating button reference. */ text = _("_Skip Lookup"); context->skip_button = gtk_button_new_with_mnemonic (text); gtk_assistant_add_action_widget ( context->assistant, context->skip_button); gtk_widget_show (context->skip_button); g_signal_connect_object ( context->skip_button, "clicked", G_CALLBACK (autoconfig_skip_button_clicked_cb), context->cancellable, 0); return context; } static void autoconfig_context_free (AutoconfigContext *context) { gtk_assistant_remove_action_widget ( context->assistant, context->skip_button); g_object_unref (context->assistant); g_object_unref (context->cancellable); g_slice_free (AutoconfigContext, context); } static gint mail_config_assistant_provider_compare (gconstpointer data1, gconstpointer data2) { const CamelProvider *provider1 = data1; const CamelProvider *provider2 = data2; /* The "none" provider comes first. */ if (g_strcmp0 (provider1->protocol, "none") == 0) return -1; if (g_strcmp0 (provider2->protocol, "none") == 0) return 1; /* Then sort remote providers before local providers. */ if (provider1->flags & CAMEL_PROVIDER_IS_REMOTE) { if (provider2->flags & CAMEL_PROVIDER_IS_REMOTE) return 0; else return -1; } else { if (provider2->flags & CAMEL_PROVIDER_IS_REMOTE) return 1; else return 0; } } static GList * mail_config_assistant_list_providers (void) { GList *list, *link; GQueue trash = G_QUEUE_INIT; list = camel_provider_list (TRUE); list = g_list_sort (list, mail_config_assistant_provider_compare); /* Keep only providers with a "mail" or "news" domain. */ for (link = list; link != NULL; link = g_list_next (link)) { CamelProvider *provider = link->data; gboolean mail_or_news_domain; mail_or_news_domain = (g_strcmp0 (provider->domain, "mail") == 0) || (g_strcmp0 (provider->domain, "news") == 0); if (mail_or_news_domain) continue; g_queue_push_tail (&trash, link); } while ((link = g_queue_pop_head (&trash)) != NULL) list = g_list_remove_link (list, link); return list; } static void mail_config_assistant_notify_account_backend (EMailConfigServicePage *page, GParamSpec *pspec, EMailConfigAssistant *assistant) { EMailConfigServiceBackend *backend; EMailConfigServicePage *sending_page; EMailConfigServicePageClass *page_class; CamelProvider *provider; backend = e_mail_config_service_page_get_active_backend (page); /* The Receiving Page combo box may not have an active item. */ if (backend == NULL) goto notify; /* The Sending Page may not have been created yet. */ if (assistant->priv->sending_page == NULL) goto notify; provider = e_mail_config_service_backend_get_provider (backend); /* XXX This should never fail, but the Camel macro below does * not check for NULL so better to malfunction than crash. */ g_return_if_fail (provider != NULL); sending_page = assistant->priv->sending_page; page_class = E_MAIL_CONFIG_SERVICE_PAGE_GET_CLASS (sending_page); /* The Sending Page is invisible when the CamelProvider for the * receiving type defines both a storage and transport service. * This is common in CamelProviders for groupware products like * Microsoft Exchange and Novell GroupWise. */ if (CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT (provider)) { backend = e_mail_config_service_page_lookup_backend ( sending_page, provider->protocol); gtk_widget_hide (GTK_WIDGET (sending_page)); } else { backend = e_mail_config_service_page_lookup_backend ( sending_page, page_class->default_backend_name); gtk_widget_show (GTK_WIDGET (sending_page)); } e_mail_config_service_page_set_active_backend (sending_page, backend); notify: g_object_freeze_notify (G_OBJECT (assistant)); g_object_notify (G_OBJECT (assistant), "account-backend"); g_object_notify (G_OBJECT (assistant), "account-source"); g_object_thaw_notify (G_OBJECT (assistant)); } static void mail_config_assistant_notify_transport_backend (EMailConfigServicePage *page, GParamSpec *pspec, EMailConfigAssistant *assistant) { g_object_freeze_notify (G_OBJECT (assistant)); g_object_notify (G_OBJECT (assistant), "transport-backend"); g_object_notify (G_OBJECT (assistant), "transport-source"); g_object_thaw_notify (G_OBJECT (assistant)); } static void mail_config_assistant_page_changed (EMailConfigPage *page, EMailConfigAssistant *assistant) { gtk_assistant_set_page_complete ( GTK_ASSISTANT (assistant), GTK_WIDGET (page), e_mail_config_page_check_complete (page)); } static void mail_config_assistant_autoconfigure_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { EMailConfigAssistantPrivate *priv; AutoconfigContext *context; EMailAutoconfig *autoconfig; const gchar *email_address; gint n_pages, ii; GError *error = NULL; context = (AutoconfigContext *) user_data; priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (context->assistant); autoconfig = e_mail_autoconfig_finish (result, &error); /* We don't really care about errors, we only capture the GError * as a debugging aid. If this doesn't work we simply proceed to * the Receiving Email page. */ if (error != NULL) { gtk_assistant_next_page (context->assistant); g_error_free (error); goto exit; } g_return_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig)); /* Autoconfiguration worked! Feed the results to the * service pages and then skip to the Summary page. */ /* For the summary page... */ priv->auto_configured = TRUE; e_mail_config_service_page_auto_configure ( priv->receiving_page, autoconfig); e_mail_config_service_page_auto_configure ( priv->sending_page, autoconfig); /* Add these pages to the visited pages hash table to * prevent calling e_mail_config_page_setup_defaults(). */ g_hash_table_add (priv->visited_pages, priv->receiving_page); g_hash_table_add (priv->visited_pages, priv->sending_page); /* Also set the initial display name to the email address * given so the user can just click past the Summary page. */ email_address = e_mail_autoconfig_get_email_address (autoconfig); e_source_set_display_name (priv->identity_source, email_address); /* Go to the next page (Receiving Email) before skipping to the * Summary Page to get it into GtkAssistant visited page history. * We want the back button to return to Receiving Email. */ gtk_assistant_next_page (context->assistant); /* XXX Can't find a better way to learn the page number of * the summary page. Oh my god this API is horrible. */ n_pages = gtk_assistant_get_n_pages (context->assistant); for (ii = 0; ii < n_pages; ii++) { GtkWidget *page; page = gtk_assistant_get_nth_page (context->assistant, ii); if (E_IS_MAIL_CONFIG_SUMMARY_PAGE (page)) break; } g_warn_if_fail (ii < n_pages); gtk_assistant_set_current_page (context->assistant, ii); exit: /* Set the page invisible so we never revisit it. */ gtk_widget_set_visible (GTK_WIDGET (priv->lookup_page), FALSE); autoconfig_context_free (context); } static gboolean mail_config_assistant_provider_page_visible (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer unused) { EMailConfigServiceBackend *active_backend; EMailConfigServiceBackend *page_backend; EMailConfigProviderPage *page; GObject *target_object; gboolean visible; target_object = g_binding_get_target (binding); page = E_MAIL_CONFIG_PROVIDER_PAGE (target_object); page_backend = e_mail_config_provider_page_get_backend (page); active_backend = g_value_get_object (source_value); visible = (page_backend == active_backend); g_value_set_boolean (target_value, visible); return TRUE; } static void mail_config_assistant_select_account_node (const gchar *account_uid) { EShell *shell; EShellWindow *shell_window; EShellView *shell_view; EShellSidebar *shell_sidebar; EMFolderTree *folder_tree = NULL; GtkWindow *active_window; g_return_if_fail (account_uid != NULL); shell = e_shell_get_default (); active_window = e_shell_get_active_window (shell); if (!E_IS_SHELL_WINDOW (active_window)) return; shell_window = E_SHELL_WINDOW (active_window); if (g_strcmp0 (e_shell_window_get_active_view (shell_window), "mail") != 0) return; shell_view = e_shell_window_get_shell_view (shell_window, "mail"); shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL); em_folder_tree_select_store_when_added (folder_tree, account_uid); g_object_unref (folder_tree); } static void mail_config_assistant_close_cb (GObject *object, GAsyncResult *result, gpointer user_data) { EMailConfigAssistant *assistant; GdkWindow *gdk_window; GError *error = NULL; assistant = E_MAIL_CONFIG_ASSISTANT (object); /* Set the cursor back to normal. */ gdk_window = gtk_widget_get_window (GTK_WIDGET (assistant)); gdk_window_set_cursor (gdk_window, NULL); /* Allow user interaction with window content. */ gtk_widget_set_sensitive (GTK_WIDGET (assistant), TRUE); e_mail_config_assistant_commit_finish (assistant, result, &error); /* 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 (assistant), "system:simple-error", error->message, NULL); g_error_free (error); } else { ESource *account; account = e_mail_config_assistant_get_account_source (assistant); if (account) mail_config_assistant_select_account_node (e_source_get_uid (account)); gtk_widget_destroy (GTK_WIDGET (assistant)); } } static void mail_config_assistant_find_back_button_cb (GtkWidget *widget, gpointer user_data) { EMailConfigAssistant *assistant; assistant = E_MAIL_CONFIG_ASSISTANT (user_data); if (GTK_IS_BUTTON (widget)) { GtkButton *button; const gchar *gtk_label; const gchar *our_label; button = GTK_BUTTON (widget); /* XXX The gtkassistant.ui file assigns the back button * an ID of "back", but I don't think we have access * to it from here. I guess just compare by label, * and hope our translation matches GTK's. Yuck. */ gtk_label = gtk_button_get_label (button); our_label = gettext (BACK_BUTTON_LABEL); if (g_strcmp0 (gtk_label, our_label) == 0) assistant->priv->back_button = button; } else if (GTK_IS_CONTAINER (widget)) { gtk_container_forall ( GTK_CONTAINER (widget), mail_config_assistant_find_back_button_cb, assistant); } } static void mail_config_assistant_set_session (EMailConfigAssistant *assistant, EMailSession *session) { g_return_if_fail (E_IS_MAIL_SESSION (session)); g_return_if_fail (assistant->priv->session == NULL); assistant->priv->session = g_object_ref (session); } static void mail_config_assistant_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_SESSION: mail_config_assistant_set_session ( E_MAIL_CONFIG_ASSISTANT (object), g_value_get_object (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_config_assistant_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_ACCOUNT_BACKEND: g_value_set_object ( value, e_mail_config_assistant_get_account_backend ( E_MAIL_CONFIG_ASSISTANT (object))); return; case PROP_ACCOUNT_SOURCE: g_value_set_object ( value, e_mail_config_assistant_get_account_source ( E_MAIL_CONFIG_ASSISTANT (object))); return; case PROP_IDENTITY_SOURCE: g_value_set_object ( value, e_mail_config_assistant_get_identity_source ( E_MAIL_CONFIG_ASSISTANT (object))); return; case PROP_SESSION: g_value_set_object ( value, e_mail_config_assistant_get_session ( E_MAIL_CONFIG_ASSISTANT (object))); return; case PROP_TRANSPORT_BACKEND: g_value_set_object ( value, e_mail_config_assistant_get_transport_backend ( E_MAIL_CONFIG_ASSISTANT (object))); return; case PROP_TRANSPORT_SOURCE: g_value_set_object ( value, e_mail_config_assistant_get_transport_source ( E_MAIL_CONFIG_ASSISTANT (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_config_assistant_dispose (GObject *object) { EMailConfigAssistantPrivate *priv; priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (object); if (priv->session != NULL) { g_object_unref (priv->session); priv->session = NULL; } if (priv->identity_source != NULL) { g_object_unref (priv->identity_source); priv->identity_source = NULL; } if (priv->receiving_page != NULL) { g_object_unref (priv->receiving_page); priv->receiving_page = NULL; } if (priv->sending_page != NULL) { g_object_unref (priv->sending_page); priv->sending_page = NULL; } if (priv->summary_page != NULL) { g_object_unref (priv->summary_page); priv->summary_page = NULL; } if (priv->lookup_page != NULL) { g_object_unref (priv->lookup_page); priv->lookup_page = NULL; } g_ptr_array_set_size (priv->account_sources, 0); g_ptr_array_set_size (priv->transport_sources, 0); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_mail_config_assistant_parent_class)-> dispose (object); } static void mail_config_assistant_finalize (GObject *object) { EMailConfigAssistantPrivate *priv; priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (object); g_ptr_array_free (priv->account_sources, TRUE); g_ptr_array_free (priv->transport_sources, TRUE); g_hash_table_destroy (priv->visited_pages); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_mail_config_assistant_parent_class)-> finalize (object); } static void mail_config_assistant_constructed (GObject *object) { EMailConfigAssistant *assistant; ESource *identity_source; ESourceRegistry *registry; ESourceExtension *extension; ESourceMailComposition *mail_composition_extension; ESourceMailIdentity *mail_identity_extension; ESourceMailSubmission *mail_submission_extension; EMailSession *session; EMailConfigPage *page; GList *list, *link; const gchar *extension_name; const gchar *title; assistant = E_MAIL_CONFIG_ASSISTANT (object); /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_mail_config_assistant_parent_class)-> constructed (object); title = _("Evolution Account Assistant"); gtk_window_set_title (GTK_WINDOW (assistant), title); gtk_window_set_position (GTK_WINDOW (assistant), GTK_WIN_POS_CENTER); session = e_mail_config_assistant_get_session (assistant); registry = e_mail_session_get_registry (session); /* XXX Locate the GtkAssistant's internal "Go Back" button so * we can temporarily rename it for autoconfigure results. * Walking the container like this is an extremely naughty * and brittle hack, but GtkAssistant does not provide API * to access it directly. */ gtk_container_forall ( GTK_CONTAINER (assistant), mail_config_assistant_find_back_button_cb, assistant); /* Configure a new identity source. */ identity_source = e_source_new (NULL, NULL, NULL); assistant->priv->identity_source = identity_source; session = e_mail_config_assistant_get_session (assistant); extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION; extension = e_source_get_extension (identity_source, extension_name); mail_composition_extension = E_SOURCE_MAIL_COMPOSITION (extension); extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; extension = e_source_get_extension (identity_source, extension_name); mail_identity_extension = E_SOURCE_MAIL_IDENTITY (extension); extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION; extension = e_source_get_extension (identity_source, extension_name); mail_submission_extension = E_SOURCE_MAIL_SUBMISSION (extension); e_source_mail_composition_set_drafts_folder ( mail_composition_extension, e_mail_session_get_local_folder_uri ( session, E_MAIL_LOCAL_FOLDER_DRAFTS)); e_source_mail_composition_set_templates_folder ( mail_composition_extension, e_mail_session_get_local_folder_uri ( session, E_MAIL_LOCAL_FOLDER_TEMPLATES)); e_source_mail_submission_set_sent_folder ( mail_submission_extension, e_mail_session_get_local_folder_uri ( session, E_MAIL_LOCAL_FOLDER_SENT)); /*** Welcome Page ***/ page = e_mail_config_welcome_page_new (); e_mail_config_assistant_add_page (assistant, page); /*** Identity Page ***/ page = e_mail_config_identity_page_new (registry, identity_source); e_mail_config_identity_page_set_show_account_info ( E_MAIL_CONFIG_IDENTITY_PAGE (page), FALSE); e_mail_config_identity_page_set_show_signatures ( E_MAIL_CONFIG_IDENTITY_PAGE (page), FALSE); e_mail_config_assistant_add_page (assistant, page); /*** Lookup Page ***/ page = e_mail_config_lookup_page_new (); e_mail_config_assistant_add_page (assistant, page); assistant->priv->lookup_page = g_object_ref (page); /*** Receiving Page ***/ page = e_mail_config_receiving_page_new (registry); e_mail_config_assistant_add_page (assistant, page); assistant->priv->receiving_page = g_object_ref (page); g_object_bind_property ( mail_identity_extension, "address", page, "email-address", G_BINDING_SYNC_CREATE); g_signal_connect ( page, "notify::active-backend", G_CALLBACK (mail_config_assistant_notify_account_backend), assistant); /*** Receiving Options (multiple) ***/ /* Populate the Receiving Email page while at the same time * adding a Receiving Options page for each account type. */ list = mail_config_assistant_list_providers (); for (link = list; link != NULL; link = g_list_next (link)) { EMailConfigServiceBackend *backend; CamelProvider *provider = link->data; ESourceBackend *backend_extension; ESource *scratch_source; const gchar *backend_name; if (provider->object_types[CAMEL_PROVIDER_STORE] == 0) continue; /* ESource uses "backend_name" and CamelProvider * uses "protocol", but the terms are synonymous. */ backend_name = provider->protocol; scratch_source = e_source_new (NULL, NULL, NULL); backend_extension = e_source_get_extension ( scratch_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT); e_source_backend_set_backend_name ( backend_extension, backend_name); /* Keep display names synchronized. */ g_object_bind_property ( identity_source, "display-name", scratch_source, "display-name", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); /* We always pass NULL for the collection argument. * The backend generates its own scratch collection * source if implements the new_collection() method. */ backend = e_mail_config_service_page_add_scratch_source ( assistant->priv->receiving_page, scratch_source, NULL); g_object_unref (scratch_source); page = e_mail_config_provider_page_new (backend); /* Note: We exclude this page if it has no options, * but we don't know that until we create it. */ if (e_mail_config_provider_page_is_empty ( E_MAIL_CONFIG_PROVIDER_PAGE (page))) { g_object_unref (g_object_ref_sink (page)); continue; } else { e_mail_config_assistant_add_page (assistant, page); } /* Each Receiving Options page is only visible when its * service backend is active on the Receiving Email page. */ g_object_bind_property_full ( assistant->priv->receiving_page, "active-backend", page, "visible", G_BINDING_SYNC_CREATE, mail_config_assistant_provider_page_visible, NULL, NULL, (GDestroyNotify) NULL); } g_list_free (list); /*** Sending Page ***/ page = e_mail_config_sending_page_new (registry); e_mail_config_assistant_add_page (assistant, page); assistant->priv->sending_page = g_object_ref (page); g_object_bind_property ( mail_identity_extension, "address", page, "email-address", G_BINDING_SYNC_CREATE); g_signal_connect ( page, "notify::active-backend", G_CALLBACK (mail_config_assistant_notify_transport_backend), assistant); list = mail_config_assistant_list_providers (); for (link = list; link != NULL; link = g_list_next (link)) { CamelProvider *provider = link->data; ESourceBackend *backend_extension; ESource *scratch_source; const gchar *backend_name; if (provider->object_types[CAMEL_PROVIDER_TRANSPORT] == 0) continue; /* ESource uses "backend_name" and CamelProvider * uses "protocol", but the terms are synonymous. */ backend_name = provider->protocol; scratch_source = e_source_new (NULL, NULL, NULL); backend_extension = e_source_get_extension ( scratch_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT); e_source_backend_set_backend_name ( backend_extension, backend_name); /* Keep display names synchronized. */ g_object_bind_property ( identity_source, "display-name", scratch_source, "display-name", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); /* We always pass NULL for the collection argument. * The backend generates its own scratch collection * source if implements the new_collection() method. */ e_mail_config_service_page_add_scratch_source ( assistant->priv->sending_page, scratch_source, NULL); g_object_unref (scratch_source); } g_list_free (list); /*** Summary Page ***/ page = e_mail_config_summary_page_new (); e_mail_config_assistant_add_page (assistant, page); assistant->priv->summary_page = g_object_ref (page); g_object_bind_property ( assistant, "account-backend", page, "account-backend", G_BINDING_SYNC_CREATE); g_object_bind_property ( assistant, "identity-source", page, "identity-source", G_BINDING_SYNC_CREATE); g_object_bind_property ( assistant, "transport-backend", page, "transport-backend", G_BINDING_SYNC_CREATE); /*** Confirm Page ***/ page = e_mail_config_confirm_page_new (); e_mail_config_assistant_add_page (assistant, page); e_extensible_load_extensions (E_EXTENSIBLE (assistant)); } static void mail_config_assistant_remove (GtkContainer *container, GtkWidget *widget) { if (E_IS_MAIL_CONFIG_PAGE (widget)) g_signal_handlers_disconnect_by_func ( widget, mail_config_assistant_page_changed, E_MAIL_CONFIG_ASSISTANT (container)); /* Chain up to parent's remove() method. */ GTK_CONTAINER_CLASS (e_mail_config_assistant_parent_class)-> remove (container, widget); } static void mail_config_assistant_prepare (GtkAssistant *assistant, GtkWidget *page) { EMailConfigAssistantPrivate *priv; gboolean first_visit = FALSE; priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (assistant); /* Only setup defaults the first time a page is visited. */ if (!g_hash_table_contains (priv->visited_pages, page)) { if (E_IS_MAIL_CONFIG_PAGE (page)) e_mail_config_page_setup_defaults ( E_MAIL_CONFIG_PAGE (page)); g_hash_table_add (priv->visited_pages, page); first_visit = TRUE; } /* Are we viewing autoconfiguration results? If so, temporarily * rename the back button to clarify that account details can be * revised. Otherwise reset the button to its original label. */ if (priv->back_button != NULL) { gboolean auto_configure_results; const gchar *label; auto_configure_results = E_IS_MAIL_CONFIG_SUMMARY_PAGE (page) && priv->auto_configured && first_visit; if (auto_configure_results) label = _("_Revise Details"); else label = gettext (BACK_BUTTON_LABEL); gtk_button_set_label (priv->back_button, label); } if (E_IS_MAIL_CONFIG_LOOKUP_PAGE (page)) { AutoconfigContext *context; ESource *source; ESourceRegistry *registry; ESourceMailIdentity *extension; const gchar *email_address; const gchar *extension_name; registry = e_mail_session_get_registry (priv->session); source = priv->identity_source; extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; extension = e_source_get_extension (source, extension_name); email_address = e_source_mail_identity_get_address (extension); context = autoconfig_context_new (assistant); e_mail_autoconfig_new ( registry, email_address, G_PRIORITY_DEFAULT, context->cancellable, mail_config_assistant_autoconfigure_cb, context); } if (E_IS_MAIL_CONFIG_RECEIVING_PAGE (page) && first_visit) { ESource *source; ESourceMailIdentity *extension; const gchar *email_address; const gchar *extension_name; /* Use the email address from the Identity Page as * the initial display name, so in case we have to * query a remote mail server, the password prompt * will have a more meaningful description. */ source = priv->identity_source; extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; extension = e_source_get_extension (source, extension_name); email_address = e_source_mail_identity_get_address (extension); e_source_set_display_name (source, email_address); } } static void mail_config_assistant_close (GtkAssistant *assistant) { GdkCursor *gdk_cursor; GdkWindow *gdk_window; /* Do not chain up. GtkAssistant does not implement this method. */ /* Make the cursor appear busy. */ gdk_cursor = gdk_cursor_new (GDK_WATCH); gdk_window = gtk_widget_get_window (GTK_WIDGET (assistant)); gdk_window_set_cursor (gdk_window, gdk_cursor); g_object_unref (gdk_cursor); /* Prevent user interaction with window content. */ gtk_widget_set_sensitive (GTK_WIDGET (assistant), FALSE); /* XXX This operation is not cancellable. */ e_mail_config_assistant_commit ( E_MAIL_CONFIG_ASSISTANT (assistant), NULL, mail_config_assistant_close_cb, NULL); } static void mail_config_assistant_cancel (GtkAssistant *assistant) { /* Do not chain up. GtkAssistant does not implement this method. */ gtk_widget_destroy (GTK_WIDGET (assistant)); } static void e_mail_config_assistant_class_init (EMailConfigAssistantClass *class) { GObjectClass *object_class; GtkContainerClass *container_class; GtkAssistantClass *assistant_class; g_type_class_add_private (class, sizeof (EMailConfigAssistantPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = mail_config_assistant_set_property; object_class->get_property = mail_config_assistant_get_property; object_class->dispose = mail_config_assistant_dispose; object_class->finalize = mail_config_assistant_finalize; object_class->constructed = mail_config_assistant_constructed; container_class = GTK_CONTAINER_CLASS (class); container_class->remove = mail_config_assistant_remove; assistant_class = GTK_ASSISTANT_CLASS (class); assistant_class->prepare = mail_config_assistant_prepare; assistant_class->close = mail_config_assistant_close; assistant_class->cancel = mail_config_assistant_cancel; g_object_class_install_property ( object_class, PROP_ACCOUNT_BACKEND, g_param_spec_object ( "account-backend", "Account Backend", "Active mail account service backend", E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_ACCOUNT_SOURCE, g_param_spec_object ( "account-source", "Account Source", "Mail account source being edited", E_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_IDENTITY_SOURCE, g_param_spec_object ( "identity-source", "Identity Source", "Mail identity source being edited", E_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_SESSION, g_param_spec_object ( "session", "Session", "Mail session", E_TYPE_MAIL_SESSION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_TRANSPORT_BACKEND, g_param_spec_object ( "transport-backend", "Transport Backend", "Active mail transport service backend", E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_TRANSPORT_SOURCE, g_param_spec_object ( "transport-source", "Transport Source", "Mail transport source being edited", E_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static void e_mail_config_assistant_init (EMailConfigAssistant *assistant) { assistant->priv = E_MAIL_CONFIG_ASSISTANT_GET_PRIVATE (assistant); assistant->priv->account_sources = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_object_unref); assistant->priv->transport_sources = g_ptr_array_new_with_free_func ( (GDestroyNotify) g_object_unref); assistant->priv->visited_pages = g_hash_table_new (NULL, NULL); } GtkWidget * e_mail_config_assistant_new (EMailSession *session) { g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL); return g_object_new ( E_TYPE_MAIL_CONFIG_ASSISTANT, "session", session, NULL); } EMailSession * e_mail_config_assistant_get_session (EMailConfigAssistant *assistant) { g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL); return assistant->priv->session; } EMailConfigServiceBackend * e_mail_config_assistant_get_account_backend (EMailConfigAssistant *assistant) { EMailConfigServicePage *page; g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL); page = assistant->priv->receiving_page; return e_mail_config_service_page_get_active_backend (page); } ESource * e_mail_config_assistant_get_account_source (EMailConfigAssistant *assistant) { EMailConfigServiceBackend *backend; ESource *source = NULL; g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL); backend = e_mail_config_assistant_get_account_backend (assistant); if (backend != NULL) source = e_mail_config_service_backend_get_source (backend); return source; } ESource * e_mail_config_assistant_get_identity_source (EMailConfigAssistant *assistant) { g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL); return assistant->priv->identity_source; } EMailConfigServiceBackend * e_mail_config_assistant_get_transport_backend (EMailConfigAssistant *assistant) { EMailConfigServicePage *page; g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL); page = assistant->priv->sending_page; return e_mail_config_service_page_get_active_backend (page); } ESource * e_mail_config_assistant_get_transport_source (EMailConfigAssistant *assistant) { EMailConfigServiceBackend *backend; ESource *source = NULL; g_return_val_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant), NULL); backend = e_mail_config_assistant_get_transport_backend (assistant); if (backend != NULL) source = e_mail_config_service_backend_get_source (backend); return source; } void e_mail_config_assistant_add_page (EMailConfigAssistant *assistant, EMailConfigPage *page) { EMailConfigPageInterface *page_interface; GtkAssistantPageType page_type; GtkWidget *page_widget; gint n_pages, position; const gchar *page_title; gboolean complete; g_return_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant)); g_return_if_fail (E_IS_MAIL_CONFIG_PAGE (page)); page_widget = GTK_WIDGET (page); page_interface = E_MAIL_CONFIG_PAGE_GET_INTERFACE (page); page_type = page_interface->page_type; page_title = page_interface->title; /* Determine the position to insert the page. */ n_pages = gtk_assistant_get_n_pages (GTK_ASSISTANT (assistant)); for (position = 0; position < n_pages; position++) { GtkWidget *nth_page; nth_page = gtk_assistant_get_nth_page ( GTK_ASSISTANT (assistant), position); if (e_mail_config_page_compare (page_widget, nth_page) < 0) break; } gtk_widget_show (page_widget); /* Some pages can be clicked through unchanged. */ complete = e_mail_config_page_check_complete (page); gtk_assistant_insert_page ( GTK_ASSISTANT (assistant), page_widget, position); gtk_assistant_set_page_type ( GTK_ASSISTANT (assistant), page_widget, page_type); gtk_assistant_set_page_title ( GTK_ASSISTANT (assistant), page_widget, page_title); gtk_assistant_set_page_complete ( GTK_ASSISTANT (assistant), page_widget, complete); /* XXX GtkAssistant has no equivalent to GtkNotebook's * "page-added" and "page-removed" signals. Fortunately * removing a page does trigger GtkContainer::remove, so * we can override that method and disconnect our signal * handler before chaining up. But I don't see any way * for a subclass to intercept GtkAssistant pages being * added, so we have to connect our signal handler here. * Not really an issue, I'm just being pedantic. */ g_signal_connect ( page, "changed", G_CALLBACK (mail_config_assistant_page_changed), assistant); } /********************* e_mail_config_assistant_commit() **********************/ static void mail_config_assistant_commit_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple; GError *error = NULL; simple = G_SIMPLE_ASYNC_RESULT (user_data); e_source_registry_create_sources_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); } void e_mail_config_assistant_commit (EMailConfigAssistant *assistant, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EMailConfigServiceBackend *backend; GSimpleAsyncResult *simple; ESourceRegistry *registry; EMailSession *session; ESource *source; GQueue *queue; gint n_pages, ii; g_return_if_fail (E_IS_MAIL_CONFIG_ASSISTANT (assistant)); session = e_mail_config_assistant_get_session (assistant); registry = e_mail_session_get_registry (session); queue = g_queue_new (); /* Queue the collection data source if one is defined. */ backend = e_mail_config_assistant_get_account_backend (assistant); source = e_mail_config_service_backend_get_collection (backend); if (source != NULL) g_queue_push_tail (queue, g_object_ref (source)); /* Queue the mail-related data sources for the account. */ source = e_mail_config_assistant_get_account_source (assistant); if (source != NULL) g_queue_push_tail (queue, g_object_ref (source)); source = e_mail_config_assistant_get_identity_source (assistant); if (source != NULL) g_queue_push_tail (queue, g_object_ref (source)); source = e_mail_config_assistant_get_transport_source (assistant); if (source != NULL) g_queue_push_tail (queue, g_object_ref (source)); n_pages = gtk_assistant_get_n_pages (GTK_ASSISTANT (assistant)); /* Tell all EMailConfigPages to commit their UI state to their * scratch ESources and push any additional data sources on to * the given source queue, such as calendars or address books * to be bundled with the mail account. */ for (ii = 0; ii < n_pages; ii++) { GtkWidget *widget; widget = gtk_assistant_get_nth_page ( GTK_ASSISTANT (assistant), ii); if (E_IS_MAIL_CONFIG_PAGE (widget)) { EMailConfigPage *page; page = E_MAIL_CONFIG_PAGE (widget); e_mail_config_page_commit_changes (page, queue); } } simple = g_simple_async_result_new ( G_OBJECT (assistant), callback, user_data, e_mail_config_assistant_commit); e_source_registry_create_sources ( registry, g_queue_peek_head_link (queue), cancellable, mail_config_assistant_commit_cb, simple); g_queue_free_full (queue, (GDestroyNotify) g_object_unref); } gboolean e_mail_config_assistant_commit_finish (EMailConfigAssistant *assistant, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (assistant), e_mail_config_assistant_commit), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); }