/* * evolution-startup-wizard.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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "e-startup-assistant.h" #include "e-mail-config-import-page.h" #include "e-mail-config-import-progress-page.h" /* Standard GObject macros */ #define E_TYPE_STARTUP_WIZARD \ (e_startup_wizard_get_type ()) #define E_STARTUP_WIZARD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST \ ((obj), E_TYPE_STARTUP_WIZARD, EStartupWizard)) typedef struct _EStartupWizard EStartupWizard; typedef struct _EStartupWizardClass EStartupWizardClass; struct _EStartupWizard { EExtension parent; }; struct _EStartupWizardClass { EExtensionClass parent_class; }; /* Module Entry Points */ void e_module_load (GTypeModule *type_module); void e_module_unload (GTypeModule *type_module); /* Forward Declarations */ GType e_startup_wizard_get_type (void); G_DEFINE_DYNAMIC_TYPE (EStartupWizard, e_startup_wizard, E_TYPE_EXTENSION) G_GNUC_NORETURN static void startup_wizard_terminate (void) { gtk_main_quit (); _exit (0); } static EShell * startup_wizard_get_shell (EStartupWizard *extension) { EExtensible *extensible; extensible = e_extension_get_extensible (E_EXTENSION (extension)); return E_SHELL (extensible); } static GtkWidget * startup_wizard_new_assistant (EStartupWizard *extension) { EShell *shell; EShellBackend *shell_backend; EMailBackend *backend; EMailSession *session; shell = startup_wizard_get_shell (extension); shell_backend = e_shell_get_backend_by_name (shell, "mail"); backend = E_MAIL_BACKEND (shell_backend); session = e_mail_backend_get_session (backend); /* Note: We subclass EMailConfigAssistant so we can distinguish * the first-time account assistant from the normal account * assistant. The backup-restore module relies on this to * add a "Restore" page to the first-time assistant only. */ return e_startup_assistant_new (session); } static gboolean startup_wizard_have_mail_account (EStartupWizard *extension) { EShell *shell; ESource *source; ESourceRegistry *registry; GList *list, *link; const gchar *extension_name; const gchar *uid; gboolean have_account; shell = startup_wizard_get_shell (extension); registry = e_shell_get_registry (shell); extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; list = e_source_registry_list_sources (registry, extension_name); /* Exclude the built-in 'On This Computer' source. */ uid = E_MAIL_SESSION_LOCAL_UID; source = e_source_registry_ref_source (registry, uid); link = g_list_find (list, source); if (link != NULL) { /* We have two references to the ESource, * one from e_source_registry_list_sources() * and one from e_source_registry_ref_source(). * Drop them both. */ g_object_unref (source); g_object_unref (source); list = g_list_delete_link (list, link); } /* Exclude the built-in 'Search Folders' source. */ uid = E_MAIL_SESSION_VFOLDER_UID; source = e_source_registry_ref_source (registry, uid); link = g_list_find (list, source); if (link != NULL) { /* We have two references to the ESource, * one from e_source_registry_list_sources() * and one from e_source_registry_ref_source(). * Drop them both. */ g_object_unref (source); g_object_unref (source); list = g_list_delete_link (list, link); } have_account = (list != NULL); g_list_free_full (list, (GDestroyNotify) g_object_unref); return have_account; } static void startup_wizard_weak_ref_cb (gpointer data, GObject *where_the_object_was) { gtk_main_quit (); } static void startup_wizard_run (EStartupWizard *extension) { GtkWidget *window = NULL; /* Accounts should now be loaded if there were any to load. * Check, and proceed with the Evolution Setup Assistant. */ if (startup_wizard_have_mail_account (extension)) return; if (window == NULL) { window = startup_wizard_new_assistant (extension); g_signal_connect ( window, "cancel", G_CALLBACK (startup_wizard_terminate), NULL); } g_object_weak_ref ( G_OBJECT (window), startup_wizard_weak_ref_cb, NULL); gtk_widget_show (window); gtk_main (); } static void startup_wizard_load_accounts_done (GMainLoop *loop, EActivity *activity, gboolean is_last_ref) { /* All asynchronous account loading operations should * be complete now, so we can terminate the main loop. */ if (is_last_ref) g_main_loop_quit (loop); } static void startup_wizard_load_accounts (EStartupWizard *extension) { EShell *shell; EActivity *activity; GMainContext *context; GMainLoop *loop; GSource *source; /* This works similar to the offline and shutdown procedure in * EShell. We broadcast a "load-accounts" EShell event with an * EActivity. The EActivity has a toggle reference which we use * as a counting semaphore. If another module needs to handle * the event asynchronously, it should reference the EActivity * until its async operation completes, then drop the reference. * Once the signal handlers finish and only the toggle reference * remains, we then proceed with the Evolution Setup Assistant. */ shell = startup_wizard_get_shell (extension); /* Start a temporary main loop so asynchronous account loading * operations can signal completion from an idle callback. We push * our own GMainContext as the thread-default so we don't trigger * other GSources that have already been attached to the current * thread-default context, such as the idle callback in main.c. */ context = g_main_context_new (); loop = g_main_loop_new (context, TRUE); g_main_context_push_thread_default (context); activity = e_activity_new (); e_activity_set_text (activity, _("Loading accounts...")); /* Drop our normal (non-toggle) EActivity reference from an * idle callback. If nothing else references the EActivity * then it will be a very short-lived main loop. */ source = g_idle_source_new (); g_source_set_callback ( source, (GSourceFunc) gtk_false, activity, (GDestroyNotify) g_object_unref); g_source_attach (source, context); g_source_unref (source); /* Add a toggle reference to the EActivity which, * when triggered, will terminate the main loop. */ g_object_add_toggle_ref ( G_OBJECT (activity), (GToggleNotify) startup_wizard_load_accounts_done, loop); /* Broadcast the "load-accounts" event. */ e_shell_event (shell, "load-accounts", activity); /* And now we wait... */ g_main_loop_run (loop); /* Increment the reference count so we can safely emit * a signal without triggering the toggle reference. */ g_object_ref (activity); e_activity_set_state (activity, E_ACTIVITY_COMPLETED); g_object_remove_toggle_ref ( G_OBJECT (activity), (GToggleNotify) startup_wizard_load_accounts_done, loop); /* Finalize the activity. */ g_object_unref (activity); /* Finalize the main loop. */ g_main_loop_unref (loop); /* Pop our GMainContext off the thread-default stack. */ g_main_context_pop_thread_default (context); g_main_context_unref (context); /* Proceed with the Evolution Setup Assistant. */ startup_wizard_run (extension); } static void startup_wizard_constructed (GObject *object) { EShell *shell; EStartupWizard *extension; extension = E_STARTUP_WIZARD (object); shell = startup_wizard_get_shell (extension); g_signal_connect_swapped ( shell, "event::ready-to-start", G_CALLBACK (startup_wizard_load_accounts), extension); /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_startup_wizard_parent_class)->constructed (object); } static void e_startup_wizard_class_init (EStartupWizardClass *class) { GObjectClass *object_class; EExtensionClass *extension_class; object_class = G_OBJECT_CLASS (class); object_class->constructed = startup_wizard_constructed; extension_class = E_EXTENSION_CLASS (class); extension_class->extensible_type = E_TYPE_SHELL; } static void e_startup_wizard_class_finalize (EStartupWizardClass *class) { } static void e_startup_wizard_init (EStartupWizard *extension) { } G_MODULE_EXPORT void e_module_load (GTypeModule *type_module) { e_startup_wizard_register_type (type_module); e_startup_assistant_type_register (type_module); e_mail_config_import_page_type_register (type_module); e_mail_config_import_progress_page_type_register (type_module); } G_MODULE_EXPORT void e_module_unload (GTypeModule *type_module) { }