/*
* 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 <http://www.gnu.org/licenses/>
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib/gi18n-lib.h>
#include <libebackend/libebackend.h>
#include <shell/e-shell.h>
#include <libevolution-utils/e-alert-dialog.h>
#include <e-util/e-import.h>
#include <mail/e-mail-backend.h>
#include <mail/e-mail-config-assistant.h>
#include <mail/e-mail-config-welcome-page.h>
#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)
{
EShell *shell;
GtkWidget *window = NULL;
const gchar *startup_view;
gboolean express_mode;
/* Accounts should now be loaded if there were any to load.
* Check, and proceed with the Evolution Setup Assistant. */
shell = startup_wizard_get_shell (extension);
express_mode = e_shell_get_express_mode (shell);
startup_view = e_shell_get_startup_view (shell);
if (startup_wizard_have_mail_account (extension))
return;
if (express_mode && g_strcmp0 (startup_view, "mail") != 0)
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)
{
}