No issues found
1 /*
2 * Evolution calendar importer component
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 * Authors:
19 * Rodrigo Moya <rodrigo@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <glib/gi18n.h>
30
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35
36 #include <gtk/gtk.h>
37
38 #include <libecal/libecal.h>
39 #include <libedataserverui/libedataserverui.h>
40 #include <libical/icalvcal.h>
41
42 #include "evolution-calendar-importer.h"
43 #include "shell/e-shell.h"
44 #include "gui/calendar-config-keys.h"
45
46 #include "e-util/e-import.h"
47 #include "e-util/e-util-private.h"
48 #include "e-util/e-datetime-format.h"
49 #include "misc/e-web-view-preview.h"
50
51 /* We timeout after 2 minutes, when opening the folders. */
52 #define IMPORTER_TIMEOUT_SECONDS 120
53
54 typedef struct {
55 EImport *import;
56 EImportTarget *target;
57
58 guint idle_id;
59
60 ECalClient *cal_client;
61 EClientSourceType source_type;
62
63 icalcomponent *icalcomp;
64
65 GCancellable *cancellable;
66 } ICalImporter;
67
68 typedef struct {
69 EImport *ei;
70 EImportTarget *target;
71 GList *tasks;
72 icalcomponent *icalcomp;
73 GCancellable *cancellable;
74 } ICalIntelligentImporter;
75
76 static const gint import_type_map[] = {
77 E_CLIENT_SOURCE_TYPE_EVENTS,
78 E_CLIENT_SOURCE_TYPE_TASKS,
79 -1
80 };
81
82 static const gchar *import_type_strings[] = {
83 N_("Appointments and Meetings"),
84 N_("Tasks"),
85 NULL
86 };
87
88 /*
89 * Functions shared by iCalendar & vCalendar importer.
90 */
91
92 static GtkWidget *ical_get_preview (icalcomponent *icalcomp);
93
94 static gboolean
95 is_icalcomp_usable (icalcomponent *icalcomp)
96 {
97 return icalcomp && icalcomponent_is_valid (icalcomp) && (
98 icalcomponent_get_first_component (icalcomp, ICAL_VEVENT_COMPONENT) != NULL ||
99 icalcomponent_get_first_component (icalcomp, ICAL_VTODO_COMPONENT) != NULL);
100 }
101
102 static void
103 ivcal_import_done (ICalImporter *ici)
104 {
105 if (ici->cal_client)
106 g_object_unref (ici->cal_client);
107 icalcomponent_free (ici->icalcomp);
108
109 e_import_complete (ici->import, ici->target);
110 g_object_unref (ici->import);
111 g_object_unref (ici->cancellable);
112 g_free (ici);
113 }
114
115 /* This removes all components except VEVENTs and VTIMEZONEs from the toplevel */
116 static void
117 prepare_events (icalcomponent *icalcomp,
118 GList **vtodos)
119 {
120 icalcomponent *subcomp;
121 icalcompiter iter;
122
123 if (vtodos)
124 *vtodos = NULL;
125
126 iter = icalcomponent_begin_component (icalcomp, ICAL_ANY_COMPONENT);
127 while ((subcomp = icalcompiter_deref (&iter)) != NULL) {
128 icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
129 if (child_kind != ICAL_VEVENT_COMPONENT
130 && child_kind != ICAL_VTIMEZONE_COMPONENT) {
131
132 icalcompiter_next (&iter);
133
134 icalcomponent_remove_component (icalcomp, subcomp);
135 if (child_kind == ICAL_VTODO_COMPONENT && vtodos)
136 *vtodos = g_list_prepend (*vtodos, subcomp);
137 else
138 icalcomponent_free (subcomp);
139 } else {
140 icalcompiter_next (&iter);
141 }
142 }
143 }
144
145 /* This removes all components except VTODOs and VTIMEZONEs from the toplevel
146 * icalcomponent, and adds the given list of VTODO components. The list is
147 * freed afterwards. */
148 static void
149 prepare_tasks (icalcomponent *icalcomp,
150 GList *vtodos)
151 {
152 icalcomponent *subcomp;
153 GList *elem;
154 icalcompiter iter;
155
156 iter = icalcomponent_begin_component (icalcomp, ICAL_ANY_COMPONENT);
157 while ((subcomp = icalcompiter_deref (&iter)) != NULL) {
158 icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
159 if (child_kind != ICAL_VTODO_COMPONENT
160 && child_kind != ICAL_VTIMEZONE_COMPONENT) {
161 icalcompiter_next (&iter);
162 icalcomponent_remove_component (icalcomp, subcomp);
163 icalcomponent_free (subcomp);
164 } else {
165 icalcompiter_next (&iter);
166 }
167 }
168
169 for (elem = vtodos; elem; elem = elem->next) {
170 icalcomponent_add_component (icalcomp, elem->data);
171 }
172 g_list_free (vtodos);
173 }
174
175 struct UpdateObjectsData
176 {
177 void (*done_cb) (gpointer user_data);
178 gpointer user_data;
179 };
180
181 static void
182 receive_objects_ready_cb (GObject *source_object,
183 GAsyncResult *result,
184 gpointer user_data)
185 {
186 ECalClient *cal_client = E_CAL_CLIENT (source_object);
187 struct UpdateObjectsData *uod = user_data;
188 GError *error = NULL;
189
190 g_return_if_fail (uod != NULL);
191
192 e_cal_client_receive_objects_finish (cal_client, result, &error);
193
194 if (error != NULL) {
195 g_warning (
196 "%s: Failed to receive objects: %s",
197 G_STRFUNC, error->message);
198 g_error_free (error);
199 }
200
201 if (uod->done_cb)
202 uod->done_cb (uod->user_data);
203 g_free (uod);
204 }
205
206 static void
207 update_objects (ECalClient *cal_client,
208 icalcomponent *icalcomp,
209 GCancellable *cancellable,
210 void (*done_cb) (gpointer user_data),
211 gpointer user_data)
212 {
213 icalcomponent_kind kind;
214 icalcomponent *vcal;
215 struct UpdateObjectsData *uod;
216
217 kind = icalcomponent_isa (icalcomp);
218 if (kind == ICAL_VTODO_COMPONENT || kind == ICAL_VEVENT_COMPONENT) {
219 vcal = e_cal_util_new_top_level ();
220 if (icalcomponent_get_method (icalcomp) == ICAL_METHOD_CANCEL)
221 icalcomponent_set_method (vcal, ICAL_METHOD_CANCEL);
222 else
223 icalcomponent_set_method (vcal, ICAL_METHOD_PUBLISH);
224 icalcomponent_add_component (vcal, icalcomponent_new_clone (icalcomp));
225 } else if (kind == ICAL_VCALENDAR_COMPONENT) {
226 vcal = icalcomponent_new_clone (icalcomp);
227 if (!icalcomponent_get_first_property (vcal, ICAL_METHOD_PROPERTY))
228 icalcomponent_set_method (vcal, ICAL_METHOD_PUBLISH);
229 } else {
230 if (done_cb)
231 done_cb (user_data);
232 return;
233 }
234
235 uod = g_new0 (struct UpdateObjectsData, 1);
236 uod->done_cb = done_cb;
237 uod->user_data = user_data;
238
239 e_cal_client_receive_objects (cal_client, vcal, cancellable, receive_objects_ready_cb, uod);
240
241 icalcomponent_free (vcal);
242
243 return;
244 }
245
246 struct _selector_data {
247 EImportTarget *target;
248 GtkWidget *selector;
249 GtkWidget *notebook;
250 gint page;
251 };
252
253 static void
254 button_toggled_cb (GtkWidget *widget,
255 struct _selector_data *sd)
256 {
257 ESourceSelector *selector;
258 ESource *source;
259 GtkNotebook *notebook;
260
261 selector = E_SOURCE_SELECTOR (sd->selector);
262 source = e_source_selector_ref_primary_selection (selector);
263 g_return_if_fail (source != NULL);
264
265 g_datalist_set_data_full (
266 &sd->target->data, "primary-source",
267 source, (GDestroyNotify) g_object_unref);
268 g_datalist_set_data (
269 &sd->target->data, "primary-type",
270 GINT_TO_POINTER (import_type_map[sd->page]));
271
272 notebook = GTK_NOTEBOOK (sd->notebook);
273 gtk_notebook_set_current_page (notebook, sd->page);
274 }
275
276 static void
277 primary_selection_changed_cb (ESourceSelector *selector,
278 EImportTarget *target)
279 {
280 ESource *source;
281
282 source = e_source_selector_ref_primary_selection (selector);
283 g_return_if_fail (source != NULL);
284
285 g_datalist_set_data_full (
286 &target->data, "primary-source",
287 source, (GDestroyNotify) g_object_unref);
288 }
289
290 static GtkWidget *
291 ivcal_getwidget (EImport *ei,
292 EImportTarget *target,
293 EImportImporter *im)
294 {
295 EShell *shell;
296 ESourceRegistry *registry;
297 GtkWidget *vbox, *hbox, *first = NULL;
298 GSList *group = NULL;
299 gint i;
300 GtkWidget *nb;
301
302 shell = e_shell_get_default ();
303 registry = e_shell_get_registry (shell);
304
305 vbox = gtk_vbox_new (FALSE, FALSE);
306
307 hbox = gtk_hbox_new (FALSE, FALSE);
308 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6);
309
310 nb = gtk_notebook_new ();
311 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
312 gtk_box_pack_start (GTK_BOX (vbox), nb, TRUE, TRUE, 6);
313
314 /* Type of icalendar items */
315 for (i = 0; import_type_map[i] != -1; i++) {
316 GtkWidget *selector, *rb;
317 ESource *source = NULL;
318 GtkWidget *scrolled;
319 struct _selector_data *sd;
320 const gchar *extension_name;
321 GList *list;
322
323 switch (import_type_map[i]) {
324 case E_CLIENT_SOURCE_TYPE_EVENTS:
325 extension_name = E_SOURCE_EXTENSION_CALENDAR;
326 break;
327 case E_CLIENT_SOURCE_TYPE_TASKS:
328 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
329 break;
330 default:
331 g_warn_if_reached ();
332 continue;
333 }
334
335 selector = e_source_selector_new (registry, extension_name);
336 e_source_selector_set_show_toggles (
337 E_SOURCE_SELECTOR (selector), FALSE);
338 scrolled = gtk_scrolled_window_new (NULL, NULL);
339 gtk_scrolled_window_set_policy ((GtkScrolledWindow *) scrolled, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
340 gtk_container_add ((GtkContainer *) scrolled, selector);
341 gtk_notebook_append_page (GTK_NOTEBOOK (nb), scrolled, NULL);
342
343 list = e_source_registry_list_sources (registry, extension_name);
344 if (list != NULL) {
345 source = E_SOURCE (list->data);
346 e_source_selector_set_primary_selection (
347 E_SOURCE_SELECTOR (selector), source);
348 }
349
350 g_list_free_full (list, (GDestroyNotify) g_object_unref);
351
352 g_signal_connect (
353 selector, "primary_selection_changed",
354 G_CALLBACK (primary_selection_changed_cb), target);
355
356 rb = gtk_radio_button_new_with_label (group, _(import_type_strings[i]));
357 gtk_box_pack_start (GTK_BOX (hbox), rb, FALSE, FALSE, 6);
358
359 sd = g_malloc0 (sizeof (*sd));
360 sd->target = target;
361 sd->selector = selector;
362 sd->notebook = nb;
363 sd->page = i;
364 g_object_set_data_full ((GObject *) rb, "selector-data", sd, g_free);
365 g_signal_connect (
366 rb, "toggled",
367 G_CALLBACK (button_toggled_cb), sd);
368
369 if (!group)
370 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rb));
371 if (first == NULL) {
372 g_datalist_set_data_full (&target->data, "primary-source", g_object_ref (source), g_object_unref);
373 g_datalist_set_data (&target->data, "primary-type", GINT_TO_POINTER (import_type_map[i]));
374 first = rb;
375 }
376 }
377 if (first)
378 gtk_toggle_button_set_active ((GtkToggleButton *) first, TRUE);
379
380 gtk_widget_show_all (vbox);
381
382 return vbox;
383 }
384
385 static void
386 ivcal_call_import_done (gpointer user_data)
387 {
388 ivcal_import_done (user_data);
389 }
390
391 static gboolean
392 ivcal_import_items (gpointer d)
393 {
394 ICalImporter *ici = d;
395
396 switch (ici->source_type) {
397 case E_CLIENT_SOURCE_TYPE_EVENTS:
398 prepare_events (ici->icalcomp, NULL);
399 update_objects (ici->cal_client, ici->icalcomp, ici->cancellable, ivcal_call_import_done, ici);
400 break;
401 case E_CLIENT_SOURCE_TYPE_TASKS:
402 prepare_tasks (ici->icalcomp, NULL);
403 update_objects (ici->cal_client, ici->icalcomp, ici->cancellable, ivcal_call_import_done, ici);
404 break;
405 default:
406 g_warn_if_reached ();
407
408 ici->idle_id = 0;
409 ivcal_import_done (ici);
410 return FALSE;
411 }
412
413 ici->idle_id = 0;
414
415 return FALSE;
416 }
417
418 static void
419 ivcal_opened (GObject *source_object,
420 GAsyncResult *result,
421 gpointer user_data)
422 {
423 ESource *source = E_SOURCE (source_object);
424 EClient *client = NULL;
425 ICalImporter *ici = user_data;
426 GError *error = NULL;
427
428 g_return_if_fail (ici != NULL);
429
430 e_client_utils_open_new_finish (source, result, &client, &error);
431
432 if (error != NULL) {
433 g_warn_if_fail (client == NULL);
434 g_warning (
435 "%s: Failed to open calendar: %s",
436 G_STRFUNC, error->message);
437 g_error_free (error);
438 ivcal_import_done (ici);
439 return;
440 }
441
442 g_return_if_fail (E_IS_CLIENT (client));
443
444 ici->cal_client = E_CAL_CLIENT (client);
445
446 e_import_status (ici->import, ici->target, _("Importing..."), 0);
447 ici->idle_id = g_idle_add (ivcal_import_items, ici);
448 }
449
450 static void
451 ivcal_import (EImport *ei,
452 EImportTarget *target,
453 icalcomponent *icalcomp)
454 {
455 EClientSourceType type;
456 ICalImporter *ici = g_malloc0 (sizeof (*ici));
457
458 type = GPOINTER_TO_INT (g_datalist_get_data (&target->data, "primary-type"));
459
460 ici->import = ei;
461 g_datalist_set_data (&target->data, "ivcal-data", ici);
462 g_object_ref (ei);
463 ici->target = target;
464 ici->icalcomp = icalcomp;
465 ici->cal_client = NULL;
466 ici->source_type = type;
467 ici->cancellable = g_cancellable_new ();
468 e_import_status (ei, target, _("Opening calendar"), 0);
469
470 e_client_utils_open_new (
471 g_datalist_get_data (&target->data, "primary-source"),
472 type, FALSE, ici->cancellable, ivcal_opened, ici);
473 }
474
475 static void
476 ivcal_cancel (EImport *ei,
477 EImportTarget *target,
478 EImportImporter *im)
479 {
480 ICalImporter *ici = g_datalist_get_data (&target->data, "ivcal-data");
481
482 if (ici)
483 g_cancellable_cancel (ici->cancellable);
484 }
485
486 /* ********************************************************************** */
487 /*
488 * iCalendar importer functions.
489 */
490
491 static gboolean
492 ical_supported (EImport *ei,
493 EImportTarget *target,
494 EImportImporter *im)
495 {
496 gchar *filename;
497 gchar *contents;
498 gboolean ret = FALSE;
499 EImportTargetURI *s;
500
501 if (target->type != E_IMPORT_TARGET_URI)
502 return FALSE;
503
504 s = (EImportTargetURI *) target;
505 if (s->uri_src == NULL)
506 return TRUE;
507
508 if (strncmp (s->uri_src, "file:///", 8) != 0)
509 return FALSE;
510
511 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
512 if (!filename)
513 return FALSE;
514
515 if (g_file_get_contents (filename, &contents, NULL, NULL)) {
516 icalcomponent *icalcomp = NULL;
517
518 if (g_ascii_strncasecmp (contents, "BEGIN:", 6) == 0)
519 icalcomp = e_cal_util_parse_ics_string (contents);
520 g_free (contents);
521
522 if (icalcomp) {
523 if (is_icalcomp_usable (icalcomp))
524 ret = TRUE;
525 else
526 ret = FALSE;
527 icalcomponent_free (icalcomp);
528 }
529 }
530 g_free (filename);
531
532 return ret;
533 }
534
535 static void
536 ical_import (EImport *ei,
537 EImportTarget *target,
538 EImportImporter *im)
539 {
540 gchar *filename;
541 gchar *contents;
542 icalcomponent *icalcomp;
543 EImportTargetURI *s = (EImportTargetURI *) target;
544
545 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
546 if (!filename) {
547 e_import_complete (ei, target);
548 return;
549 }
550
551 if (!g_file_get_contents (filename, &contents, NULL, NULL)) {
552 g_free (filename);
553 e_import_complete (ei, target);
554 return;
555 }
556 g_free (filename);
557
558 icalcomp = e_cal_util_parse_ics_string (contents);
559 g_free (contents);
560
561 if (icalcomp)
562 ivcal_import (ei, target, icalcomp);
563 else
564 e_import_complete (ei, target);
565 }
566
567 static GtkWidget *
568 ivcal_get_preview (EImport *ei,
569 EImportTarget *target,
570 EImportImporter *im)
571 {
572 GtkWidget *preview;
573 EImportTargetURI *s = (EImportTargetURI *) target;
574 gchar *filename;
575 icalcomponent *icalcomp;
576 gchar *contents;
577
578 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
579 if (filename == NULL) {
580 g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
581 return NULL;
582 }
583
584 if (!g_file_get_contents (filename, &contents, NULL, NULL)) {
585 g_free (filename);
586 return NULL;
587 }
588 g_free (filename);
589
590 icalcomp = e_cal_util_parse_ics_string (contents);
591 g_free (contents);
592
593 if (!icalcomp)
594 return NULL;
595
596 preview = ical_get_preview (icalcomp);
597
598 icalcomponent_free (icalcomp);
599
600 return preview;
601 }
602
603 static EImportImporter ical_importer = {
604 E_IMPORT_TARGET_URI,
605 0,
606 ical_supported,
607 ivcal_getwidget,
608 ical_import,
609 ivcal_cancel,
610 ivcal_get_preview,
611 };
612
613 EImportImporter *
614 ical_importer_peek (void)
615 {
616 ical_importer.name = _("iCalendar files (.ics)");
617 ical_importer.description = _("Evolution iCalendar importer");
618
619 return &ical_importer;
620 }
621
622 /* ********************************************************************** */
623 /*
624 * vCalendar importer functions.
625 */
626
627 static gboolean
628 vcal_supported (EImport *ei,
629 EImportTarget *target,
630 EImportImporter *im)
631 {
632 gchar *filename;
633 gchar *contents;
634 gboolean ret = FALSE;
635 EImportTargetURI *s;
636
637 if (target->type != E_IMPORT_TARGET_URI)
638 return FALSE;
639
640 s = (EImportTargetURI *) target;
641 if (s->uri_src == NULL)
642 return TRUE;
643
644 if (strncmp (s->uri_src, "file:///", 8) != 0)
645 return FALSE;
646
647 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
648 if (!filename)
649 return FALSE;
650
651 /* Z: Wow, this is *efficient* */
652
653 if (g_file_get_contents (filename, &contents, NULL, NULL)) {
654 VObject *vcal;
655 icalcomponent *icalcomp;
656
657 icalcomp = e_cal_util_parse_ics_string (contents);
658
659 if (icalcomp && is_icalcomp_usable (icalcomp)) {
660 /* If we can create proper iCalendar from the file, then
661 * rather use ics importer, because it knows to read more
662 * information than older version, the vCalendar. */
663 ret = FALSE;
664 g_free (contents);
665 } else {
666 if (icalcomp)
667 icalcomponent_free (icalcomp);
668
669 /* parse the file */
670 vcal = Parse_MIME (contents, strlen (contents));
671 g_free (contents);
672
673 if (vcal) {
674 icalcomp = icalvcal_convert (vcal);
675
676 if (icalcomp) {
677 icalcomponent_free (icalcomp);
678 ret = TRUE;
679 }
680
681 cleanVObject (vcal);
682 }
683 }
684 }
685 g_free (filename);
686
687 return ret;
688 }
689
690 /* This tries to load in a vCalendar file and convert it to an icalcomponent.
691 * It returns NULL on failure. */
692 static icalcomponent *
693 load_vcalendar_file (const gchar *filename)
694 {
695 icalvcal_defaults defaults = { NULL };
696 icalcomponent *icalcomp = NULL;
697 gchar *contents;
698 gchar *default_alarm_filename;
699
700 default_alarm_filename = g_build_filename (
701 EVOLUTION_SOUNDDIR,
702 "default_alarm.wav",
703 NULL);
704 defaults.alarm_audio_url = g_filename_to_uri (
705 default_alarm_filename,
706 NULL, NULL);
707 g_free (default_alarm_filename);
708 defaults.alarm_audio_fmttype = (gchar *) "audio/x-wav";
709 defaults.alarm_description = (gchar *) _("Reminder!");
710
711 if (g_file_get_contents (filename, &contents, NULL, NULL)) {
712 VObject *vcal;
713
714 /* parse the file */
715 vcal = Parse_MIME (contents, strlen (contents));
716 g_free (contents);
717
718 if (vcal) {
719 icalcomp = icalvcal_convert_with_defaults (
720 vcal, &defaults);
721 cleanVObject (vcal);
722 }
723 }
724
725 return icalcomp;
726 }
727
728 static void
729 vcal_import (EImport *ei,
730 EImportTarget *target,
731 EImportImporter *im)
732 {
733 gchar *filename;
734 icalcomponent *icalcomp;
735 EImportTargetURI *s = (EImportTargetURI *) target;
736
737 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
738 if (!filename) {
739 e_import_complete (ei, target);
740 return;
741 }
742
743 icalcomp = load_vcalendar_file (filename);
744 g_free (filename);
745 if (icalcomp)
746 ivcal_import (ei, target, icalcomp);
747 else
748 e_import_complete (ei, target);
749 }
750
751 static GtkWidget *
752 vcal_get_preview (EImport *ei,
753 EImportTarget *target,
754 EImportImporter *im)
755 {
756 GtkWidget *preview;
757 EImportTargetURI *s = (EImportTargetURI *) target;
758 gchar *filename;
759 icalcomponent *icalcomp;
760
761 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
762 if (filename == NULL) {
763 g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
764 return NULL;
765 }
766
767 icalcomp = load_vcalendar_file (filename);
768 g_free (filename);
769
770 if (!icalcomp)
771 return NULL;
772
773 preview = ical_get_preview (icalcomp);
774
775 icalcomponent_free (icalcomp);
776
777 return preview;
778 }
779
780 static EImportImporter vcal_importer = {
781 E_IMPORT_TARGET_URI,
782 0,
783 vcal_supported,
784 ivcal_getwidget,
785 vcal_import,
786 ivcal_cancel,
787 vcal_get_preview,
788 };
789
790 EImportImporter *
791 vcal_importer_peek (void)
792 {
793 vcal_importer.name = _("vCalendar files (.vcs)");
794 vcal_importer.description = _("Evolution vCalendar importer");
795
796 return &vcal_importer;
797 }
798
799 /* ********************************************************************** */
800
801 static gboolean
802 gnome_calendar_supported (EImport *ei,
803 EImportTarget *target,
804 EImportImporter *im)
805 {
806 gchar *filename;
807 gboolean res;
808
809 if (target->type != E_IMPORT_TARGET_HOME)
810 return FALSE;
811
812 filename = g_build_filename (g_get_home_dir (), "user-cal.vcf", NULL);
813 res = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
814 g_free (filename);
815
816 return res;
817 }
818
819 static void
820 free_ici (gpointer ptr)
821 {
822 ICalIntelligentImporter *ici = ptr;
823
824 if (!ici)
825 return;
826
827 if (ici->icalcomp)
828 icalcomponent_free (ici->icalcomp);
829
830 g_object_unref (ici->cancellable);
831 g_free (ici);
832 }
833
834 struct OpenDefaultSourceData
835 {
836 ICalIntelligentImporter *ici;
837 void (* opened_cb) (ECalClient *cal_client, const GError *error, ICalIntelligentImporter *ici);
838 };
839
840 static void
841 default_source_opened_cb (GObject *source_object,
842 GAsyncResult *result,
843 gpointer user_data)
844 {
845 ESource *source = E_SOURCE (source_object);
846 EClient *client = NULL;
847 struct OpenDefaultSourceData *odsd = user_data;
848 GError *error = NULL;
849
850 g_return_if_fail (odsd != NULL);
851 g_return_if_fail (odsd->ici != NULL);
852 g_return_if_fail (odsd->opened_cb != NULL);
853
854 e_client_utils_open_new_finish (source, result, &client, &error);
855
856 /* Client may be NULL; don't use a type cast macro. */
857 odsd->opened_cb ((ECalClient *) client, error, odsd->ici);
858
859 if (client)
860 g_object_unref (client);
861 if (error)
862 g_error_free (error);
863
864 g_free (odsd);
865 }
866
867 static void
868 open_default_source (ICalIntelligentImporter *ici,
869 ECalClientSourceType source_type,
870 void (* opened_cb) (ECalClient *cal_client,
871 const GError *error,
872 ICalIntelligentImporter *ici))
873 {
874 EShell *shell;
875 ESource *source;
876 ESourceRegistry *registry;
877 EClientSourceType client_source_type;
878 struct OpenDefaultSourceData *odsd;
879
880 g_return_if_fail (ici != NULL);
881 g_return_if_fail (opened_cb != NULL);
882
883 shell = e_shell_get_default ();
884 registry = e_shell_get_registry (shell);
885
886 switch (source_type) {
887 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
888 client_source_type = E_CLIENT_SOURCE_TYPE_EVENTS;
889 source = e_source_registry_ref_default_calendar (registry);
890 break;
891 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
892 client_source_type = E_CLIENT_SOURCE_TYPE_TASKS;
893 source = e_source_registry_ref_default_task_list (registry);
894 break;
895 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
896 client_source_type = E_CLIENT_SOURCE_TYPE_MEMOS;
897 source = e_source_registry_ref_default_memo_list (registry);
898 break;
899 default:
900 g_return_if_reached ();
901 }
902
903 odsd = g_new0 (struct OpenDefaultSourceData, 1);
904 odsd->ici = ici;
905 odsd->opened_cb = opened_cb;
906
907 e_import_status (ici->ei, ici->target, _("Opening calendar"), 0);
908
909 e_client_utils_open_new (
910 source, client_source_type, FALSE, ici->cancellable,
911 default_source_opened_cb, odsd);
912
913 g_object_unref (source);
914 }
915
916 static void
917 continue_done_cb (gpointer user_data)
918 {
919 ICalIntelligentImporter *ici = user_data;
920
921 g_return_if_fail (ici != NULL);
922
923 e_import_complete (ici->ei, ici->target);
924 }
925
926 static void
927 gc_import_tasks (ECalClient *cal_client,
928 const GError *error,
929 ICalIntelligentImporter *ici)
930 {
931 g_return_if_fail (ici != NULL);
932
933 if (error != NULL) {
934 g_warning (
935 "%s: Failed to open tasks: %s",
936 G_STRFUNC, error->message);
937 e_import_complete (ici->ei, ici->target);
938 return;
939 }
940
941 e_import_status (ici->ei, ici->target, _("Importing..."), 0);
942
943 prepare_tasks (ici->icalcomp, ici->tasks);
944
945 update_objects (
946 cal_client, ici->icalcomp,
947 ici->cancellable, continue_done_cb, ici);
948 }
949
950 static void
951 continue_tasks_cb (gpointer user_data)
952 {
953 ICalIntelligentImporter *ici = user_data;
954
955 g_return_if_fail (ici != NULL);
956
957 open_default_source (ici, E_CAL_CLIENT_SOURCE_TYPE_TASKS, gc_import_tasks);
958 }
959
960 static void
961 gc_import_events (ECalClient *cal_client,
962 const GError *error,
963 ICalIntelligentImporter *ici)
964 {
965 g_return_if_fail (ici != NULL);
966
967 if (error != NULL) {
968 g_warning (
969 "%s: Failed to open events calendar: %s",
970 G_STRFUNC, error->message);
971 if (ici->tasks)
972 open_default_source (
973 ici, E_CAL_CLIENT_SOURCE_TYPE_TASKS,
974 gc_import_tasks);
975 else
976 e_import_complete (ici->ei, ici->target);
977 return;
978 }
979
980 e_import_status (ici->ei, ici->target, _("Importing..."), 0);
981
982 update_objects (
983 cal_client, ici->icalcomp, ici->cancellable,
984 ici->tasks ? continue_tasks_cb : continue_done_cb, ici);
985 }
986
987 static void
988 gnome_calendar_import (EImport *ei,
989 EImportTarget *target,
990 EImportImporter *im)
991 {
992 icalcomponent *icalcomp = NULL;
993 gchar *filename;
994 gint do_calendar, do_tasks;
995 ICalIntelligentImporter *ici;
996
997 /* This is pretty shitty, everything runs in the gui thread and can block
998 * for quite some time */
999
1000 do_calendar = GPOINTER_TO_INT (g_datalist_get_data (&target->data, "gnomecal-do-cal"));
1001 do_tasks = GPOINTER_TO_INT (g_datalist_get_data (&target->data, "gnomecal-do-tasks"));
1002
1003 /* If neither is selected, just return. */
1004 if (!do_calendar && !do_tasks)
1005 return;
1006
1007 /* Load the Gnome Calendar file and convert to iCalendar. */
1008 filename = g_build_filename (g_get_home_dir (), "user-cal.vcf", NULL);
1009 icalcomp = load_vcalendar_file (filename);
1010 g_free (filename);
1011
1012 /* If we couldn't load the file, just return. FIXME: Error message? */
1013 if (!icalcomp)
1014 goto out;
1015
1016 ici = g_malloc0 (sizeof (*ici));
1017 ici->ei = ei;
1018 ici->target = target;
1019 ici->cancellable = g_cancellable_new ();
1020 ici->icalcomp = icalcomp;
1021 icalcomp = NULL;
1022
1023 g_datalist_set_data_full (&target->data, "gnomecal-data", ici, free_ici);
1024
1025 prepare_events (ici->icalcomp, &ici->tasks);
1026 if (do_calendar) {
1027 open_default_source (ici, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, gc_import_events);
1028 return;
1029 }
1030
1031 prepare_tasks (ici->icalcomp, ici->tasks);
1032 if (do_tasks) {
1033 open_default_source (ici, E_CAL_CLIENT_SOURCE_TYPE_TASKS, gc_import_tasks);
1034 return;
1035 }
1036
1037 out:
1038 if (icalcomp)
1039 icalcomponent_free (icalcomp);
1040
1041 e_import_complete (ei, target);
1042 }
1043
1044 static void
1045 calendar_toggle_cb (GtkToggleButton *tb,
1046 EImportTarget *target)
1047 {
1048 g_datalist_set_data (&target->data, "gnomecal-do-cal", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
1049 }
1050
1051 static void
1052 tasks_toggle_cb (GtkToggleButton *tb,
1053 EImportTarget *target)
1054 {
1055 g_datalist_set_data (&target->data, "gnomecal-do-tasks", GINT_TO_POINTER (gtk_toggle_button_get_active (tb)));
1056 }
1057
1058 static GtkWidget *
1059 gnome_calendar_getwidget (EImport *ei,
1060 EImportTarget *target,
1061 EImportImporter *im)
1062 {
1063 GtkWidget *hbox, *w;
1064 GSettings *settings;
1065 gboolean done_cal, done_tasks;
1066
1067 settings = g_settings_new ("org.gnome.evolution.importer");
1068 done_cal = g_settings_get_boolean (settings, "gnome-calendar-done-calendar");
1069 done_tasks = g_settings_get_boolean (settings, "gnome-calendar-done-tasks");
1070 g_object_unref (settings);
1071
1072 g_datalist_set_data (&target->data, "gnomecal-do-cal", GINT_TO_POINTER (!done_cal));
1073 g_datalist_set_data (&target->data, "gnomecal-do-tasks", GINT_TO_POINTER (!done_tasks));
1074
1075 hbox = gtk_hbox_new (FALSE, 2);
1076
1077 w = gtk_check_button_new_with_label (_("Calendar Events"));
1078 gtk_toggle_button_set_active ((GtkToggleButton *) w, !done_cal);
1079 g_signal_connect (
1080 w, "toggled",
1081 G_CALLBACK (calendar_toggle_cb), target);
1082 gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
1083
1084 w = gtk_check_button_new_with_label (_("Tasks"));
1085 gtk_toggle_button_set_active ((GtkToggleButton *) w, !done_tasks);
1086 g_signal_connect (
1087 w, "toggled",
1088 G_CALLBACK (tasks_toggle_cb), target);
1089 gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
1090
1091 gtk_widget_show_all (hbox);
1092
1093 return hbox;
1094 }
1095
1096 static void
1097 gnome_calendar_cancel (EImport *ei,
1098 EImportTarget *target,
1099 EImportImporter *im)
1100 {
1101 ICalIntelligentImporter *ici = g_datalist_get_data (&target->data, "gnomecal-data");
1102
1103 if (ici)
1104 g_cancellable_cancel (ici->cancellable);
1105 }
1106
1107 static EImportImporter gnome_calendar_importer = {
1108 E_IMPORT_TARGET_HOME,
1109 0,
1110 gnome_calendar_supported,
1111 gnome_calendar_getwidget,
1112 gnome_calendar_import,
1113 gnome_calendar_cancel,
1114 NULL, /* get_preview */
1115 };
1116
1117 EImportImporter *
1118 gnome_calendar_importer_peek (void)
1119 {
1120 gnome_calendar_importer.name = _("Gnome Calendar");
1121 gnome_calendar_importer.description = _("Evolution Calendar intelligent importer");
1122
1123 return &gnome_calendar_importer;
1124 }
1125
1126 /* ********************************************************************** */
1127
1128 static gchar *
1129 format_dt (const ECalComponentDateTime *dt,
1130 GHashTable *timezones,
1131 icaltimezone *users_zone)
1132 {
1133 struct tm tm;
1134
1135 g_return_val_if_fail (dt != NULL, NULL);
1136 g_return_val_if_fail (timezones != NULL, NULL);
1137
1138 if (!dt->value)
1139 return NULL;
1140
1141 dt->value->zone = NULL;
1142 if (dt->tzid) {
1143 dt->value->zone = g_hash_table_lookup (timezones, dt->tzid);
1144 if (!dt->value->zone)
1145 dt->value->zone = icaltimezone_get_builtin_timezone_from_tzid (dt->tzid);
1146
1147 if (!dt->value->zone && g_ascii_strcasecmp (dt->tzid, "UTC") == 0)
1148 dt->value->zone = icaltimezone_get_utc_timezone ();
1149 }
1150
1151 if (dt->value->zone)
1152 tm = icaltimetype_to_tm_with_zone (dt->value, (icaltimezone *) dt->value->zone, users_zone);
1153 else
1154 tm = icaltimetype_to_tm (dt->value);
1155
1156 return e_datetime_format_format_tm ("calendar", "table", dt->value->is_date ? DTFormatKindDate : DTFormatKindDateTime, &tm);
1157 }
1158
1159 static const gchar *
1160 strip_mailto (const gchar *str)
1161 {
1162 if (!str || g_ascii_strncasecmp (str, "mailto:", 7) != 0)
1163 return str;
1164
1165 return str + 7;
1166 }
1167
1168 static void
1169 preview_comp (EWebViewPreview *preview,
1170 ECalComponent *comp)
1171 {
1172 ECalComponentText text = { 0 };
1173 ECalComponentDateTime dt;
1174 ECalComponentClassification classif;
1175 const gchar *str;
1176 gchar *tmp;
1177 gint percent;
1178 gboolean have;
1179 GHashTable *timezones;
1180 icaltimezone *users_zone;
1181 GSList *slist, *l;
1182
1183 g_return_if_fail (preview != NULL);
1184 g_return_if_fail (comp != NULL);
1185
1186 timezones = g_object_get_data (G_OBJECT (preview), "iCalImp-timezones");
1187 users_zone = g_object_get_data (G_OBJECT (preview), "iCalImp-userszone");
1188
1189 str = NULL;
1190 switch (e_cal_component_get_vtype (comp)) {
1191 case E_CAL_COMPONENT_EVENT:
1192 str = e_cal_component_has_attendees (comp) ? C_("iCalImp", "Meeting") : C_("iCalImp", "Event");
1193 break;
1194 case E_CAL_COMPONENT_TODO:
1195 str = C_("iCalImp", "Task");
1196 break;
1197 case E_CAL_COMPONENT_JOURNAL:
1198 str = C_("iCalImp", "Memo");
1199 break;
1200 default:
1201 str = "??? Other ???";
1202 break;
1203 }
1204
1205 have = FALSE;
1206 if (e_cal_component_has_recurrences (comp)) {
1207 e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "has recurrences"));
1208 have = TRUE;
1209 }
1210
1211 if (e_cal_component_is_instance (comp)) {
1212 e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "is an instance"));
1213 have = TRUE;
1214 }
1215
1216 if (e_cal_component_has_alarms (comp)) {
1217 e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "has reminders"));
1218 have = TRUE;
1219 }
1220
1221 if (e_cal_component_has_attachments (comp)) {
1222 e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "has attachments"));
1223 have = TRUE;
1224 }
1225
1226 if (!have) {
1227 e_web_view_preview_add_section (preview, str, "");
1228 }
1229
1230 str = NULL;
1231 classif = E_CAL_COMPONENT_CLASS_NONE;
1232 e_cal_component_get_classification (comp, &classif);
1233 if (classif == E_CAL_COMPONENT_CLASS_PUBLIC) {
1234 /* Translators: Appointment's classification */
1235 str = C_("iCalImp", "Public");
1236 } else if (classif == E_CAL_COMPONENT_CLASS_PRIVATE) {
1237 /* Translators: Appointment's classification */
1238 str = C_("iCalImp", "Private");
1239 } else if (classif == E_CAL_COMPONENT_CLASS_CONFIDENTIAL) {
1240 /* Translators: Appointment's classification */
1241 str = C_("iCalImp", "Confidential");
1242 }
1243 if (str)
1244 /* Translators: Appointment's classification section name */
1245 e_web_view_preview_add_section (preview, C_("iCalImp", "Classification"), str);
1246
1247 e_cal_component_get_summary (comp, &text);
1248 if ((text.value && *text.value) || (text.altrep && *text.altrep))
1249 /* Translators: Appointment's summary */
1250 e_web_view_preview_add_section (preview, C_("iCalImp", "Summary"), (text.value && *text.value) ? text.value : text.altrep);
1251
1252 str = NULL;
1253 e_cal_component_get_location (comp, &str);
1254 if (str && *str)
1255 /* Translators: Appointment's location */
1256 e_web_view_preview_add_section (preview, C_("iCalImp", "Location"), str);
1257
1258 dt.value = NULL;
1259 e_cal_component_get_dtstart (comp, &dt);
1260 if (dt.value) {
1261 tmp = format_dt (&dt, timezones, users_zone);
1262 if (tmp)
1263 /* Translators: Appointment's start time */
1264 e_web_view_preview_add_section (preview, C_("iCalImp", "Start"), tmp);
1265 g_free (tmp);
1266 }
1267 e_cal_component_free_datetime (&dt);
1268
1269 dt.value = NULL;
1270 e_cal_component_get_due (comp, &dt);
1271 if (dt.value) {
1272 tmp = format_dt (&dt, timezones, users_zone);
1273 if (tmp)
1274 /* Translators: 'Due' like the time due a task should be finished */
1275 e_web_view_preview_add_section (preview, C_("iCalImp", "Due"), tmp);
1276 g_free (tmp);
1277 } else {
1278 e_cal_component_free_datetime (&dt);
1279
1280 dt.value = NULL;
1281 e_cal_component_get_dtend (comp, &dt);
1282 if (dt.value) {
1283 tmp = format_dt (&dt, timezones, users_zone);
1284
1285 if (tmp)
1286 /* Translators: Appointment's end time */
1287 e_web_view_preview_add_section (preview, C_("iCalImp", "End"), tmp);
1288 g_free (tmp);
1289 }
1290 }
1291 e_cal_component_free_datetime (&dt);
1292
1293 str = NULL;
1294 e_cal_component_get_categories (comp, &str);
1295 if (str && *str)
1296 /* Translators: Appointment's categories */
1297 e_web_view_preview_add_section (preview, C_("iCalImp", "Categories"), str);
1298
1299 percent = e_cal_component_get_percent_as_int (comp);
1300 if (percent >= 0) {
1301 tmp = NULL;
1302 if (percent == 100) {
1303 icaltimetype *completed = NULL;
1304
1305 e_cal_component_get_completed (comp, &completed);
1306
1307 if (completed) {
1308 dt.tzid = "UTC";
1309 dt.value = completed;
1310
1311 tmp = format_dt (&dt, timezones, users_zone);
1312
1313 e_cal_component_free_icaltimetype (completed);
1314 }
1315 }
1316
1317 if (!tmp)
1318 tmp = g_strdup_printf ("%d%%", percent);
1319
1320 /* Translators: Appointment's complete value (either percentage, or a date/time of a completion) */
1321 e_web_view_preview_add_section (preview, C_("iCalImp", "Completed"), tmp);
1322 g_free (tmp);
1323 }
1324
1325 str = NULL;
1326 e_cal_component_get_url (comp, &str);
1327 if (str && *str)
1328 /* Translators: Appointment's URL */
1329 e_web_view_preview_add_section (preview, C_("iCalImp", "URL"), str);
1330
1331 if (e_cal_component_has_organizer (comp)) {
1332 ECalComponentOrganizer organizer = { 0 };
1333
1334 e_cal_component_get_organizer (comp, &organizer);
1335
1336 if (organizer.value && *organizer.value) {
1337 if (organizer.cn && *organizer.cn) {
1338 tmp = g_strconcat (organizer.cn, " <", strip_mailto (organizer.value), ">", NULL);
1339 /* Translators: Appointment's organizer */
1340 e_web_view_preview_add_section (preview, C_("iCalImp", "Organizer"), tmp);
1341 g_free (tmp);
1342 } else {
1343 e_web_view_preview_add_section (preview, C_("iCalImp", "Organizer"), strip_mailto (organizer.value));
1344 }
1345 }
1346 }
1347
1348 if (e_cal_component_has_attendees (comp)) {
1349 GSList *attendees = NULL, *a;
1350 have = FALSE;
1351
1352 e_cal_component_get_attendee_list (comp, &attendees);
1353
1354 for (a = attendees; a; a = a->next) {
1355 ECalComponentAttendee *attnd = a->data;
1356
1357 if (!attnd || !attnd->value || !*attnd->value)
1358 continue;
1359
1360 if (attnd->cn && *attnd->cn) {
1361 tmp = g_strconcat (attnd->cn, " <", strip_mailto (attnd->value), ">", NULL);
1362 /* Translators: Appointment's attendees */
1363 e_web_view_preview_add_section (preview, have ? NULL : C_("iCalImp", "Attendees"), tmp);
1364 g_free (tmp);
1365 } else {
1366 e_web_view_preview_add_section (preview, have ? NULL : C_("iCalImp", "Attendees"), strip_mailto (attnd->value));
1367 }
1368
1369 have = TRUE;
1370 }
1371
1372 e_cal_component_free_attendee_list (attendees);
1373 }
1374
1375 slist = NULL;
1376 e_cal_component_get_description_list (comp, &slist);
1377 for (l = slist; l; l = l->next) {
1378 ECalComponentText *txt = l->data;
1379
1380 e_web_view_preview_add_section (preview, l != slist ? NULL : C_("iCalImp", "Description"), (txt && txt->value) ? txt->value : "");
1381 }
1382
1383 e_cal_component_free_text_list (slist);
1384 }
1385
1386 static void
1387 preview_selection_changed_cb (GtkTreeSelection *selection,
1388 EWebViewPreview *preview)
1389 {
1390 GtkTreeIter iter;
1391 GtkTreeModel *model = NULL;
1392
1393 g_return_if_fail (selection != NULL);
1394 g_return_if_fail (preview != NULL);
1395
1396 e_web_view_preview_begin_update (preview);
1397
1398 if (gtk_tree_selection_get_selected (selection, &model, &iter) && model) {
1399 ECalComponent *comp = NULL;
1400
1401 gtk_tree_model_get (model, &iter, 3, &comp, -1);
1402
1403 if (comp) {
1404 preview_comp (preview, comp);
1405 g_object_unref (comp);
1406 }
1407 }
1408
1409 e_web_view_preview_end_update (preview);
1410 }
1411
1412 static icaltimezone *
1413 get_users_timezone (void)
1414 {
1415 /* more or less copy&paste of calendar_config_get_icaltimezone */
1416 EShell *shell;
1417 EShellSettings *shell_settings;
1418 icaltimezone *zone = NULL;
1419 gchar *location;
1420
1421 /* FIXME Pass this in. */
1422 shell = e_shell_get_default ();
1423 shell_settings = e_shell_get_shell_settings (shell);
1424
1425 if (e_shell_settings_get_boolean (shell_settings, "cal-use-system-timezone")) {
1426 location = e_cal_util_get_system_timezone_location ();
1427 } else {
1428 GSettings *settings = g_settings_new ("org.gnome.evolution.calendar");
1429
1430 location = g_settings_get_string (settings, "timezone");
1431
1432 g_object_unref (settings);
1433 }
1434
1435 if (location) {
1436 zone = icaltimezone_get_builtin_timezone (location);
1437
1438 g_free (location);
1439 }
1440
1441 return zone;
1442 }
1443
1444 static void
1445 free_zone_cb (gpointer ptr)
1446 {
1447 icaltimezone *zone = ptr;
1448
1449 if (zone)
1450 icaltimezone_free (zone, 1);
1451 }
1452
1453 static GtkWidget *
1454 ical_get_preview (icalcomponent *icalcomp)
1455 {
1456 GtkWidget *preview;
1457 GtkTreeView *tree_view;
1458 GtkTreeSelection *selection;
1459 GtkListStore *store;
1460 GtkTreeIter iter;
1461 GHashTable *timezones;
1462 icalcomponent *subcomp;
1463 icaltimezone *users_zone;
1464
1465 if (!icalcomp || !is_icalcomp_usable (icalcomp))
1466 return NULL;
1467
1468 store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, E_TYPE_CAL_COMPONENT);
1469
1470 timezones = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_zone_cb);
1471 users_zone = get_users_timezone ();
1472
1473 /* get timezones first */
1474 for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VTIMEZONE_COMPONENT);
1475 subcomp;
1476 subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VTIMEZONE_COMPONENT)) {
1477 icaltimezone *zone = icaltimezone_new ();
1478 if (!icaltimezone_set_component (zone, icalcomponent_new_clone (subcomp)) || !icaltimezone_get_tzid (zone)) {
1479 icaltimezone_free (zone, 1);
1480 } else {
1481 g_hash_table_insert (timezones, (gchar *) icaltimezone_get_tzid (zone), zone);
1482 }
1483 }
1484
1485 /* then each component */
1486 for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT);
1487 subcomp;
1488 subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT)) {
1489 icalcomponent_kind kind = icalcomponent_isa (subcomp);
1490
1491 if (kind == ICAL_VEVENT_COMPONENT ||
1492 kind == ICAL_VTODO_COMPONENT ||
1493 kind == ICAL_VJOURNAL_COMPONENT) {
1494 ECalComponent *comp = e_cal_component_new ();
1495 ECalComponentText summary = { 0 };
1496 ECalComponentDateTime dt = { 0 };
1497 gchar *formatted_dt;
1498
1499 if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp))) {
1500 g_object_unref (comp);
1501 continue;
1502 }
1503
1504 e_cal_component_get_summary (comp, &summary);
1505 e_cal_component_get_dtstart (comp, &dt);
1506 formatted_dt = format_dt (&dt, timezones, users_zone);
1507
1508 gtk_list_store_append (store, &iter);
1509 gtk_list_store_set (
1510 store, &iter,
1511 0, kind == ICAL_VEVENT_COMPONENT ? (e_cal_component_has_attendees (comp) ? C_("iCalImp", "Meeting") : C_("iCalImp", "Event")) :
1512 kind == ICAL_VTODO_COMPONENT ? C_("iCalImp", "Task") :
1513 kind == ICAL_VJOURNAL_COMPONENT ? C_("iCalImp", "Memo") : "??? Other ???",
1514 1, formatted_dt ? formatted_dt : "",
1515 2, summary.value && *summary.value ? summary.value : summary.altrep && *summary.altrep ? summary.altrep : "",
1516 3, comp,
1517 -1);
1518
1519 g_free (formatted_dt);
1520 e_cal_component_free_datetime (&dt);
1521 g_object_unref (comp);
1522 }
1523 }
1524
1525 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
1526 g_object_unref (store);
1527 g_hash_table_destroy (timezones);
1528 return NULL;
1529 }
1530
1531 preview = e_web_view_preview_new ();
1532 gtk_widget_show (preview);
1533
1534 g_object_set_data_full (G_OBJECT (preview), "iCalImp-timezones", timezones, (GDestroyNotify) g_hash_table_destroy);
1535 g_object_set_data (G_OBJECT (preview), "iCalImp-userszone", users_zone);
1536
1537 tree_view = e_web_view_preview_get_tree_view (E_WEB_VIEW_PREVIEW (preview));
1538 g_return_val_if_fail (tree_view != NULL, NULL);
1539
1540 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
1541 g_object_unref (store);
1542
1543 /* Translators: Column header for a component type; it can be Event, Task or Memo */
1544 gtk_tree_view_insert_column_with_attributes (
1545 tree_view, -1, C_("iCalImp", "Type"),
1546 gtk_cell_renderer_text_new (), "text", 0, NULL);
1547
1548 /* Translators: Column header for a component start date/time */
1549 gtk_tree_view_insert_column_with_attributes (
1550 tree_view, -1, C_("iCalImp", "Start"),
1551 gtk_cell_renderer_text_new (), "text", 1, NULL);
1552
1553 /* Translators: Column header for a component summary */
1554 gtk_tree_view_insert_column_with_attributes (
1555 tree_view, -1, C_("iCalImp", "Summary"),
1556 gtk_cell_renderer_text_new (), "text", 2, NULL);
1557
1558 if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1)
1559 e_web_view_preview_show_tree_view (E_WEB_VIEW_PREVIEW (preview));
1560
1561 selection = gtk_tree_view_get_selection (tree_view);
1562 gtk_tree_selection_select_iter (selection, &iter);
1563 g_signal_connect (
1564 selection, "changed",
1565 G_CALLBACK (preview_selection_changed_cb), preview);
1566
1567 preview_selection_changed_cb (selection, E_WEB_VIEW_PREVIEW (preview));
1568
1569 return preview;
1570 }