1 /*
2  * e-shell-backend.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authors:
17  *   Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
18  *
19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20  * Copyright (C) 2009 Intel Corporation
21  */
22 
23 /**
24  * SECTION: e-shell-backend
25  * @short_description: dynamically loaded capabilities
26  * @include: shell/e-shell-backend.h
27  **/
28 
29 #include "evolution-config.h"
30 
31 #include "e-shell-backend.h"
32 
33 #include <errno.h>
34 #include <glib/gi18n.h>
35 
36 #include "e-util/e-util.h"
37 
38 #include "e-shell.h"
39 #include "e-shell-view.h"
40 
41 #define E_SHELL_BACKEND_GET_PRIVATE(obj) \
42 	(G_TYPE_INSTANCE_GET_PRIVATE \
43 	((obj), E_TYPE_SHELL_BACKEND, EShellBackendPrivate))
44 
45 struct _EShellBackendPrivate {
46 
47 	/* We keep a reference to corresponding EShellView subclass
48 	 * since it keeps a reference back to us.  This ensures the
49 	 * subclass is not finalized before we are, otherwise it
50 	 * would leak its EShellBackend reference. */
51 	EShellViewClass *shell_view_class;
52 
53 	/* This tracks what the backend is busy doing. */
54 	GQueue *activities;
55 
56 	gchar *config_dir;
57 	gchar *data_dir;
58 	gchar *prefer_new_item;
59 
60 	/* This is set to delay shutdown. */
61 	gulong notify_busy_handler_id;
62 
63 	guint started : 1;
64 };
65 
66 enum {
67 	PROP_0,
68 	PROP_BUSY,
69 	PROP_PREFER_NEW_ITEM
70 };
71 
72 enum {
73 	ACTIVITY_ADDED,
74 	LAST_SIGNAL
75 };
76 
77 static guint signals[LAST_SIGNAL];
78 
G_DEFINE_ABSTRACT_TYPE(EShellBackend,e_shell_backend,E_TYPE_EXTENSION)79 G_DEFINE_ABSTRACT_TYPE (EShellBackend, e_shell_backend, E_TYPE_EXTENSION)
80 
81 static void
82 shell_backend_debug_list_activities (EShellBackend *shell_backend)
83 {
84 	EShellBackendClass *class;
85 	GList *head, *link;
86 	guint n_activities;
87 
88 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
89 	g_return_if_fail (class != NULL);
90 
91 	n_activities = g_queue_get_length (shell_backend->priv->activities);
92 
93 	if (n_activities == 0)
94 		return;
95 
96 	g_debug (
97 		"%u active '%s' %s:",
98 		n_activities, class->name,
99 		(n_activities == 1) ? "activity" : "activities");
100 
101 	head = g_queue_peek_head_link (shell_backend->priv->activities);
102 
103 	for (link = head; link != NULL; link = g_list_next (link)) {
104 		EActivity *activity = E_ACTIVITY (link->data);
105 		gchar *description;
106 		const gchar *was;
107 
108 		description = e_activity_describe (activity);
109 		was = e_activity_get_last_known_text (activity);
110 
111 		if (description != NULL)
112 			g_debug ("* %s", description);
113 		else if (was != NULL)
114 			g_debug ("* (was \"%s\")", was);
115 		else
116 			g_debug ("* (no description)");
117 
118 		g_free (description);
119 	}
120 }
121 
122 static void
shell_backend_activity_finalized_cb(EShellBackend * shell_backend,EActivity * finalized_activity)123 shell_backend_activity_finalized_cb (EShellBackend *shell_backend,
124                                      EActivity *finalized_activity)
125 {
126 	g_queue_remove (shell_backend->priv->activities, finalized_activity);
127 
128 	/* Only emit "notify::busy" when switching from busy to idle. */
129 	if (g_queue_is_empty (shell_backend->priv->activities))
130 		g_object_notify (G_OBJECT (shell_backend), "busy");
131 
132 	g_object_unref (shell_backend);
133 }
134 
135 static void
shell_backend_notify_busy_cb(EShellBackend * shell_backend,GParamSpec * pspec,EActivity * activity)136 shell_backend_notify_busy_cb (EShellBackend *shell_backend,
137                               GParamSpec *pspec,
138                               EActivity *activity)
139 {
140 	shell_backend_debug_list_activities (shell_backend);
141 
142 	if (!e_shell_backend_is_busy (shell_backend)) {
143 		/* Disconnecting this signal handler will unreference the
144 		 * EActivity and allow the shell to proceed with shutdown. */
145 		g_signal_handler_disconnect (
146 			shell_backend,
147 			shell_backend->priv->notify_busy_handler_id);
148 		shell_backend->priv->notify_busy_handler_id = 0;
149 	}
150 }
151 
152 static void
shell_backend_prepare_for_quit_cb(EShell * shell,EActivity * activity,EShellBackend * shell_backend)153 shell_backend_prepare_for_quit_cb (EShell *shell,
154                                    EActivity *activity,
155                                    EShellBackend *shell_backend)
156 {
157 	shell_backend_debug_list_activities (shell_backend);
158 
159 	if (e_shell_backend_is_busy (shell_backend)) {
160 		gulong handler_id;
161 
162 		/* Referencing the EActivity delays shutdown; the
163 		 * reference count acts like a counting semaphore. */
164 		handler_id = g_signal_connect_data (
165 			shell_backend, "notify::busy",
166 			G_CALLBACK (shell_backend_notify_busy_cb),
167 			g_object_ref (activity),
168 			(GClosureNotify) g_object_unref, 0);
169 		shell_backend->priv->notify_busy_handler_id = handler_id;
170 	}
171 }
172 
173 static GObject *
shell_backend_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)174 shell_backend_constructor (GType type,
175                            guint n_construct_properties,
176                            GObjectConstructParam *construct_properties)
177 {
178 	EShellBackend *shell_backend;
179 	EShellBackendClass *class;
180 	EShellViewClass *shell_view_class;
181 	EShell *shell;
182 	GObject *object;
183 
184 	/* Chain up to parent's construct() method. */
185 	object = G_OBJECT_CLASS (e_shell_backend_parent_class)->constructor (
186 		type, n_construct_properties, construct_properties);
187 
188 	shell_backend = E_SHELL_BACKEND (object);
189 	shell = e_shell_backend_get_shell (shell_backend);
190 
191 	/* Install a reference to ourselves in the
192 	 * corresponding EShellViewClass structure. */
193 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
194 	g_return_val_if_fail (class != NULL, object);
195 
196 	shell_view_class = g_type_class_ref (class->shell_view_type);
197 	shell_view_class->shell_backend = g_object_ref (shell_backend);
198 	shell_backend->priv->shell_view_class = shell_view_class;
199 
200 	g_signal_connect (
201 		shell, "prepare-for-quit",
202 		G_CALLBACK (shell_backend_prepare_for_quit_cb),
203 		shell_backend);
204 
205 	return object;
206 }
207 
208 static void
shell_backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)209 shell_backend_get_property (GObject *object,
210                             guint property_id,
211                             GValue *value,
212                             GParamSpec *pspec)
213 {
214 	switch (property_id) {
215 		case PROP_BUSY:
216 			g_value_set_boolean (
217 				value, e_shell_backend_is_busy (
218 				E_SHELL_BACKEND (object)));
219 			return;
220 
221 		case PROP_PREFER_NEW_ITEM:
222 			g_value_set_string (
223 				value,
224 				e_shell_backend_get_prefer_new_item (
225 				E_SHELL_BACKEND (object)));
226 			return;
227 	}
228 
229 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
230 }
231 
232 static void
shell_backend_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)233 shell_backend_set_property (GObject *object,
234                             guint property_id,
235                             const GValue *value,
236                             GParamSpec *pspec)
237 {
238 	switch (property_id) {
239 		case PROP_PREFER_NEW_ITEM:
240 			e_shell_backend_set_prefer_new_item (
241 				E_SHELL_BACKEND (object),
242 				g_value_get_string (value));
243 			return;
244 	}
245 
246 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
247 }
248 
249 static void
shell_backend_dispose(GObject * object)250 shell_backend_dispose (GObject *object)
251 {
252 	EShellBackendPrivate *priv;
253 
254 	priv = E_SHELL_BACKEND_GET_PRIVATE (object);
255 	g_clear_pointer (&priv->shell_view_class, g_type_class_unref);
256 
257 	if (priv->notify_busy_handler_id > 0) {
258 		g_signal_handler_disconnect (
259 			object, priv->notify_busy_handler_id);
260 		priv->notify_busy_handler_id = 0;
261 	}
262 
263 	/* Chain up to parent's dispose() method. */
264 	G_OBJECT_CLASS (e_shell_backend_parent_class)->dispose (object);
265 }
266 
267 static void
shell_backend_finalize(GObject * object)268 shell_backend_finalize (GObject *object)
269 {
270 	EShellBackendPrivate *priv;
271 
272 	priv = E_SHELL_BACKEND_GET_PRIVATE (object);
273 
274 	g_warn_if_fail (g_queue_is_empty (priv->activities));
275 	g_queue_free (priv->activities);
276 
277 	g_free (priv->config_dir);
278 	g_free (priv->data_dir);
279 	g_free (priv->prefer_new_item);
280 
281 	/* Chain up to parent's finalize() method. */
282 	G_OBJECT_CLASS (e_shell_backend_parent_class)->finalize (object);
283 }
284 
285 static const gchar *
shell_backend_get_config_dir(EShellBackend * shell_backend)286 shell_backend_get_config_dir (EShellBackend *shell_backend)
287 {
288 	EShellBackendClass *class;
289 
290 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
291 	g_return_val_if_fail (class != NULL, NULL);
292 
293 	/* Determine the user config directory for this backend. */
294 	if (G_UNLIKELY (shell_backend->priv->config_dir == NULL)) {
295 		const gchar *user_config_dir;
296 
297 		user_config_dir = e_get_user_config_dir ();
298 		shell_backend->priv->config_dir =
299 			g_build_filename (user_config_dir, class->name, NULL);
300 		g_mkdir_with_parents (shell_backend->priv->config_dir, 0700);
301 	}
302 
303 	return shell_backend->priv->config_dir;
304 }
305 
306 static const gchar *
shell_backend_get_data_dir(EShellBackend * shell_backend)307 shell_backend_get_data_dir (EShellBackend *shell_backend)
308 {
309 	EShellBackendClass *class;
310 
311 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
312 	g_return_val_if_fail (class != NULL, NULL);
313 
314 	/* Determine the user data directory for this backend. */
315 	if (G_UNLIKELY (shell_backend->priv->data_dir == NULL)) {
316 		const gchar *user_data_dir;
317 
318 		user_data_dir = e_get_user_data_dir ();
319 		shell_backend->priv->data_dir =
320 			g_build_filename (user_data_dir, class->name, NULL);
321 		g_mkdir_with_parents (shell_backend->priv->data_dir, 0700);
322 	}
323 
324 	return shell_backend->priv->data_dir;
325 }
326 
327 static void
e_shell_backend_class_init(EShellBackendClass * class)328 e_shell_backend_class_init (EShellBackendClass *class)
329 {
330 	GObjectClass *object_class;
331 	EExtensionClass *extension_class;
332 
333 	g_type_class_add_private (class, sizeof (EShellBackendPrivate));
334 
335 	object_class = G_OBJECT_CLASS (class);
336 	object_class->constructor = shell_backend_constructor;
337 	object_class->get_property = shell_backend_get_property;
338 	object_class->set_property = shell_backend_set_property;
339 	object_class->dispose = shell_backend_dispose;
340 	object_class->finalize = shell_backend_finalize;
341 
342 	extension_class = E_EXTENSION_CLASS (class);
343 	extension_class->extensible_type = E_TYPE_SHELL;
344 
345 	class->get_config_dir = shell_backend_get_config_dir;
346 	class->get_data_dir = shell_backend_get_data_dir;
347 
348 	/**
349 	 * EShellBackend:busy
350 	 *
351 	 * Whether any activities are still in progress.
352 	 **/
353 	g_object_class_install_property (
354 		object_class,
355 		PROP_BUSY,
356 		g_param_spec_boolean (
357 			"busy",
358 			"Busy",
359 			"Whether any activities are still in progress",
360 			FALSE,
361 			G_PARAM_READABLE |
362 			G_PARAM_STATIC_STRINGS));
363 
364 	/**
365 	 * EShellBackend:prefer-new-item
366 	 *
367 	 * Name of an item to prefer in New toolbar button; can be NULL
368 	 **/
369 	g_object_class_install_property (
370 		object_class,
371 		PROP_PREFER_NEW_ITEM,
372 		g_param_spec_string (
373 			"prefer-new-item",
374 			"Prefer New Item",
375 			"Name of an item to prefer in New toolbar button",
376 			NULL,
377 			G_PARAM_READWRITE |
378 			G_PARAM_STATIC_STRINGS));
379 
380 	/**
381 	 * EShellBackend::activity-added
382 	 * @shell_backend: the #EShellBackend that emitted the signal
383 	 * @activity: an #EActivity
384 	 *
385 	 * Broadcasts a newly added activity.
386 	 **/
387 	signals[ACTIVITY_ADDED] = g_signal_new (
388 		"activity-added",
389 		G_OBJECT_CLASS_TYPE (object_class),
390 		G_SIGNAL_RUN_LAST,
391 		0, NULL, NULL,
392 		g_cclosure_marshal_VOID__OBJECT,
393 		G_TYPE_NONE, 1,
394 		E_TYPE_ACTIVITY);
395 }
396 
397 static void
e_shell_backend_init(EShellBackend * shell_backend)398 e_shell_backend_init (EShellBackend *shell_backend)
399 {
400 	shell_backend->priv = E_SHELL_BACKEND_GET_PRIVATE (shell_backend);
401 	shell_backend->priv->activities = g_queue_new ();
402 }
403 
404 /**
405  * e_shell_backend_compare:
406  * @shell_backend_a: an #EShellBackend
407  * @shell_backend_b: an #EShellBackend
408  *
409  * Using the <structfield>sort_order</structfield> field from both backends'
410  * #EShellBackendClass, compares @shell_backend_a with @shell_mobule_b and
411  * returns -1, 0 or +1 if @shell_backend_a is found to be less than, equal
412  * to or greater than @shell_backend_b, respectively.
413  *
414  * Returns: -1, 0 or +1, for a less than, equal to or greater than result
415  **/
416 gint
e_shell_backend_compare(EShellBackend * shell_backend_a,EShellBackend * shell_backend_b)417 e_shell_backend_compare (EShellBackend *shell_backend_a,
418                          EShellBackend *shell_backend_b)
419 {
420 	EShellBackendClass *a_klass, *b_klass;
421 	gint aa, bb;
422 
423 	a_klass = E_SHELL_BACKEND_GET_CLASS (shell_backend_a);
424 	b_klass = E_SHELL_BACKEND_GET_CLASS (shell_backend_b);
425 
426 	g_return_val_if_fail (a_klass != NULL, 0);
427 	g_return_val_if_fail (b_klass != NULL, 0);
428 
429 	aa = a_klass->sort_order;
430 	bb = b_klass->sort_order;
431 
432 	return (aa < bb) ? -1 : (aa > bb) ? 1 : 0;
433 }
434 
435 /**
436  * e_shell_backend_get_config_dir:
437  * @shell_backend: an #EShellBackend
438  *
439  * Returns the absolute path to the configuration directory for
440  * @shell_backend.  The string is owned by @shell_backend and should
441  * not be modified or freed.
442  *
443  * Returns: the backend's configuration directory
444  **/
445 const gchar *
e_shell_backend_get_config_dir(EShellBackend * shell_backend)446 e_shell_backend_get_config_dir (EShellBackend *shell_backend)
447 {
448 	EShellBackendClass *class;
449 
450 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
451 
452 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
453 	g_return_val_if_fail (class != NULL, NULL);
454 	g_return_val_if_fail (class->get_config_dir != NULL, NULL);
455 
456 	return class->get_config_dir (shell_backend);
457 }
458 
459 /**
460  * e_shell_backend_get_data_dir:
461  * @shell_backend: an #EShellBackend
462  *
463  * Returns the absolute path to the data directory for @shell_backend.
464  * The string is owned by @shell_backend and should not be modified or
465  * freed.
466  *
467  * Returns: the backend's data directory
468  **/
469 const gchar *
e_shell_backend_get_data_dir(EShellBackend * shell_backend)470 e_shell_backend_get_data_dir (EShellBackend *shell_backend)
471 {
472 	EShellBackendClass *class;
473 
474 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
475 
476 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
477 	g_return_val_if_fail (class != NULL, NULL);
478 	g_return_val_if_fail (class->get_data_dir != NULL, NULL);
479 
480 	return class->get_data_dir (shell_backend);
481 }
482 
483 /**
484  * e_shell_backend_get_shell:
485  * @shell_backend: an #EShellBackend
486  *
487  * Returns the #EShell singleton.
488  *
489  * Returns: the #EShell
490  **/
491 EShell *
e_shell_backend_get_shell(EShellBackend * shell_backend)492 e_shell_backend_get_shell (EShellBackend *shell_backend)
493 {
494 	EExtensible *extensible;
495 
496 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
497 
498 	extensible = e_extension_get_extensible (E_EXTENSION (shell_backend));
499 
500 	return E_SHELL (extensible);
501 }
502 
503 /**
504  * e_shell_backend_add_activity:
505  * @shell_backend: an #EShellBackend
506  * @activity: an #EActivity
507  *
508  * Emits an #EShellBackend::activity-added signal and tracks the @activity
509  * until it is finalized.
510  **/
511 void
e_shell_backend_add_activity(EShellBackend * shell_backend,EActivity * activity)512 e_shell_backend_add_activity (EShellBackend *shell_backend,
513                               EActivity *activity)
514 {
515 	EActivityState state;
516 
517 	g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
518 	g_return_if_fail (E_IS_ACTIVITY (activity));
519 
520 	state = e_activity_get_state (activity);
521 
522 	/* Disregard cancelled or completed activities. */
523 
524 	if (state == E_ACTIVITY_CANCELLED)
525 		return;
526 
527 	if (state == E_ACTIVITY_COMPLETED)
528 		return;
529 
530 	g_queue_push_tail (shell_backend->priv->activities, activity);
531 
532 	/* Emit the "activity-added" signal before adding a weak reference
533 	 * to the EActivity because EShellTaskbar's signal handler also adds
534 	 * a weak reference to the EActivity, and we want its GWeakNotify
535 	 * to run before ours, since ours may destroy the EShellTaskbar
536 	 * during application shutdown. */
537 	g_signal_emit (shell_backend, signals[ACTIVITY_ADDED], 0, activity);
538 
539 	/* We reference the backend on every activity to
540 	 * guarantee the backend outlives the activity. */
541 	g_object_weak_ref (
542 		G_OBJECT (activity), (GWeakNotify)
543 		shell_backend_activity_finalized_cb,
544 		g_object_ref (shell_backend));
545 
546 	/* Only emit "notify::busy" when switching from idle to busy. */
547 	if (g_queue_get_length (shell_backend->priv->activities) == 1)
548 		g_object_notify (G_OBJECT (shell_backend), "busy");
549 }
550 
551 /**
552  * e_shell_backend_is_busy:
553  * @shell_backend: an #EShellBackend
554  *
555  * Returns %TRUE if any activities passed to e_shell_backend_add_activity()
556  * are still in progress, %FALSE if the @shell_backend is currently idle.
557  *
558  * Returns: %TRUE if activities are still in progress
559  **/
560 gboolean
e_shell_backend_is_busy(EShellBackend * shell_backend)561 e_shell_backend_is_busy (EShellBackend *shell_backend)
562 {
563 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE);
564 
565 	return !g_queue_is_empty (shell_backend->priv->activities);
566 }
567 
568 /**
569  * e_shell_backend_get_prefer_new_item:
570  * @shell_backend: an #EShellBackend
571  *
572  * Returns: Name of a preferred item in New toolbar button, %NULL or
573  * an empty string for no preference.
574  *
575  * Since: 3.4
576  **/
577 const gchar *
e_shell_backend_get_prefer_new_item(EShellBackend * shell_backend)578 e_shell_backend_get_prefer_new_item (EShellBackend *shell_backend)
579 {
580 	g_return_val_if_fail (shell_backend != NULL, NULL);
581 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
582 
583 	return shell_backend->priv->prefer_new_item;
584 }
585 
586 /**
587  * e_shell_backend_set_prefer_new_item:
588  * @shell_backend: an #EShellBackend
589  * @prefer_new_item: name of an item
590  *
591  * Sets name of a preferred item in New toolbar button. Use %NULL or
592  * an empty string for no preference.
593  *
594  * Since: 3.4
595  **/
596 void
e_shell_backend_set_prefer_new_item(EShellBackend * shell_backend,const gchar * prefer_new_item)597 e_shell_backend_set_prefer_new_item (EShellBackend *shell_backend,
598                                      const gchar *prefer_new_item)
599 {
600 	g_return_if_fail (shell_backend != NULL);
601 	g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
602 
603 	if (g_strcmp0 (shell_backend->priv->prefer_new_item, prefer_new_item) == 0)
604 		return;
605 
606 	g_free (shell_backend->priv->prefer_new_item);
607 	shell_backend->priv->prefer_new_item = g_strdup (prefer_new_item);
608 
609 	g_object_notify (G_OBJECT (shell_backend), "prefer-new-item");
610 }
611 
612 /**
613  * e_shell_backend_cancel_all:
614  * @shell_backend: an #EShellBackend
615  *
616  * Cancels all activities passed to e_shell_backend_add_activity() that
617  * have not already been finalized.  Note that an #EActivity can only be
618  * cancelled if it was given a #GCancellable object.
619  *
620  * Also, assuming all activities are cancellable, there may still be a
621  * delay before e_shell_backend_is_busy() returns %FALSE, because some
622  * activities may not be able to respond to the cancellation request
623  * immediately.  Connect to the "notify::busy" signal if you need
624  * notification of @shell_backend becoming idle.
625  **/
626 void
e_shell_backend_cancel_all(EShellBackend * shell_backend)627 e_shell_backend_cancel_all (EShellBackend *shell_backend)
628 {
629 	GList *list, *iter;
630 
631 	g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
632 
633 	list = g_queue_peek_head_link (shell_backend->priv->activities);
634 
635 	for (iter = list; iter != NULL; iter = g_list_next (iter))
636 		e_activity_cancel (E_ACTIVITY (iter->data));
637 }
638 
639 /**
640  * e_shell_backend_start:
641  * @shell_backend: an #EShellBackend
642  *
643  * Tells the @shell_backend to begin loading data or running background
644  * tasks which may consume significant resources.  This gets called in
645  * reponse to the user switching to the corresponding #EShellView for
646  * the first time.  The function is idempotent for each @shell_backend.
647  **/
648 void
e_shell_backend_start(EShellBackend * shell_backend)649 e_shell_backend_start (EShellBackend *shell_backend)
650 {
651 	EShellBackendClass *class;
652 
653 	g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
654 
655 	if (shell_backend->priv->started)
656 		return;
657 
658 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
659 	g_return_if_fail (class != NULL);
660 
661 	if (class->start != NULL)
662 		class->start (shell_backend);
663 
664 	shell_backend->priv->started = TRUE;
665 }
666 
667 /**
668  * e_shell_backend_is_started:
669  * @shell_backend: an #EShellBackend
670  *
671  * Returns whether e_shell_backend_start() was called for @shell_backend.
672  *
673  * Returns: whether @shell_backend is started
674  **/
675 gboolean
e_shell_backend_is_started(EShellBackend * shell_backend)676 e_shell_backend_is_started (EShellBackend *shell_backend)
677 {
678 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE);
679 
680 	return shell_backend->priv->started;
681 }
682 
683 /**
684  * e_shell_backend_migrate:
685  * @shell_backend: an #EShellBackend
686  * @major: major part of version to migrate from
687  * @minor: minor part of version to migrate from
688  * @micro: micro part of version to migrate from
689  * @error: return location for a #GError, or %NULL
690  *
691  * Attempts to migrate data and settings from version %major.%minor.%micro.
692  * Returns %TRUE if the migration was successful or if no action was
693  * necessary.  Returns %FALSE and sets %error if the migration failed.
694  *
695  * Returns: %TRUE if successful, %FALSE otherwise
696  **/
697 gboolean
e_shell_backend_migrate(EShellBackend * shell_backend,gint major,gint minor,gint micro,GError ** error)698 e_shell_backend_migrate (EShellBackend *shell_backend,
699                         gint major,
700                         gint minor,
701                         gint micro,
702                         GError **error)
703 {
704 	EShellBackendClass *class;
705 
706 	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), TRUE);
707 
708 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
709 	g_return_val_if_fail (class != NULL, TRUE);
710 
711 	if (class->migrate == NULL)
712 		return TRUE;
713 
714 	return class->migrate (shell_backend, major, minor, micro, error);
715 }
716