No issues found
1 /*
2 * evolution-startup-wizard.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <glib/gi18n-lib.h>
24 #include <libebackend/libebackend.h>
25
26 #include <shell/e-shell.h>
27
28 #include <libevolution-utils/e-alert-dialog.h>
29 #include <e-util/e-import.h>
30
31 #include <mail/e-mail-backend.h>
32 #include <mail/e-mail-config-assistant.h>
33 #include <mail/e-mail-config-welcome-page.h>
34
35 #include "e-startup-assistant.h"
36 #include "e-mail-config-import-page.h"
37 #include "e-mail-config-import-progress-page.h"
38
39 /* Standard GObject macros */
40 #define E_TYPE_STARTUP_WIZARD \
41 (e_startup_wizard_get_type ())
42 #define E_STARTUP_WIZARD(obj) \
43 (G_TYPE_CHECK_INSTANCE_CAST \
44 ((obj), E_TYPE_STARTUP_WIZARD, EStartupWizard))
45
46 typedef struct _EStartupWizard EStartupWizard;
47 typedef struct _EStartupWizardClass EStartupWizardClass;
48
49 struct _EStartupWizard {
50 EExtension parent;
51 };
52
53 struct _EStartupWizardClass {
54 EExtensionClass parent_class;
55 };
56
57 /* Module Entry Points */
58 void e_module_load (GTypeModule *type_module);
59 void e_module_unload (GTypeModule *type_module);
60
61 /* Forward Declarations */
62 GType e_startup_wizard_get_type (void);
63
64 G_DEFINE_DYNAMIC_TYPE (EStartupWizard, e_startup_wizard, E_TYPE_EXTENSION)
65
66 G_GNUC_NORETURN static void
67 startup_wizard_terminate (void)
68 {
69 gtk_main_quit ();
70 _exit (0);
71 }
72
73 static EShell *
74 startup_wizard_get_shell (EStartupWizard *extension)
75 {
76 EExtensible *extensible;
77
78 extensible = e_extension_get_extensible (E_EXTENSION (extension));
79
80 return E_SHELL (extensible);
81 }
82
83 static GtkWidget *
84 startup_wizard_new_assistant (EStartupWizard *extension)
85 {
86 EShell *shell;
87 EShellBackend *shell_backend;
88 EMailBackend *backend;
89 EMailSession *session;
90
91 shell = startup_wizard_get_shell (extension);
92 shell_backend = e_shell_get_backend_by_name (shell, "mail");
93
94 backend = E_MAIL_BACKEND (shell_backend);
95 session = e_mail_backend_get_session (backend);
96
97 /* Note: We subclass EMailConfigAssistant so we can distinguish
98 * the first-time account assistant from the normal account
99 * assistant. The backup-restore module relies on this to
100 * add a "Restore" page to the first-time assistant only. */
101 return e_startup_assistant_new (session);
102 }
103
104 static gboolean
105 startup_wizard_have_mail_account (EStartupWizard *extension)
106 {
107 EShell *shell;
108 ESource *source;
109 ESourceRegistry *registry;
110 GList *list, *link;
111 const gchar *extension_name;
112 const gchar *uid;
113 gboolean have_account;
114
115 shell = startup_wizard_get_shell (extension);
116
117 registry = e_shell_get_registry (shell);
118 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
119
120 list = e_source_registry_list_sources (registry, extension_name);
121
122 /* Exclude the built-in 'On This Computer' source. */
123 uid = E_MAIL_SESSION_LOCAL_UID;
124 source = e_source_registry_ref_source (registry, uid);
125 link = g_list_find (list, source);
126 if (link != NULL) {
127 /* We have two references to the ESource,
128 * one from e_source_registry_list_sources()
129 * and one from e_source_registry_ref_source().
130 * Drop them both. */
131 g_object_unref (source);
132 g_object_unref (source);
133 list = g_list_delete_link (list, link);
134 }
135
136 /* Exclude the built-in 'Search Folders' source. */
137 uid = E_MAIL_SESSION_VFOLDER_UID;
138 source = e_source_registry_ref_source (registry, uid);
139 link = g_list_find (list, source);
140 if (link != NULL) {
141 /* We have two references to the ESource,
142 * one from e_source_registry_list_sources()
143 * and one from e_source_registry_ref_source().
144 * Drop them both. */
145 g_object_unref (source);
146 g_object_unref (source);
147 list = g_list_delete_link (list, link);
148 }
149
150 have_account = (list != NULL);
151
152 g_list_free_full (list, (GDestroyNotify) g_object_unref);
153
154 return have_account;
155 }
156
157 static void
158 startup_wizard_weak_ref_cb (gpointer data,
159 GObject *where_the_object_was)
160 {
161 gtk_main_quit ();
162 }
163
164 static void
165 startup_wizard_run (EStartupWizard *extension)
166 {
167 EShell *shell;
168 GtkWidget *window = NULL;
169 const gchar *startup_view;
170 gboolean express_mode;
171
172 /* Accounts should now be loaded if there were any to load.
173 * Check, and proceed with the Evolution Setup Assistant. */
174
175 shell = startup_wizard_get_shell (extension);
176 express_mode = e_shell_get_express_mode (shell);
177 startup_view = e_shell_get_startup_view (shell);
178
179 if (startup_wizard_have_mail_account (extension))
180 return;
181
182 if (express_mode && g_strcmp0 (startup_view, "mail") != 0)
183 return;
184
185 if (window == NULL) {
186 window = startup_wizard_new_assistant (extension);
187 g_signal_connect (
188 window, "cancel",
189 G_CALLBACK (startup_wizard_terminate), NULL);
190 }
191
192 g_object_weak_ref (
193 G_OBJECT (window),
194 startup_wizard_weak_ref_cb, NULL);
195
196 gtk_widget_show (window);
197
198 gtk_main ();
199 }
200
201 static void
202 startup_wizard_load_accounts_done (GMainLoop *loop,
203 EActivity *activity,
204 gboolean is_last_ref)
205 {
206 /* All asynchronous account loading operations should
207 * be complete now, so we can terminate the main loop. */
208 if (is_last_ref)
209 g_main_loop_quit (loop);
210 }
211
212 static void
213 startup_wizard_load_accounts (EStartupWizard *extension)
214 {
215 EShell *shell;
216 EActivity *activity;
217 GMainContext *context;
218 GMainLoop *loop;
219 GSource *source;
220
221 /* This works similar to the offline and shutdown procedure in
222 * EShell. We broadcast a "load-accounts" EShell event with an
223 * EActivity. The EActivity has a toggle reference which we use
224 * as a counting semaphore. If another module needs to handle
225 * the event asynchronously, it should reference the EActivity
226 * until its async operation completes, then drop the reference.
227 * Once the signal handlers finish and only the toggle reference
228 * remains, we then proceed with the Evolution Setup Assistant. */
229
230 shell = startup_wizard_get_shell (extension);
231
232 /* Start a temporary main loop so asynchronous account loading
233 * operations can signal completion from an idle callback. We push
234 * our own GMainContext as the thread-default so we don't trigger
235 * other GSources that have already been attached to the current
236 * thread-default context, such as the idle callback in main.c. */
237 context = g_main_context_new ();
238 loop = g_main_loop_new (context, TRUE);
239 g_main_context_push_thread_default (context);
240
241 activity = e_activity_new ();
242 e_activity_set_text (activity, _("Loading accounts..."));
243
244 /* Drop our normal (non-toggle) EActivity reference from an
245 * idle callback. If nothing else references the EActivity
246 * then it will be a very short-lived main loop. */
247 source = g_idle_source_new ();
248 g_source_set_callback (
249 source, (GSourceFunc) gtk_false,
250 activity, (GDestroyNotify) g_object_unref);
251 g_source_attach (source, context);
252 g_source_unref (source);
253
254 /* Add a toggle reference to the EActivity which,
255 * when triggered, will terminate the main loop. */
256 g_object_add_toggle_ref (
257 G_OBJECT (activity), (GToggleNotify)
258 startup_wizard_load_accounts_done, loop);
259
260 /* Broadcast the "load-accounts" event. */
261 e_shell_event (shell, "load-accounts", activity);
262
263 /* And now we wait... */
264 g_main_loop_run (loop);
265
266 /* Increment the reference count so we can safely emit
267 * a signal without triggering the toggle reference. */
268 g_object_ref (activity);
269
270 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
271
272 g_object_remove_toggle_ref (
273 G_OBJECT (activity), (GToggleNotify)
274 startup_wizard_load_accounts_done, loop);
275
276 /* Finalize the activity. */
277 g_object_unref (activity);
278
279 /* Finalize the main loop. */
280 g_main_loop_unref (loop);
281
282 /* Pop our GMainContext off the thread-default stack. */
283 g_main_context_pop_thread_default (context);
284 g_main_context_unref (context);
285
286 /* Proceed with the Evolution Setup Assistant. */
287 startup_wizard_run (extension);
288 }
289
290 static void
291 startup_wizard_constructed (GObject *object)
292 {
293 EShell *shell;
294 EStartupWizard *extension;
295
296 extension = E_STARTUP_WIZARD (object);
297 shell = startup_wizard_get_shell (extension);
298
299 g_signal_connect_swapped (
300 shell, "event::ready-to-start",
301 G_CALLBACK (startup_wizard_load_accounts), extension);
302
303 /* Chain up to parent's constructed() method. */
304 G_OBJECT_CLASS (e_startup_wizard_parent_class)->constructed (object);
305 }
306
307 static void
308 e_startup_wizard_class_init (EStartupWizardClass *class)
309 {
310 GObjectClass *object_class;
311 EExtensionClass *extension_class;
312
313 object_class = G_OBJECT_CLASS (class);
314 object_class->constructed = startup_wizard_constructed;
315
316 extension_class = E_EXTENSION_CLASS (class);
317 extension_class->extensible_type = E_TYPE_SHELL;
318 }
319
320 static void
321 e_startup_wizard_class_finalize (EStartupWizardClass *class)
322 {
323 }
324
325 static void
326 e_startup_wizard_init (EStartupWizard *extension)
327 {
328 }
329
330 G_MODULE_EXPORT void
331 e_module_load (GTypeModule *type_module)
332 {
333 e_startup_wizard_register_type (type_module);
334 e_startup_assistant_type_register (type_module);
335 e_mail_config_import_page_type_register (type_module);
336 e_mail_config_import_progress_page_type_register (type_module);
337 }
338
339 G_MODULE_EXPORT void
340 e_module_unload (GTypeModule *type_module)
341 {
342 }