Update Romanian translation
[evolution.git] / src / shell / e-shell-backend.c
blobdd74c7777c73722f730d04fc89f2fef9fcc5071a
1 /*
2 * e-shell-backend.c
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.
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.
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/>.
16 * Authors:
17 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 * Copyright (C) 2009 Intel Corporation
23 /**
24 * SECTION: e-shell-backend
25 * @short_description: dynamically loaded capabilities
26 * @include: shell/e-shell-backend.h
27 **/
29 #include "evolution-config.h"
31 #include "e-shell-backend.h"
33 #include <errno.h>
34 #include <glib/gi18n.h>
36 #include "e-util/e-util.h"
38 #include "e-shell.h"
39 #include "e-shell-view.h"
41 #define E_SHELL_BACKEND_GET_PRIVATE(obj) \
42 (G_TYPE_INSTANCE_GET_PRIVATE \
43 ((obj), E_TYPE_SHELL_BACKEND, EShellBackendPrivate))
45 struct _EShellBackendPrivate {
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;
53 /* This tracks what the backend is busy doing. */
54 GQueue *activities;
56 gchar *config_dir;
57 gchar *data_dir;
58 gchar *prefer_new_item;
60 /* This is set to delay shutdown. */
61 gulong notify_busy_handler_id;
63 guint started : 1;
66 enum {
67 PROP_0,
68 PROP_BUSY,
69 PROP_PREFER_NEW_ITEM
72 enum {
73 ACTIVITY_ADDED,
74 LAST_SIGNAL
77 static guint signals[LAST_SIGNAL];
79 G_DEFINE_ABSTRACT_TYPE (EShellBackend, e_shell_backend, E_TYPE_EXTENSION)
81 static void
82 shell_backend_debug_list_activities (EShellBackend *shell_backend)
84 EShellBackendClass *class;
85 GList *head, *link;
86 guint n_activities;
88 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
89 g_return_if_fail (class != NULL);
91 n_activities = g_queue_get_length (shell_backend->priv->activities);
93 if (n_activities == 0)
94 return;
96 g_debug (
97 "%u active '%s' %s:",
98 n_activities, class->name,
99 (n_activities == 1) ? "activity" : "activities");
101 head = g_queue_peek_head_link (shell_backend->priv->activities);
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;
108 description = e_activity_describe (activity);
109 was = e_activity_get_last_known_text (activity);
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)");
118 g_free (description);
122 static void
123 shell_backend_activity_finalized_cb (EShellBackend *shell_backend,
124 EActivity *finalized_activity)
126 g_queue_remove (shell_backend->priv->activities, finalized_activity);
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");
132 g_object_unref (shell_backend);
135 static void
136 shell_backend_notify_busy_cb (EShellBackend *shell_backend,
137 GParamSpec *pspec,
138 EActivity *activity)
140 shell_backend_debug_list_activities (shell_backend);
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;
152 static void
153 shell_backend_prepare_for_quit_cb (EShell *shell,
154 EActivity *activity,
155 EShellBackend *shell_backend)
157 shell_backend_debug_list_activities (shell_backend);
159 if (e_shell_backend_is_busy (shell_backend)) {
160 gulong handler_id;
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;
173 static GObject *
174 shell_backend_constructor (GType type,
175 guint n_construct_properties,
176 GObjectConstructParam *construct_properties)
178 EShellBackend *shell_backend;
179 EShellBackendClass *class;
180 EShellViewClass *shell_view_class;
181 EShell *shell;
182 GObject *object;
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);
188 shell_backend = E_SHELL_BACKEND (object);
189 shell = e_shell_backend_get_shell (shell_backend);
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);
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;
200 g_signal_connect (
201 shell, "prepare-for-quit",
202 G_CALLBACK (shell_backend_prepare_for_quit_cb),
203 shell_backend);
205 return object;
208 static void
209 shell_backend_get_property (GObject *object,
210 guint property_id,
211 GValue *value,
212 GParamSpec *pspec)
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;
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;
229 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
232 static void
233 shell_backend_set_property (GObject *object,
234 guint property_id,
235 const GValue *value,
236 GParamSpec *pspec)
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;
246 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
249 static void
250 shell_backend_dispose (GObject *object)
252 EShellBackendPrivate *priv;
254 priv = E_SHELL_BACKEND_GET_PRIVATE (object);
256 if (priv->shell_view_class != NULL) {
257 g_type_class_unref (priv->shell_view_class);
258 priv->shell_view_class = NULL;
261 if (priv->notify_busy_handler_id > 0) {
262 g_signal_handler_disconnect (
263 object, priv->notify_busy_handler_id);
264 priv->notify_busy_handler_id = 0;
267 /* Chain up to parent's dispose() method. */
268 G_OBJECT_CLASS (e_shell_backend_parent_class)->dispose (object);
271 static void
272 shell_backend_finalize (GObject *object)
274 EShellBackendPrivate *priv;
276 priv = E_SHELL_BACKEND_GET_PRIVATE (object);
278 g_warn_if_fail (g_queue_is_empty (priv->activities));
279 g_queue_free (priv->activities);
281 g_free (priv->config_dir);
282 g_free (priv->data_dir);
283 g_free (priv->prefer_new_item);
285 /* Chain up to parent's finalize() method. */
286 G_OBJECT_CLASS (e_shell_backend_parent_class)->finalize (object);
289 static const gchar *
290 shell_backend_get_config_dir (EShellBackend *shell_backend)
292 EShellBackendClass *class;
294 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
295 g_return_val_if_fail (class != NULL, NULL);
297 /* Determine the user config directory for this backend. */
298 if (G_UNLIKELY (shell_backend->priv->config_dir == NULL)) {
299 const gchar *user_config_dir;
301 user_config_dir = e_get_user_config_dir ();
302 shell_backend->priv->config_dir =
303 g_build_filename (user_config_dir, class->name, NULL);
304 g_mkdir_with_parents (shell_backend->priv->config_dir, 0700);
307 return shell_backend->priv->config_dir;
310 static const gchar *
311 shell_backend_get_data_dir (EShellBackend *shell_backend)
313 EShellBackendClass *class;
315 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
316 g_return_val_if_fail (class != NULL, NULL);
318 /* Determine the user data directory for this backend. */
319 if (G_UNLIKELY (shell_backend->priv->data_dir == NULL)) {
320 const gchar *user_data_dir;
322 user_data_dir = e_get_user_data_dir ();
323 shell_backend->priv->data_dir =
324 g_build_filename (user_data_dir, class->name, NULL);
325 g_mkdir_with_parents (shell_backend->priv->data_dir, 0700);
328 return shell_backend->priv->data_dir;
331 static void
332 e_shell_backend_class_init (EShellBackendClass *class)
334 GObjectClass *object_class;
335 EExtensionClass *extension_class;
337 g_type_class_add_private (class, sizeof (EShellBackendPrivate));
339 object_class = G_OBJECT_CLASS (class);
340 object_class->constructor = shell_backend_constructor;
341 object_class->get_property = shell_backend_get_property;
342 object_class->set_property = shell_backend_set_property;
343 object_class->dispose = shell_backend_dispose;
344 object_class->finalize = shell_backend_finalize;
346 extension_class = E_EXTENSION_CLASS (class);
347 extension_class->extensible_type = E_TYPE_SHELL;
349 class->get_config_dir = shell_backend_get_config_dir;
350 class->get_data_dir = shell_backend_get_data_dir;
353 * EShellBackend:busy
355 * Whether any activities are still in progress.
357 g_object_class_install_property (
358 object_class,
359 PROP_BUSY,
360 g_param_spec_boolean (
361 "busy",
362 "Busy",
363 "Whether any activities are still in progress",
364 FALSE,
365 G_PARAM_READABLE |
366 G_PARAM_STATIC_STRINGS));
369 * EShellBackend:prefer-new-item
371 * Name of an item to prefer in New toolbar button; can be NULL
373 g_object_class_install_property (
374 object_class,
375 PROP_PREFER_NEW_ITEM,
376 g_param_spec_string (
377 "prefer-new-item",
378 "Prefer New Item",
379 "Name of an item to prefer in New toolbar button",
380 NULL,
381 G_PARAM_READWRITE |
382 G_PARAM_STATIC_STRINGS));
385 * EShellBackend::activity-added
386 * @shell_backend: the #EShellBackend that emitted the signal
387 * @activity: an #EActivity
389 * Broadcasts a newly added activity.
391 signals[ACTIVITY_ADDED] = g_signal_new (
392 "activity-added",
393 G_OBJECT_CLASS_TYPE (object_class),
394 G_SIGNAL_RUN_LAST,
395 0, NULL, NULL,
396 g_cclosure_marshal_VOID__OBJECT,
397 G_TYPE_NONE, 1,
398 E_TYPE_ACTIVITY);
401 static void
402 e_shell_backend_init (EShellBackend *shell_backend)
404 shell_backend->priv = E_SHELL_BACKEND_GET_PRIVATE (shell_backend);
405 shell_backend->priv->activities = g_queue_new ();
409 * e_shell_backend_compare:
410 * @shell_backend_a: an #EShellBackend
411 * @shell_backend_b: an #EShellBackend
413 * Using the <structfield>sort_order</structfield> field from both backends'
414 * #EShellBackendClass, compares @shell_backend_a with @shell_mobule_b and
415 * returns -1, 0 or +1 if @shell_backend_a is found to be less than, equal
416 * to or greater than @shell_backend_b, respectively.
418 * Returns: -1, 0 or +1, for a less than, equal to or greater than result
420 gint
421 e_shell_backend_compare (EShellBackend *shell_backend_a,
422 EShellBackend *shell_backend_b)
424 EShellBackendClass *a_klass, *b_klass;
425 gint aa, bb;
427 a_klass = E_SHELL_BACKEND_GET_CLASS (shell_backend_a);
428 b_klass = E_SHELL_BACKEND_GET_CLASS (shell_backend_b);
430 g_return_val_if_fail (a_klass != NULL, 0);
431 g_return_val_if_fail (b_klass != NULL, 0);
433 aa = a_klass->sort_order;
434 bb = b_klass->sort_order;
436 return (aa < bb) ? -1 : (aa > bb) ? 1 : 0;
440 * e_shell_backend_get_config_dir:
441 * @shell_backend: an #EShellBackend
443 * Returns the absolute path to the configuration directory for
444 * @shell_backend. The string is owned by @shell_backend and should
445 * not be modified or freed.
447 * Returns: the backend's configuration directory
449 const gchar *
450 e_shell_backend_get_config_dir (EShellBackend *shell_backend)
452 EShellBackendClass *class;
454 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
456 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
457 g_return_val_if_fail (class != NULL, NULL);
458 g_return_val_if_fail (class->get_config_dir != NULL, NULL);
460 return class->get_config_dir (shell_backend);
464 * e_shell_backend_get_data_dir:
465 * @shell_backend: an #EShellBackend
467 * Returns the absolute path to the data directory for @shell_backend.
468 * The string is owned by @shell_backend and should not be modified or
469 * freed.
471 * Returns: the backend's data directory
473 const gchar *
474 e_shell_backend_get_data_dir (EShellBackend *shell_backend)
476 EShellBackendClass *class;
478 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
480 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
481 g_return_val_if_fail (class != NULL, NULL);
482 g_return_val_if_fail (class->get_data_dir != NULL, NULL);
484 return class->get_data_dir (shell_backend);
488 * e_shell_backend_get_shell:
489 * @shell_backend: an #EShellBackend
491 * Returns the #EShell singleton.
493 * Returns: the #EShell
495 EShell *
496 e_shell_backend_get_shell (EShellBackend *shell_backend)
498 EExtensible *extensible;
500 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
502 extensible = e_extension_get_extensible (E_EXTENSION (shell_backend));
504 return E_SHELL (extensible);
508 * e_shell_backend_add_activity:
509 * @shell_backend: an #EShellBackend
510 * @activity: an #EActivity
512 * Emits an #EShellBackend::activity-added signal and tracks the @activity
513 * until it is finalized.
515 void
516 e_shell_backend_add_activity (EShellBackend *shell_backend,
517 EActivity *activity)
519 EActivityState state;
521 g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
522 g_return_if_fail (E_IS_ACTIVITY (activity));
524 state = e_activity_get_state (activity);
526 /* Disregard cancelled or completed activities. */
528 if (state == E_ACTIVITY_CANCELLED)
529 return;
531 if (state == E_ACTIVITY_COMPLETED)
532 return;
534 g_queue_push_tail (shell_backend->priv->activities, activity);
536 /* Emit the "activity-added" signal before adding a weak reference
537 * to the EActivity because EShellTaskbar's signal handler also adds
538 * a weak reference to the EActivity, and we want its GWeakNotify
539 * to run before ours, since ours may destroy the EShellTaskbar
540 * during application shutdown. */
541 g_signal_emit (shell_backend, signals[ACTIVITY_ADDED], 0, activity);
543 /* We reference the backend on every activity to
544 * guarantee the backend outlives the activity. */
545 g_object_weak_ref (
546 G_OBJECT (activity), (GWeakNotify)
547 shell_backend_activity_finalized_cb,
548 g_object_ref (shell_backend));
550 /* Only emit "notify::busy" when switching from idle to busy. */
551 if (g_queue_get_length (shell_backend->priv->activities) == 1)
552 g_object_notify (G_OBJECT (shell_backend), "busy");
556 * e_shell_backend_is_busy:
557 * @shell_backend: an #EShellBackend
559 * Returns %TRUE if any activities passed to e_shell_backend_add_activity()
560 * are still in progress, %FALSE if the @shell_backend is currently idle.
562 * Returns: %TRUE if activities are still in progress
564 gboolean
565 e_shell_backend_is_busy (EShellBackend *shell_backend)
567 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE);
569 return !g_queue_is_empty (shell_backend->priv->activities);
573 * e_shell_backend_get_prefer_new_item:
574 * @shell_backend: an #EShellBackend
576 * Returns: Name of a preferred item in New toolbar button, %NULL or
577 * an empty string for no preference.
579 * Since: 3.4
581 const gchar *
582 e_shell_backend_get_prefer_new_item (EShellBackend *shell_backend)
584 g_return_val_if_fail (shell_backend != NULL, NULL);
585 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
587 return shell_backend->priv->prefer_new_item;
591 * e_shell_backend_set_prefer_new_item:
592 * @shell_backend: an #EShellBackend
593 * @prefer_new_item: name of an item
595 * Sets name of a preferred item in New toolbar button. Use %NULL or
596 * an empty string for no preference.
598 * Since: 3.4
600 void
601 e_shell_backend_set_prefer_new_item (EShellBackend *shell_backend,
602 const gchar *prefer_new_item)
604 g_return_if_fail (shell_backend != NULL);
605 g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
607 if (g_strcmp0 (shell_backend->priv->prefer_new_item, prefer_new_item) == 0)
608 return;
610 g_free (shell_backend->priv->prefer_new_item);
611 shell_backend->priv->prefer_new_item = g_strdup (prefer_new_item);
613 g_object_notify (G_OBJECT (shell_backend), "prefer-new-item");
617 * e_shell_backend_cancel_all:
618 * @shell_backend: an #EShellBackend
620 * Cancels all activities passed to e_shell_backend_add_activity() that
621 * have not already been finalized. Note that an #EActivity can only be
622 * cancelled if it was given a #GCancellable object.
624 * Also, assuming all activities are cancellable, there may still be a
625 * delay before e_shell_backend_is_busy() returns %FALSE, because some
626 * activities may not be able to respond to the cancellation request
627 * immediately. Connect to the "notify::busy" signal if you need
628 * notification of @shell_backend becoming idle.
630 void
631 e_shell_backend_cancel_all (EShellBackend *shell_backend)
633 GList *list, *iter;
635 g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
637 list = g_queue_peek_head_link (shell_backend->priv->activities);
639 for (iter = list; iter != NULL; iter = g_list_next (iter))
640 e_activity_cancel (E_ACTIVITY (iter->data));
644 * e_shell_backend_start:
645 * @shell_backend: an #EShellBackend
647 * Tells the @shell_backend to begin loading data or running background
648 * tasks which may consume significant resources. This gets called in
649 * reponse to the user switching to the corresponding #EShellView for
650 * the first time. The function is idempotent for each @shell_backend.
652 void
653 e_shell_backend_start (EShellBackend *shell_backend)
655 EShellBackendClass *class;
657 g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
659 if (shell_backend->priv->started)
660 return;
662 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
663 g_return_if_fail (class != NULL);
665 if (class->start != NULL)
666 class->start (shell_backend);
668 shell_backend->priv->started = TRUE;
672 * e_shell_backend_is_started:
673 * @shell_backend: an #EShellBackend
675 * Returns whether e_shell_backend_start() was called for @shell_backend.
677 * Returns: whether @shell_backend is started
679 gboolean
680 e_shell_backend_is_started (EShellBackend *shell_backend)
682 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), FALSE);
684 return shell_backend->priv->started;
688 * e_shell_backend_migrate:
689 * @shell_backend: an #EShellBackend
690 * @major: major part of version to migrate from
691 * @minor: minor part of version to migrate from
692 * @micro: micro part of version to migrate from
693 * @error: return location for a #GError, or %NULL
695 * Attempts to migrate data and settings from version %major.%minor.%micro.
696 * Returns %TRUE if the migration was successful or if no action was
697 * necessary. Returns %FALSE and sets %error if the migration failed.
699 * Returns: %TRUE if successful, %FALSE otherwise
701 gboolean
702 e_shell_backend_migrate (EShellBackend *shell_backend,
703 gint major,
704 gint minor,
705 gint micro,
706 GError **error)
708 EShellBackendClass *class;
710 g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), TRUE);
712 class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
713 g_return_val_if_fail (class != NULL, TRUE);
715 if (class->migrate == NULL)
716 return TRUE;
718 return class->migrate (shell_backend, major, minor, micro, error);