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 * Chris Toshok <toshok@ximian.com>
20 * JP Rosevear <jpr@ximian.com>
21 * Michael Zucchi <notzed@ximian.com>
22 *
23 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 *
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36 #include <glib/gstdio.h>
37
38 #include <libebook/libebook.h>
39 #include <libedataserverui/libedataserverui.h>
40
41 #include <util/eab-book-util.h>
42
43 #include <shell/e-shell.h>
44 #include <e-util/e-import.h>
45 #include <e-util/e-datetime-format.h>
46 #include <misc/e-web-view-preview.h>
47
48 #include "evolution-addressbook-importers.h"
49
50 enum _VCardEncoding {
51 VCARD_ENCODING_NONE,
52 VCARD_ENCODING_UTF8,
53 VCARD_ENCODING_UTF16,
54 VCARD_ENCODING_LOCALE
55 };
56
57 typedef enum _VCardEncoding VCardEncoding;
58
59 typedef struct {
60 EImport *import;
61 EImportTarget *target;
62
63 guint idle_id;
64
65 gint state; /* 0 - importing, 1 - cancelled/complete */
66 gint total;
67 gint count;
68
69 ESource *primary;
70
71 GSList *contactlist;
72 GSList *iterator;
73 EBookClient *book_client;
74
75 /* when opening book */
76 gchar *contents;
77 VCardEncoding encoding;
78 } VCardImporter;
79
80 static void vcard_import_done (VCardImporter *gci);
81
82 static void
83 add_to_notes (EContact *contact,
84 EContactField field)
85 {
86 const gchar *old_text;
87 const gchar *field_text;
88 gchar *new_text;
89
90 old_text = e_contact_get_const (contact, E_CONTACT_NOTE);
91 if (old_text && strstr (old_text, e_contact_pretty_name (field)))
92 return;
93
94 field_text = e_contact_get_const (contact, field);
95 if (!field_text || !*field_text)
96 return;
97
98 new_text = g_strdup_printf (
99 "%s%s%s: %s",
100 old_text ? old_text : "",
101 old_text && *old_text &&
102 *(old_text + strlen (old_text) - 1) != '\n' ? "\n" : "",
103 e_contact_pretty_name (field), field_text);
104 e_contact_set (contact, E_CONTACT_NOTE, new_text);
105 g_free (new_text);
106 }
107
108 static void
109 vcard_import_contact (VCardImporter *gci,
110 EContact *contact)
111 {
112 EContactPhoto *photo;
113 GList *attrs, *attr;
114 gchar *uid = NULL;
115
116 /* Apple's addressbook.app exports PHOTO's without a TYPE
117 * param, so let's figure out the format here if there's a
118 * PHOTO attribute missing a TYPE param.
119 *
120 * this is sort of a hack, as EContact sets the type for us if
121 * we use the setter. so let's e_contact_get + e_contact_set
122 * on E_CONTACT_PHOTO.
123 */
124 photo = e_contact_get (contact, E_CONTACT_PHOTO);
125 if (photo) {
126 e_contact_set (contact, E_CONTACT_PHOTO, photo);
127 e_contact_photo_free (photo);
128 }
129
130 /* Deal with our XML EDestination stuff in EMAIL attributes, if there is any. */
131 attrs = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
132 for (attr = attrs; attr; attr = attr->next) {
133 EVCardAttribute *a = attr->data;
134 GList *v = e_vcard_attribute_get_values (a);
135
136 if (v && v->data) {
137 if (!strncmp ((gchar *) v->data, "<?xml", 5)) {
138 EDestination *dest = e_destination_import ((gchar *) v->data);
139
140 e_destination_export_to_vcard_attribute (dest, a);
141
142 g_object_unref (dest);
143
144 }
145 }
146 }
147 e_contact_set_attributes (contact, E_CONTACT_EMAIL, attrs);
148
149 /* Deal with TEL attributes that don't conform to what we need.
150 *
151 * 1. if there's no location (HOME/WORK/OTHER), default to OTHER.
152 * 2. if there's *only* a location specified, default to VOICE.
153 */
154 attrs = e_vcard_get_attributes (E_VCARD (contact));
155 for (attr = attrs; attr; attr = attr->next) {
156 EVCardAttribute *a = attr->data;
157 gboolean location_only = TRUE;
158 gboolean no_location = TRUE;
159 gboolean is_work_home = FALSE;
160 GList *params, *param;
161
162 if (g_ascii_strcasecmp (e_vcard_attribute_get_name (a),
163 EVC_TEL))
164 continue;
165
166 params = e_vcard_attribute_get_params (a);
167 for (param = params; param; param = param->next) {
168 EVCardAttributeParam *p = param->data;
169 GList *vs, *v;
170
171 if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (p),
172 EVC_TYPE))
173 continue;
174
175 vs = e_vcard_attribute_param_get_values (p);
176 for (v = vs; v; v = v->next) {
177 is_work_home = is_work_home ||
178 !g_ascii_strcasecmp ((gchar *) v->data, "WORK") ||
179 !g_ascii_strcasecmp ((gchar *) v->data, "HOME");
180
181 if (!g_ascii_strcasecmp ((gchar *) v->data, "WORK") ||
182 !g_ascii_strcasecmp ((gchar *) v->data, "HOME") ||
183 !g_ascii_strcasecmp ((gchar *) v->data, "OTHER"))
184 no_location = FALSE;
185 else
186 location_only = FALSE;
187 }
188 }
189
190 if (is_work_home) {
191 /* only WORK and HOME phone numbers require locations,
192 * the rest should be kept as is */
193 if (location_only) {
194 /* add VOICE */
195 e_vcard_attribute_add_param_with_value (
196 a,
197 e_vcard_attribute_param_new (EVC_TYPE),
198 "VOICE");
199 }
200 if (no_location) {
201 /* add OTHER */
202 e_vcard_attribute_add_param_with_value (
203 a,
204 e_vcard_attribute_param_new (EVC_TYPE),
205 "OTHER");
206 }
207 }
208 }
209
210 /* Deal with ADR and EMAIL attributes that don't conform to what
211 * we need. If HOME or WORK isn't specified, add TYPE=OTHER. */
212 attrs = e_vcard_get_attributes (E_VCARD (contact));
213 for (attr = attrs; attr; attr = attr->next) {
214 EVCardAttribute *a = attr->data;
215 gboolean no_location = TRUE;
216 GList *params, *param;
217
218 if (g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_ADR) &&
219 g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_EMAIL))
220 continue;
221
222 params = e_vcard_attribute_get_params (a);
223 for (param = params; param; param = param->next) {
224 EVCardAttributeParam *p = param->data;
225 GList *vs, *v;
226
227 if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (p),
228 EVC_TYPE))
229 continue;
230
231 vs = e_vcard_attribute_param_get_values (p);
232 for (v = vs; v; v = v->next) {
233 if (!g_ascii_strcasecmp ((gchar *) v->data, "WORK") ||
234 !g_ascii_strcasecmp ((gchar *) v->data, "HOME"))
235 no_location = FALSE;
236 }
237 }
238
239 if (no_location) {
240 /* add OTHER */
241 e_vcard_attribute_add_param_with_value (
242 a,
243 e_vcard_attribute_param_new (EVC_TYPE),
244 "OTHER");
245 }
246 }
247
248 /* Work around the fact that these fields no longer show up in the UI */
249 add_to_notes (contact, E_CONTACT_OFFICE);
250 add_to_notes (contact, E_CONTACT_SPOUSE);
251 add_to_notes (contact, E_CONTACT_BLOG_URL);
252
253 /* FIXME Error checking */
254 if (e_book_client_add_contact_sync (gci->book_client, contact, &uid, NULL, NULL) && uid) {
255 e_contact_set (contact, E_CONTACT_UID, uid);
256 g_free (uid);
257 }
258 }
259
260 static gboolean
261 vcard_import_contacts (gpointer data)
262 {
263 VCardImporter *gci = data;
264 gint count = 0;
265 GSList *iterator = gci->iterator;
266
267 if (gci->state == 0) {
268 while (count < 50 && iterator) {
269 vcard_import_contact (gci, iterator->data);
270 count++;
271 iterator = iterator->next;
272 }
273 gci->count += count;
274 gci->iterator = iterator;
275 if (iterator == NULL)
276 gci->state = 1;
277 }
278 if (gci->state == 1) {
279 vcard_import_done (gci);
280 return FALSE;
281 } else {
282 e_import_status (
283 gci->import, gci->target, _("Importing..."),
284 gci->count * 100 / gci->total);
285 return TRUE;
286 }
287 }
288
289 #define BOM (gunichar2)0xFEFF
290 #define ANTIBOM (gunichar2)0xFFFE
291
292 static gboolean
293 has_bom (const gunichar2 *utf16)
294 {
295
296 if ((utf16 == NULL) || (*utf16 == '\0')) {
297 return FALSE;
298 }
299
300 return ((*utf16 == BOM) || (*utf16 == ANTIBOM));
301 }
302
303 static void
304 fix_utf16_endianness (gunichar2 *utf16)
305 {
306 gunichar2 *it;
307
308 if ((utf16 == NULL) || (*utf16 == '\0')) {
309 return;
310 }
311
312 if (*utf16 != ANTIBOM) {
313 return;
314 }
315
316 for (it = utf16; *it != '\0'; it++) {
317 *it = GUINT16_SWAP_LE_BE (*it);
318 }
319 }
320
321 /* Converts an UTF-16 string to an UTF-8 string removing the BOM character
322 * WARNING: this may modify the utf16 argument if the function detects the
323 * string isn't using the local endianness
324 */
325 static gchar *
326 utf16_to_utf8 (gunichar2 *utf16)
327 {
328
329 if (utf16 == NULL) {
330 return NULL;
331 }
332
333 fix_utf16_endianness (utf16);
334
335 if (*utf16 == BOM) {
336 utf16++;
337 }
338
339 return g_utf16_to_utf8 (utf16, -1, NULL, NULL, NULL);
340 }
341
342 /* Actually check the contents of this file */
343 static VCardEncoding
344 guess_vcard_encoding (const gchar *filename)
345 {
346 FILE *handle;
347 gchar line[4096];
348 gchar *line_utf8;
349 VCardEncoding encoding = VCARD_ENCODING_NONE;
350
351 handle = g_fopen (filename, "r");
352 if (handle == NULL) {
353 g_print ("\n");
354 return VCARD_ENCODING_NONE;
355 }
356
357 if (fgets (line, 4096, handle) == NULL) {
358 fclose (handle);
359 g_print ("\n");
360 return VCARD_ENCODING_NONE;
361 }
362 fclose (handle);
363
364 if (has_bom ((gunichar2 *) line)) {
365 gunichar2 *utf16 = (gunichar2 *) line;
366 /* Check for a BOM to try to detect UTF-16 encoded vcards
367 * (MacOSX address book creates such vcards for example)
368 */
369 line_utf8 = utf16_to_utf8 (utf16);
370 if (line_utf8 == NULL) {
371 return VCARD_ENCODING_NONE;
372 }
373 encoding = VCARD_ENCODING_UTF16;
374 } else if (g_utf8_validate (line, -1, NULL)) {
375 line_utf8 = g_strdup (line);
376 encoding = VCARD_ENCODING_UTF8;
377 } else {
378 line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL);
379 if (line_utf8 == NULL) {
380 return VCARD_ENCODING_NONE;
381 }
382 encoding = VCARD_ENCODING_LOCALE;
383 }
384
385 if (g_ascii_strncasecmp (line_utf8, "BEGIN:VCARD", 11) != 0) {
386 encoding = VCARD_ENCODING_NONE;
387 }
388
389 g_free (line_utf8);
390 return encoding;
391 }
392
393 static void
394 primary_selection_changed_cb (ESourceSelector *selector,
395 EImportTarget *target)
396 {
397 ESource *source;
398
399 source = e_source_selector_ref_primary_selection (selector);
400 g_return_if_fail (source != NULL);
401
402 g_datalist_set_data_full (
403 &target->data, "vcard-source",
404 source, (GDestroyNotify) g_object_unref);
405 }
406
407 static GtkWidget *
408 vcard_getwidget (EImport *ei,
409 EImportTarget *target,
410 EImportImporter *im)
411 {
412 EShell *shell;
413 GtkWidget *vbox, *selector;
414 ESourceRegistry *registry;
415 ESource *primary;
416 const gchar *extension_name;
417
418 vbox = gtk_vbox_new (FALSE, FALSE);
419
420 shell = e_shell_get_default ();
421 registry = e_shell_get_registry (shell);
422 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
423 selector = e_source_selector_new (registry, extension_name);
424 e_source_selector_set_show_toggles (
425 E_SOURCE_SELECTOR (selector), FALSE);
426 gtk_box_pack_start (GTK_BOX (vbox), selector, FALSE, TRUE, 6);
427
428 primary = g_datalist_get_data (&target->data, "vcard-source");
429 if (primary == NULL) {
430 GList *list;
431
432 list = e_source_registry_list_sources (registry, extension_name);
433 if (list != NULL) {
434 primary = g_object_ref (list->data);
435 g_datalist_set_data_full (
436 &target->data, "vcard-source", primary,
437 (GDestroyNotify) g_object_unref);
438 }
439
440 g_list_free_full (list, (GDestroyNotify) g_object_unref);
441 }
442 e_source_selector_set_primary_selection (
443 E_SOURCE_SELECTOR (selector), primary);
444
445 g_signal_connect (
446 selector, "primary_selection_changed",
447 G_CALLBACK (primary_selection_changed_cb), target);
448
449 gtk_widget_show_all (vbox);
450
451 return vbox;
452 }
453
454 static gboolean
455 vcard_supported (EImport *ei,
456 EImportTarget *target,
457 EImportImporter *im)
458 {
459 EImportTargetURI *s;
460 gchar *filename;
461 gboolean retval;
462
463 if (target->type != E_IMPORT_TARGET_URI)
464 return FALSE;
465
466 s = (EImportTargetURI *) target;
467 if (s->uri_src == NULL)
468 return TRUE;
469
470 if (strncmp (s->uri_src, "file:///", 8) != 0)
471 return FALSE;
472
473 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
474 if (filename == NULL)
475 return FALSE;
476 retval = (guess_vcard_encoding (filename) != VCARD_ENCODING_NONE);
477 g_free (filename);
478
479 return retval;
480 }
481
482 static void
483 vcard_import_done (VCardImporter *gci)
484 {
485 if (gci->idle_id)
486 g_source_remove (gci->idle_id);
487
488 g_free (gci->contents);
489 g_object_unref (gci->book_client);
490 e_client_util_free_object_slist (gci->contactlist);
491
492 e_import_complete (gci->import, gci->target);
493 g_object_unref (gci->import);
494 g_free (gci);
495 }
496
497 static void
498 book_loaded_cb (GObject *source_object,
499 GAsyncResult *result,
500 gpointer user_data)
501 {
502 ESource *source = E_SOURCE (source_object);
503 VCardImporter *gci = user_data;
504 EClient *client = NULL;
505
506 e_client_utils_open_new_finish (source, result, &client, NULL);
507
508 if (client == NULL) {
509 vcard_import_done (gci);
510 return;
511 }
512
513 gci->book_client = E_BOOK_CLIENT (client);
514
515 if (gci->encoding == VCARD_ENCODING_UTF16) {
516 gchar *tmp;
517
518 gunichar2 *contents_utf16 = (gunichar2 *) gci->contents;
519 tmp = utf16_to_utf8 (contents_utf16);
520 g_free (gci->contents);
521 gci->contents = tmp;
522
523 } else if (gci->encoding == VCARD_ENCODING_LOCALE) {
524 gchar *tmp;
525 tmp = g_locale_to_utf8 (gci->contents, -1, NULL, NULL, NULL);
526 g_free (gci->contents);
527 gci->contents = tmp;
528 }
529
530 gci->contactlist = eab_contact_list_from_string (gci->contents);
531 g_free (gci->contents);
532 gci->contents = NULL;
533 gci->iterator = gci->contactlist;
534 gci->total = g_slist_length (gci->contactlist);
535
536 if (gci->iterator)
537 gci->idle_id = g_idle_add (vcard_import_contacts, gci);
538 else
539 vcard_import_done (gci);
540 }
541
542 static void
543 vcard_import (EImport *ei,
544 EImportTarget *target,
545 EImportImporter *im)
546 {
547 VCardImporter *gci;
548 ESource *source;
549 EImportTargetURI *s = (EImportTargetURI *) target;
550 gchar *filename;
551 gchar *contents;
552 VCardEncoding encoding;
553
554 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
555 if (filename == NULL) {
556 g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
557 e_import_complete (ei, target);
558 return;
559 }
560 encoding = guess_vcard_encoding (filename);
561 if (encoding == VCARD_ENCODING_NONE) {
562 g_free (filename);
563 /* This check is superfluous, we've already
564 * checked otherwise we can't get here ... */
565 e_import_complete (ei, target);
566 return;
567 }
568
569 if (!g_file_get_contents (filename, &contents, NULL, NULL)) {
570 g_message (G_STRLOC ":Couldn't read file.");
571 g_free (filename);
572 e_import_complete (ei, target);
573 return;
574 }
575
576 g_free (filename);
577 gci = g_malloc0 (sizeof (*gci));
578 g_datalist_set_data (&target->data, "vcard-data", gci);
579 gci->import = g_object_ref (ei);
580 gci->target = target;
581 gci->encoding = encoding;
582 gci->contents = contents;
583
584 source = g_datalist_get_data (&target->data, "vcard-source");
585
586 e_client_utils_open_new (
587 source, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
588 book_loaded_cb, gci);
589 }
590
591 static void
592 vcard_cancel (EImport *ei,
593 EImportTarget *target,
594 EImportImporter *im)
595 {
596 VCardImporter *gci = g_datalist_get_data (&target->data, "vcard-data");
597
598 if (gci)
599 gci->state = 1;
600 }
601
602 static GtkWidget *
603 vcard_get_preview (EImport *ei,
604 EImportTarget *target,
605 EImportImporter *im)
606 {
607 GtkWidget *preview;
608 GSList *contacts;
609 gchar *contents;
610 VCardEncoding encoding;
611 EImportTargetURI *s = (EImportTargetURI *) target;
612 gchar *filename;
613
614 filename = g_filename_from_uri (s->uri_src, NULL, NULL);
615 if (filename == NULL) {
616 g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
617 return NULL;
618 }
619
620 encoding = guess_vcard_encoding (filename);
621 if (encoding == VCARD_ENCODING_NONE) {
622 g_free (filename);
623 return NULL;
624 }
625
626 if (!g_file_get_contents (filename, &contents, NULL, NULL)) {
627 g_message (G_STRLOC ": Couldn't read file.");
628 g_free (filename);
629 return NULL;
630 }
631
632 g_free (filename);
633
634 if (encoding == VCARD_ENCODING_UTF16) {
635 gchar *tmp;
636
637 gunichar2 *contents_utf16 = (gunichar2 *) contents;
638 tmp = utf16_to_utf8 (contents_utf16);
639 g_free (contents);
640 contents = tmp;
641 } else if (encoding == VCARD_ENCODING_LOCALE) {
642 gchar *tmp;
643 tmp = g_locale_to_utf8 (contents, -1, NULL, NULL, NULL);
644 g_free (contents);
645 contents = tmp;
646 }
647
648 contacts = eab_contact_list_from_string (contents);
649 g_free (contents);
650
651 preview = evolution_contact_importer_get_preview_widget (contacts);
652
653 e_client_util_free_object_slist (contacts);
654
655 return preview;
656 }
657
658 static EImportImporter vcard_importer = {
659 E_IMPORT_TARGET_URI,
660 0,
661 vcard_supported,
662 vcard_getwidget,
663 vcard_import,
664 vcard_cancel,
665 vcard_get_preview,
666 };
667
668 EImportImporter *
669 evolution_vcard_importer_peek (void)
670 {
671 vcard_importer.name = _("vCard (.vcf, .gcrd)");
672 vcard_importer.description = _("Evolution vCard Importer");
673
674 return &vcard_importer;
675 }
676
677 /* utility functions shared between all contact importers */
678 static void
679 preview_contact (EWebViewPreview *preview,
680 EContact *contact)
681 {
682 gint idx;
683 gboolean had_value = FALSE;
684
685 const gint fields[] = {
686 E_CONTACT_FILE_AS,
687 E_CONTACT_CATEGORIES,
688
689 E_CONTACT_IS_LIST,
690 E_CONTACT_LIST_SHOW_ADDRESSES,
691 E_CONTACT_WANTS_HTML,
692
693 E_CONTACT_FULL_NAME,
694 E_CONTACT_GIVEN_NAME,
695 E_CONTACT_FAMILY_NAME,
696 E_CONTACT_NICKNAME,
697 E_CONTACT_SPOUSE,
698 E_CONTACT_BIRTH_DATE,
699 E_CONTACT_ANNIVERSARY,
700 E_CONTACT_MAILER,
701 E_CONTACT_EMAIL,
702
703 -1,
704
705 E_CONTACT_ORG,
706 E_CONTACT_ORG_UNIT,
707 E_CONTACT_OFFICE,
708 E_CONTACT_TITLE,
709 E_CONTACT_ROLE,
710 E_CONTACT_MANAGER,
711 E_CONTACT_ASSISTANT,
712
713 -1,
714
715 E_CONTACT_PHONE_ASSISTANT,
716 E_CONTACT_PHONE_BUSINESS,
717 E_CONTACT_PHONE_BUSINESS_2,
718 E_CONTACT_PHONE_BUSINESS_FAX,
719 E_CONTACT_PHONE_CALLBACK,
720 E_CONTACT_PHONE_CAR,
721 E_CONTACT_PHONE_COMPANY,
722 E_CONTACT_PHONE_HOME,
723 E_CONTACT_PHONE_HOME_2,
724 E_CONTACT_PHONE_HOME_FAX,
725 E_CONTACT_PHONE_ISDN,
726 E_CONTACT_PHONE_MOBILE,
727 E_CONTACT_PHONE_OTHER,
728 E_CONTACT_PHONE_OTHER_FAX,
729 E_CONTACT_PHONE_PAGER,
730 E_CONTACT_PHONE_PRIMARY,
731 E_CONTACT_PHONE_RADIO,
732 E_CONTACT_PHONE_TELEX,
733 E_CONTACT_PHONE_TTYTDD,
734
735 -1,
736
737 E_CONTACT_ADDRESS_HOME,
738 E_CONTACT_ADDRESS_WORK,
739 E_CONTACT_ADDRESS_OTHER,
740
741 -1,
742
743 E_CONTACT_HOMEPAGE_URL,
744 E_CONTACT_BLOG_URL,
745 E_CONTACT_CALENDAR_URI,
746 E_CONTACT_FREEBUSY_URL,
747 E_CONTACT_ICS_CALENDAR,
748 E_CONTACT_VIDEO_URL,
749
750 -1,
751
752 E_CONTACT_IM_AIM,
753 E_CONTACT_IM_GROUPWISE,
754 E_CONTACT_IM_JABBER,
755 E_CONTACT_IM_YAHOO,
756 E_CONTACT_IM_MSN,
757 E_CONTACT_IM_ICQ,
758 E_CONTACT_IM_GADUGADU,
759 E_CONTACT_IM_SKYPE,
760 E_CONTACT_IM_TWITTER,
761
762 -1,
763
764 E_CONTACT_NOTE
765 };
766
767 g_return_if_fail (preview != NULL);
768 g_return_if_fail (contact != NULL);
769
770 for (idx = 0; idx < G_N_ELEMENTS (fields); idx++) {
771 EContactField field;
772
773 if (fields[idx] == -1) {
774 if (had_value)
775 e_web_view_preview_add_empty_line (preview);
776 had_value = FALSE;
777 continue;
778 }
779
780 field = fields[idx];
781
782 if (field == E_CONTACT_BIRTH_DATE || field == E_CONTACT_ANNIVERSARY) {
783 EContactDate *dt = e_contact_get (contact, field);
784 if (dt) {
785 GDate gd = { 0 };
786 struct tm tm;
787 gchar *value;
788
789 g_date_set_dmy (&gd, dt->day, dt->month, dt->year);
790 g_date_to_struct_tm (&gd, &tm);
791
792 value = e_datetime_format_format_tm (
793 "addressbook", "table",
794 DTFormatKindDate, &tm);
795 if (value) {
796 e_web_view_preview_add_section (
797 preview,
798 e_contact_pretty_name (field),
799 value);
800 had_value = TRUE;
801 }
802
803 g_free (value);
804 e_contact_date_free (dt);
805 }
806 } else if (field == E_CONTACT_IS_LIST ||
807 field == E_CONTACT_WANTS_HTML ||
808 field == E_CONTACT_LIST_SHOW_ADDRESSES) {
809 if (e_contact_get (contact, field)) {
810 e_web_view_preview_add_text (
811 preview, e_contact_pretty_name (field));
812 had_value = TRUE;
813 }
814 } else if (field == E_CONTACT_ADDRESS_HOME ||
815 field == E_CONTACT_ADDRESS_WORK ||
816 field == E_CONTACT_ADDRESS_OTHER) {
817 EContactAddress *addr = e_contact_get (contact, field);
818 if (addr) {
819 gboolean have = FALSE;
820
821 #define add_it(_what) \
822 if (addr->_what && *addr->_what) { \
823 e_web_view_preview_add_section ( \
824 preview, have ? NULL : \
825 e_contact_pretty_name (field), addr->_what); \
826 have = TRUE; \
827 had_value = TRUE; \
828 }
829
830 add_it (po);
831 add_it (ext);
832 add_it (street);
833 add_it (locality);
834 add_it (region);
835 add_it (code);
836 add_it (country);
837
838 #undef add_it
839
840 e_contact_address_free (addr);
841 }
842 } else if (field == E_CONTACT_IM_AIM ||
843 field == E_CONTACT_IM_GROUPWISE ||
844 field == E_CONTACT_IM_JABBER ||
845 field == E_CONTACT_IM_YAHOO ||
846 field == E_CONTACT_IM_MSN ||
847 field == E_CONTACT_IM_ICQ ||
848 field == E_CONTACT_IM_GADUGADU ||
849 field == E_CONTACT_IM_SKYPE ||
850 field == E_CONTACT_IM_TWITTER ||
851 field == E_CONTACT_EMAIL) {
852 GList *attrs, *a;
853 gboolean have = FALSE;
854 const gchar *pretty_name;
855
856 pretty_name = e_contact_pretty_name (field);
857
858 attrs = e_contact_get_attributes (contact, field);
859 for (a = attrs; a; a = a->next) {
860 EVCardAttribute *attr = a->data;
861 GList *value;
862
863 if (!attr)
864 continue;
865
866 value = e_vcard_attribute_get_values (attr);
867
868 while (value != NULL) {
869 const gchar *str = value->data;
870
871 if (str && *str) {
872 e_web_view_preview_add_section (
873 preview, have ? NULL :
874 pretty_name, str);
875 have = TRUE;
876 had_value = TRUE;
877 }
878
879 value = value->next;
880 }
881
882 e_vcard_attribute_free (attr);
883 }
884
885 g_list_free (attrs);
886
887 } else if (field == E_CONTACT_CATEGORIES) {
888 const gchar *pretty_name;
889 const gchar *value;
890
891 pretty_name = e_contact_pretty_name (field);
892 value = e_contact_get_const (contact, field);
893
894 if (value != NULL && *value != '\0') {
895 e_web_view_preview_add_section (
896 preview, pretty_name, value);
897 had_value = TRUE;
898 }
899
900 } else {
901 const gchar *pretty_name;
902 const gchar *value;
903
904 pretty_name = e_contact_pretty_name (field);
905 value = e_contact_get_const (contact, field);
906
907 if (value != NULL && *value != '\0') {
908 e_web_view_preview_add_section (
909 preview, pretty_name, value);
910 had_value = TRUE;
911 }
912 }
913 }
914 }
915
916 static void
917 preview_selection_changed_cb (GtkTreeSelection *selection,
918 EWebViewPreview *preview)
919 {
920 GtkTreeIter iter;
921 GtkTreeModel *model = NULL;
922
923 g_return_if_fail (selection != NULL);
924 g_return_if_fail (preview != NULL);
925
926 e_web_view_preview_begin_update (preview);
927
928 if (gtk_tree_selection_get_selected (selection, &model, &iter) && model) {
929 EContact *contact = NULL;
930
931 gtk_tree_model_get (model, &iter, 1, &contact, -1);
932
933 if (contact) {
934 preview_contact (preview, contact);
935 g_object_unref (contact);
936 }
937 }
938
939 e_web_view_preview_end_update (preview);
940 }
941
942 GtkWidget *
943 evolution_contact_importer_get_preview_widget (const GSList *contacts)
944 {
945 GtkWidget *preview;
946 GtkTreeView *tree_view;
947 GtkTreeSelection *selection;
948 GtkListStore *store;
949 GtkTreeIter iter;
950 const GSList *c;
951
952 if (!contacts)
953 return NULL;
954
955 store = gtk_list_store_new (2, G_TYPE_STRING, E_TYPE_CONTACT);
956
957 for (c = contacts; c; c = c->next) {
958 const gchar *description;
959 gchar *free_description = NULL;
960 EContact *contact = (EContact *) c->data;
961
962 if (!contact || !E_IS_CONTACT (contact))
963 continue;
964
965 description = e_contact_get_const (contact, E_CONTACT_FILE_AS);
966 if (!description)
967 description = e_contact_get_const (contact, E_CONTACT_UID);
968 if (!description)
969 description = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
970 if (!description) {
971 description = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
972 if (description) {
973 const gchar *at = strchr (description, '@');
974 if (at) {
975 free_description = g_strndup (
976 description,
977 (gsize) (at - description));
978 description = free_description;
979 }
980 }
981 }
982
983 gtk_list_store_append (store, &iter);
984 gtk_list_store_set (
985 store, &iter,
986 0, description ? description : "",
987 1, contact,
988 -1);
989
990 g_free (free_description);
991 }
992
993 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
994 g_object_unref (store);
995 return NULL;
996 }
997
998 preview = e_web_view_preview_new ();
999 gtk_widget_show (preview);
1000
1001 tree_view = e_web_view_preview_get_tree_view (E_WEB_VIEW_PREVIEW (preview));
1002 g_return_val_if_fail (tree_view != NULL, NULL);
1003
1004 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
1005 g_object_unref (store);
1006
1007 gtk_tree_view_insert_column_with_attributes (
1008 tree_view, -1, _("Contact"),
1009 gtk_cell_renderer_text_new (), "text", 0, NULL);
1010
1011 if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1)
1012 e_web_view_preview_show_tree_view (E_WEB_VIEW_PREVIEW (preview));
1013
1014 selection = gtk_tree_view_get_selection (tree_view);
1015 gtk_tree_selection_select_iter (selection, &iter);
1016 g_signal_connect (
1017 selection, "changed",
1018 G_CALLBACK (preview_selection_changed_cb), preview);
1019
1020 preview_selection_changed_cb (selection, E_WEB_VIEW_PREVIEW (preview));
1021
1022 return preview;
1023 }