evolution-3.6.4/modules/calendar/e-cal-shell-backend.c

No issues found

  1 /*
  2  * e-cal-shell-backend.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  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 19  *
 20  */
 21 
 22 #ifdef HAVE_CONFIG_H
 23 #include <config.h>
 24 #endif
 25 
 26 #include "e-cal-shell-backend.h"
 27 
 28 #include <string.h>
 29 #include <glib/gi18n.h>
 30 #include <libecal/libecal.h>
 31 #include <libedataserverui/libedataserverui.h>
 32 
 33 #include "e-util/e-import.h"
 34 #include "shell/e-shell.h"
 35 #include "shell/e-shell-backend.h"
 36 #include "shell/e-shell-window.h"
 37 #include "widgets/misc/e-cal-source-config.h"
 38 #include "widgets/misc/e-preferences-window.h"
 39 #include "widgets/misc/e-source-config-dialog.h"
 40 
 41 #include "calendar/gui/comp-util.h"
 42 #include "calendar/gui/dialogs/event-editor.h"
 43 #include "calendar/gui/e-calendar-view.h"
 44 #include "calendar/gui/gnome-cal.h"
 45 #include "calendar/importers/evolution-calendar-importer.h"
 46 
 47 #include "e-cal-shell-content.h"
 48 #include "e-cal-shell-migrate.h"
 49 #include "e-cal-shell-settings.h"
 50 #include "e-cal-shell-sidebar.h"
 51 #include "e-cal-shell-view.h"
 52 
 53 #include "e-calendar-preferences.h"
 54 
 55 #define E_CAL_SHELL_BACKEND_GET_PRIVATE(obj) \
 56 	(G_TYPE_INSTANCE_GET_PRIVATE \
 57 	((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendPrivate))
 58 
 59 struct _ECalShellBackendPrivate {
 60 	gint placeholder;
 61 };
 62 
 63 G_DEFINE_DYNAMIC_TYPE (
 64 	ECalShellBackend,
 65 	e_cal_shell_backend,
 66 	E_TYPE_SHELL_BACKEND)
 67 
 68 static void
 69 cal_shell_backend_new_event (ESource *source,
 70                              GAsyncResult *result,
 71                              EShell *shell,
 72                              CompEditorFlags flags,
 73                              gboolean all_day)
 74 {
 75 	EClient *client = NULL;
 76 	ECalClient *cal_client;
 77 	ECalComponent *comp;
 78 	EShellSettings *shell_settings;
 79 	CompEditor *editor;
 80 	GError *error = NULL;
 81 
 82 	/* XXX Handle errors better. */
 83 	e_client_utils_open_new_finish (source, result, &client, &error);
 84 
 85 	if (error != NULL) {
 86 		g_warn_if_fail (client == NULL);
 87 		g_warning (
 88 			"%s: Failed to open '%s': %s",
 89 			G_STRFUNC, e_source_get_display_name (source),
 90 			error->message);
 91 		g_error_free (error);
 92 		return;
 93 	}
 94 
 95 	g_return_if_fail (E_IS_CAL_CLIENT (client));
 96 
 97 	cal_client = E_CAL_CLIENT (client);
 98 	shell_settings = e_shell_get_shell_settings (shell);
 99 
100 	editor = event_editor_new (cal_client, shell, flags);
101 	comp = cal_comp_event_new_with_current_time (
102 		cal_client, all_day,
103 		e_shell_settings_get_pointer (
104 			shell_settings, "cal-timezone"),
105 		e_shell_settings_get_boolean (
106 			shell_settings, "cal-use-default-reminder"),
107 		e_shell_settings_get_int (
108 			shell_settings, "cal-default-reminder-interval"),
109 		e_shell_settings_get_int (  /* enum, actually */
110 			shell_settings, "cal-default-reminder-units"));
111 	e_cal_component_commit_sequence (comp);
112 	comp_editor_edit_comp (editor, comp);
113 
114 	gtk_window_present (GTK_WINDOW (editor));
115 
116 	g_object_unref (comp);
117 	g_object_unref (client);
118 }
119 
120 static void
121 cal_shell_backend_event_new_cb (GObject *source_object,
122                                 GAsyncResult *result,
123                                 gpointer shell)
124 {
125 	CompEditorFlags flags = 0;
126 	gboolean all_day = FALSE;
127 
128 	flags |= COMP_EDITOR_NEW_ITEM;
129 	flags |= COMP_EDITOR_USER_ORG;
130 
131 	cal_shell_backend_new_event (
132 		E_SOURCE (source_object), result, shell, flags, all_day);
133 
134 	g_object_unref (shell);
135 }
136 
137 static void
138 cal_shell_backend_event_all_day_new_cb (GObject *source_object,
139                                         GAsyncResult *result,
140                                         gpointer shell)
141 {
142 	CompEditorFlags flags = 0;
143 	gboolean all_day = TRUE;
144 
145 	flags |= COMP_EDITOR_NEW_ITEM;
146 	flags |= COMP_EDITOR_USER_ORG;
147 
148 	cal_shell_backend_new_event (
149 		E_SOURCE (source_object), result, shell, flags, all_day);
150 
151 	g_object_unref (shell);
152 }
153 
154 static void
155 cal_shell_backend_event_meeting_new_cb (GObject *source_object,
156                                         GAsyncResult *result,
157                                         gpointer shell)
158 {
159 	CompEditorFlags flags = 0;
160 	gboolean all_day = FALSE;
161 
162 	flags |= COMP_EDITOR_NEW_ITEM;
163 	flags |= COMP_EDITOR_USER_ORG;
164 	flags |= COMP_EDITOR_MEETING;
165 
166 	cal_shell_backend_new_event (
167 		E_SOURCE (source_object), result, shell, flags, all_day);
168 
169 	g_object_unref (shell);
170 }
171 
172 static void
173 action_event_new_cb (GtkAction *action,
174                      EShellWindow *shell_window)
175 {
176 	EShell *shell;
177 	EShellView *shell_view;
178 	EShellBackend *shell_backend;
179 	ESource *source;
180 	ESourceRegistry *registry;
181 	EClientSourceType source_type;
182 	const gchar *action_name;
183 
184 	shell = e_shell_window_get_shell (shell_window);
185 
186 	action_name = gtk_action_get_name (action);
187 
188 	/* With a 'calendar' active shell view pass the new appointment
189 	 * request to it, thus the event will inherit selected time from
190 	 * the view. */
191 	shell_view = e_shell_window_peek_shell_view (shell_window, "calendar");
192 	if (shell_view != NULL) {
193 		EShellContent *shell_content;
194 		GnomeCalendar *gcal;
195 		GnomeCalendarViewType view_type;
196 		ECalendarView *view;
197 
198 		shell_backend = e_shell_view_get_shell_backend (shell_view);
199 		shell_content = e_shell_view_get_shell_content (shell_view);
200 
201 		gcal = e_cal_shell_content_get_calendar (
202 			E_CAL_SHELL_CONTENT (shell_content));
203 
204 		view_type = gnome_calendar_get_view (gcal);
205 		view = gnome_calendar_get_calendar_view (gcal, view_type);
206 
207 		if (view) {
208 			g_object_set (
209 				G_OBJECT (shell_backend),
210 				"prefer-new-item", action_name, NULL);
211 
212 			e_calendar_view_new_appointment_full (
213 				view,
214 				g_str_equal (action_name, "event-all-day-new"),
215 				g_str_equal (action_name, "event-meeting-new"),
216 				TRUE);
217 
218 			return;
219 		}
220 	}
221 
222 	/* This callback is used for both appointments and meetings. */
223 
224 	source_type = E_CLIENT_SOURCE_TYPE_EVENTS;
225 
226 	registry = e_shell_get_registry (shell);
227 	source = e_source_registry_ref_default_calendar (registry);
228 
229 	shell_backend = e_shell_get_backend_by_name (shell, "calendar");
230 	g_object_set (G_OBJECT (shell_backend), "prefer-new-item", action_name, NULL);
231 
232 	/* Use a callback function appropriate for the action.
233 	 * FIXME Need to obtain a better default time zone. */
234 	if (strcmp (action_name, "event-all-day-new") == 0)
235 		e_client_utils_open_new (
236 			source, source_type, FALSE, NULL,
237 			cal_shell_backend_event_all_day_new_cb,
238 			g_object_ref (shell));
239 	else if (strcmp (action_name, "event-meeting-new") == 0)
240 		e_client_utils_open_new (
241 			source, source_type, FALSE, NULL,
242 			cal_shell_backend_event_meeting_new_cb,
243 			g_object_ref (shell));
244 	else
245 		e_client_utils_open_new (
246 			source, source_type, FALSE, NULL,
247 			cal_shell_backend_event_new_cb,
248 			g_object_ref (shell));
249 
250 	g_object_unref (source);
251 }
252 
253 static void
254 action_calendar_new_cb (GtkAction *action,
255                         EShellWindow *shell_window)
256 {
257 	EShell *shell;
258 	ESourceRegistry *registry;
259 	ECalClientSourceType source_type;
260 	GtkWidget *config;
261 	GtkWidget *dialog;
262 	const gchar *icon_name;
263 
264 	shell = e_shell_window_get_shell (shell_window);
265 
266 	registry = e_shell_get_registry (shell);
267 	source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
268 	config = e_cal_source_config_new (registry, NULL, source_type);
269 
270 	dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config));
271 
272 	gtk_window_set_transient_for (
273 		GTK_WINDOW (dialog), GTK_WINDOW (shell_window));
274 
275 	icon_name = gtk_action_get_icon_name (action);
276 	gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name);
277 
278 	gtk_window_set_title (GTK_WINDOW (dialog), _("New Calendar"));
279 
280 	gtk_widget_show (dialog);
281 }
282 
283 static GtkActionEntry item_entries[] = {
284 
285 	{ "event-new",
286 	  "appointment-new",
287 	  NC_("New", "_Appointment"),
288 	  "<Shift><Control>a",
289 	  N_("Create a new appointment"),
290 	  G_CALLBACK (action_event_new_cb) },
291 
292 	{ "event-all-day-new",
293 	  "stock_new-24h-appointment",
294 	  NC_("New", "All Day A_ppointment"),
295 	  NULL,
296 	  N_("Create a new all-day appointment"),
297 	  G_CALLBACK (action_event_new_cb) },
298 
299 	{ "event-meeting-new",
300 	  "stock_new-meeting",
301 	  NC_("New", "M_eeting"),
302 	  "<Shift><Control>e",
303 	  N_("Create a new meeting request"),
304 	  G_CALLBACK (action_event_new_cb) }
305 };
306 
307 static GtkActionEntry source_entries[] = {
308 
309 	{ "calendar-new",
310 	  "x-office-calendar",
311 	  NC_("New", "Cale_ndar"),
312 	  NULL,
313 	  N_("Create a new calendar"),
314 	  G_CALLBACK (action_calendar_new_cb) }
315 };
316 
317 static void
318 cal_shell_backend_init_importers (void)
319 {
320 	EImportClass *import_class;
321 	EImportImporter *importer;
322 
323 	import_class = g_type_class_ref (e_import_get_type ());
324 
325 	importer = gnome_calendar_importer_peek ();
326 	e_import_class_add_importer (import_class, importer, NULL, NULL);
327 
328 	importer = ical_importer_peek ();
329 	e_import_class_add_importer (import_class, importer, NULL, NULL);
330 
331 	importer = vcal_importer_peek ();
332 	e_import_class_add_importer (import_class, importer, NULL, NULL);
333 }
334 
335 static time_t
336 utc_to_user_zone (time_t utc_time,
337                   icaltimezone *zone)
338 {
339 	if (!zone || (gint) utc_time == -1)
340 		return utc_time;
341 
342 	return icaltime_as_timet (
343 		icaltime_from_timet_with_zone (utc_time, FALSE, zone));
344 }
345 
346 static gboolean
347 cal_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
348                                  const gchar *uri)
349 {
350 	EShell *shell;
351 	EShellSettings *shell_settings;
352 	CompEditor *editor;
353 	CompEditorFlags flags = 0;
354 	ECalClient *client;
355 	ECalComponent *comp;
356 	ESource *source;
357 	ESourceRegistry *registry;
358 	ECalClientSourceType source_type;
359 	SoupURI *soup_uri;
360 	icalcomponent *icalcomp;
361 	icalproperty *icalprop;
362 	const gchar *cp;
363 	gchar *source_uid = NULL;
364 	gchar *comp_uid = NULL;
365 	gchar *comp_rid = NULL;
366 	GDate start_date;
367 	GDate end_date;
368 	icaltimezone *zone;
369 	gboolean handled = FALSE;
370 	GError *error = NULL;
371 
372 	source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
373 	shell = e_shell_backend_get_shell (shell_backend);
374 	shell_settings = e_shell_get_shell_settings (shell);
375 
376 	zone = e_shell_settings_get_pointer (shell_settings, "cal-timezone");
377 
378 	if (strncmp (uri, "calendar:", 9) != 0)
379 		return FALSE;
380 
381 	soup_uri = soup_uri_new (uri);
382 
383 	if (soup_uri == NULL)
384 		return FALSE;
385 
386 	cp = soup_uri_get_query (soup_uri);
387 	if (cp == NULL)
388 		goto exit;
389 
390 	g_date_clear (&start_date, 1);
391 	g_date_clear (&end_date, 1);
392 
393 	while (*cp != '\0') {
394 		gchar *header;
395 		gchar *content;
396 		gsize header_len;
397 		gsize content_len;
398 
399 		header_len = strcspn (cp, "=&");
400 
401 		/* It it's malformed, give up. */
402 		if (cp[header_len] != '=')
403 			break;
404 
405 		header = (gchar *) cp;
406 		header[header_len] = '\0';
407 		cp += header_len + 1;
408 
409 		content_len = strcspn (cp, "&");
410 
411 		content = g_strndup (cp, content_len);
412 		if (g_ascii_strcasecmp (header, "startdate") == 0)
413 			g_date_set_time_t (
414 				&start_date, utc_to_user_zone (
415 				time_from_isodate (content), zone));
416 		else if (g_ascii_strcasecmp (header, "enddate") == 0)
417 			g_date_set_time_t (
418 				&end_date, utc_to_user_zone (
419 				time_from_isodate (content), zone));
420 		else if (g_ascii_strcasecmp (header, "source-uid") == 0)
421 			source_uid = g_strdup (content);
422 		else if (g_ascii_strcasecmp (header, "comp-uid") == 0)
423 			comp_uid = g_strdup (content);
424 		else if (g_ascii_strcasecmp (header, "comp-rid") == 0)
425 			comp_rid = g_strdup (content);
426 		g_free (content);
427 
428 		cp += content_len;
429 		if (*cp == '&') {
430 			cp++;
431 			if (strncmp (cp, "amp;", 4) == 0)
432 				cp += 4;
433 		}
434 	}
435 
436 	/* This is primarily for launching Evolution
437 	 * from the calendar in the clock applet. */
438 	if (g_date_valid (&start_date)) {
439 		if (g_date_valid (&end_date))
440 			e_cal_shell_backend_open_date_range (
441 				E_CAL_SHELL_BACKEND (shell_backend),
442 				&start_date, &end_date);
443 		else
444 			e_cal_shell_backend_open_date_range (
445 				E_CAL_SHELL_BACKEND (shell_backend),
446 				&start_date, NULL);
447 		handled = TRUE;
448 		goto exit;
449 	}
450 
451 	if (source_uid == NULL || comp_uid == NULL)
452 		goto exit;
453 
454 	/* URI is valid, so consider it handled.  Whether
455 	 * we successfully open it is another matter... */
456 	handled = TRUE;
457 
458 	registry = e_shell_get_registry (shell);
459 	source = e_source_registry_ref_source (registry, source_uid);
460 	if (source == NULL) {
461 		g_printerr ("No source for UID '%s'\n", source_uid);
462 		goto exit;
463 	}
464 
465 	client = e_cal_client_new (source, source_type, &error);
466 
467 	if (client != NULL)
468 		e_client_open_sync (E_CLIENT (client), TRUE, NULL, &error);
469 
470 	if (error != NULL) {
471 		g_warning (
472 			"%s: Failed to create/open client '%s': %s",
473 			G_STRFUNC, e_source_get_display_name (source),
474 			error->message);
475 		if (client != NULL)
476 			g_object_unref (client);
477 		g_object_unref (source);
478 		g_error_free (error);
479 		goto exit;
480 	}
481 
482 	g_object_unref (source);
483 	source = NULL;
484 
485 	/* XXX Copied from e_cal_shell_view_open_event().
486 	 *     Clearly a new utility function is needed. */
487 
488 	editor = comp_editor_find_instance (comp_uid);
489 
490 	if (editor != NULL)
491 		goto present;
492 
493 	e_cal_client_get_object_sync (
494 		client, comp_uid, comp_rid, &icalcomp, NULL, &error);
495 
496 	if (error != NULL) {
497 		g_warning (
498 			"%s: Failed to get object from client: %s",
499 			G_STRFUNC, error->message);
500 		g_object_unref (client);
501 		g_error_free (error);
502 		goto exit;
503 	}
504 
505 	comp = e_cal_component_new ();
506 	if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
507 		g_warning ("%s: Failed to set icalcomp to comp\n", G_STRFUNC);
508 		icalcomponent_free (icalcomp);
509 		icalcomp = NULL;
510 	}
511 
512 	icalprop = icalcomp ? icalcomponent_get_first_property (
513 		icalcomp, ICAL_ATTENDEE_PROPERTY) : NULL;
514 	if (icalprop != NULL)
515 		flags |= COMP_EDITOR_MEETING;
516 
517 	if (itip_organizer_is_user (registry, comp, client))
518 		flags |= COMP_EDITOR_USER_ORG;
519 
520 	if (itip_sentby_is_user (registry, comp, client))
521 		flags |= COMP_EDITOR_USER_ORG;
522 
523 	if (!e_cal_component_has_attendees (comp))
524 		flags |= COMP_EDITOR_USER_ORG;
525 
526 	editor = event_editor_new (client, shell, flags);
527 	comp_editor_edit_comp (editor, comp);
528 
529 	g_object_unref (comp);
530 
531 present:
532 	gtk_window_present (GTK_WINDOW (editor));
533 
534 	g_object_unref (client);
535 
536 exit:
537 	g_free (source_uid);
538 	g_free (comp_uid);
539 	g_free (comp_rid);
540 
541 	soup_uri_free (soup_uri);
542 
543 	return handled;
544 }
545 
546 static void
547 cal_shell_backend_window_added_cb (EShellBackend *shell_backend,
548                                    GtkWindow *window)
549 {
550 	const gchar *backend_name;
551 
552 	if (!E_IS_SHELL_WINDOW (window))
553 		return;
554 
555 	backend_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
556 
557 	e_shell_window_register_new_item_actions (
558 		E_SHELL_WINDOW (window), backend_name,
559 		item_entries, G_N_ELEMENTS (item_entries));
560 
561 	e_shell_window_register_new_source_actions (
562 		E_SHELL_WINDOW (window), backend_name,
563 		source_entries, G_N_ELEMENTS (source_entries));
564 }
565 
566 static void
567 ensure_alarm_notify_is_running (void)
568 {
569 	const gchar *base_dir;
570 	gchar *filename;
571 
572 	#ifdef G_OS_WIN32
573 	base_dir = EVOLUTION_BINDIR;
574 	#else
575 	base_dir = EVOLUTION_PRIVLIBEXECDIR;
576 	#endif
577 
578 	filename = g_build_filename (base_dir, "evolution-alarm-notify", NULL);
579 
580 	if (g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE)) {
581 		gchar *argv[2];
582 		GError *error = NULL;
583 
584 		argv[0] = filename;
585 		argv[1] = NULL;
586 
587 		g_spawn_async (
588 			base_dir, argv, NULL, 0, NULL, NULL, NULL, &error);
589 
590 		if (error != NULL) {
591 			g_message (
592 				"Failed to start '%s': %s",
593 				filename, error->message);
594 			g_error_free (error);
595 		}
596 	}
597 
598 	g_free (filename);
599 }
600 
601 static void
602 cal_shell_backend_constructed (GObject *object)
603 {
604 	EShell *shell;
605 	EShellBackend *shell_backend;
606 	GtkWidget *preferences_window;
607 
608 	shell_backend = E_SHELL_BACKEND (object);
609 	shell = e_shell_backend_get_shell (shell_backend);
610 
611 	g_signal_connect_swapped (
612 		shell, "handle-uri",
613 		G_CALLBACK (cal_shell_backend_handle_uri_cb),
614 		shell_backend);
615 
616 	g_signal_connect_swapped (
617 		shell, "window-added",
618 		G_CALLBACK (cal_shell_backend_window_added_cb),
619 		shell_backend);
620 
621 	cal_shell_backend_init_importers ();
622 
623 	e_cal_shell_backend_init_settings (shell);
624 
625 	/* Setup preference widget factories */
626 	preferences_window = e_shell_get_preferences_window (shell);
627 
628 	e_preferences_window_add_page (
629 		E_PREFERENCES_WINDOW (preferences_window),
630 		"calendar-and-tasks",
631 		"preferences-calendar-and-tasks",
632 		_("Calendar and Tasks"),
633 		"index#calendar",
634 		e_calendar_preferences_new,
635 		600);
636 
637 	g_object_bind_property (
638 		e_shell_get_shell_settings (shell), "cal-prefer-new-item",
639 		shell_backend, "prefer-new-item",
640 		G_BINDING_BIDIRECTIONAL |
641 		G_BINDING_SYNC_CREATE);
642 
643 	/* Chain up to parent's constructed() method. */
644 	G_OBJECT_CLASS (e_cal_shell_backend_parent_class)->constructed (object);
645 
646 	ensure_alarm_notify_is_running ();
647 }
648 
649 static void
650 e_cal_shell_backend_class_init (ECalShellBackendClass *class)
651 {
652 	GObjectClass *object_class;
653 	EShellBackendClass *shell_backend_class;
654 
655 	g_type_class_add_private (class, sizeof (ECalShellBackendPrivate));
656 
657 	object_class = G_OBJECT_CLASS (class);
658 	object_class->constructed = cal_shell_backend_constructed;
659 
660 	shell_backend_class = E_SHELL_BACKEND_CLASS (class);
661 	shell_backend_class->shell_view_type = E_TYPE_CAL_SHELL_VIEW;
662 	shell_backend_class->name = "calendar";
663 	shell_backend_class->aliases = "";
664 	shell_backend_class->schemes = "calendar";
665 	shell_backend_class->sort_order = 400;
666 	shell_backend_class->preferences_page = "calendar-and-tasks";
667 	shell_backend_class->start = NULL;
668 	shell_backend_class->migrate = e_cal_shell_backend_migrate;
669 
670 	/* Register relevant ESource extensions. */
671 	E_TYPE_SOURCE_CALENDAR;
672 }
673 
674 static void
675 e_cal_shell_backend_class_finalize (ECalShellBackendClass *class)
676 {
677 }
678 
679 static void
680 e_cal_shell_backend_init (ECalShellBackend *cal_shell_backend)
681 {
682 	icalarray *builtin_timezones;
683 	gint ii;
684 
685 	cal_shell_backend->priv =
686 		E_CAL_SHELL_BACKEND_GET_PRIVATE (cal_shell_backend);
687 
688 	/* XXX Pre-load all built-in timezones in libical.
689 	 *
690 	 *     Built-in time zones in libical 0.43 are loaded on demand,
691 	 *     but not in a thread-safe manner, resulting in a race when
692 	 *     multiple threads call icaltimezone_load_builtin_timezone()
693 	 *     on the same time zone.  Until built-in time zone loading
694 	 *     in libical is made thread-safe, work around the issue by
695 	 *     loading all built-in time zones now, so libical's internal
696 	 *     time zone array will be fully populated before any threads
697 	 *     are spawned.
698 	 */
699 	builtin_timezones = icaltimezone_get_builtin_timezones ();
700 	for (ii = 0; ii < builtin_timezones->num_elements; ii++) {
701 		icaltimezone *zone;
702 
703 		zone = icalarray_element_at (builtin_timezones, ii);
704 
705 		/* We don't care about the component right now,
706 		 * we just need some function that will trigger
707 		 * icaltimezone_load_builtin_timezone(). */
708 		icaltimezone_get_component (zone);
709 	}
710 }
711 
712 void
713 e_cal_shell_backend_type_register (GTypeModule *type_module)
714 {
715 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
716 	 *     function, so we have to wrap it with a public function in
717 	 *     order to register types from a separate compilation unit. */
718 	e_cal_shell_backend_register_type (type_module);
719 }
720 
721 void
722 e_cal_shell_backend_open_date_range (ECalShellBackend *cal_shell_backend,
723                                      const GDate *start_date,
724                                      const GDate *end_date)
725 {
726 	EShell *shell;
727 	EShellView *shell_view;
728 	EShellBackend *shell_backend;
729 	EShellSidebar *shell_sidebar;
730 	GtkWidget *shell_window = NULL;
731 	GtkApplication *application;
732 	ECalendar *navigator;
733 	GList *list;
734 
735 	g_return_if_fail (E_IS_CAL_SHELL_BACKEND (cal_shell_backend));
736 
737 	shell_backend = E_SHELL_BACKEND (cal_shell_backend);
738 	shell = e_shell_backend_get_shell (shell_backend);
739 
740 	application = GTK_APPLICATION (shell);
741 	list = gtk_application_get_windows (application);
742 
743 	/* Try to find an EShellWindow already in calendar view. */
744 	while (list != NULL) {
745 		GtkWidget *window = GTK_WIDGET (list->data);
746 
747 		if (E_IS_SHELL_WINDOW (window)) {
748 			const gchar *active_view;
749 
750 			active_view = e_shell_window_get_active_view (
751 				E_SHELL_WINDOW (window));
752 			if (g_strcmp0 (active_view, "calendar") == 0) {
753 				gtk_window_present (GTK_WINDOW (window));
754 				shell_window = window;
755 				break;
756 			}
757 		}
758 
759 		list = g_list_next (list);
760 	}
761 
762 	/* Otherwise create a new EShellWindow in calendar view. */
763 	if (shell_window == NULL)
764 		shell_window = e_shell_create_shell_window (shell, "calendar");
765 
766 	/* Now dig up the date navigator and select the date range. */
767 
768 	shell_view = e_shell_window_get_shell_view (
769 		E_SHELL_WINDOW (shell_window), "calendar");
770 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
771 	navigator = e_cal_shell_sidebar_get_date_navigator (
772 		E_CAL_SHELL_SIDEBAR (shell_sidebar));
773 
774 	e_calendar_item_set_selection (
775 		navigator->calitem, start_date, end_date);
776 }