3dd14b9aaa95d79664b2aa2118f4bb567fb93bb2
[profile/ivi/lemolo.git] / utils / contacts.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5 #include <Eet.h>
6 #include <Eina.h>
7
8 #include "log.h"
9 #include "ofono.h"
10 #include "contacts-ofono-efl.h"
11 #include "util.h"
12
13 #ifndef EET_COMPRESSION_DEFAULT
14 #define EET_COMPRESSION_DEFAULT 1
15 #endif
16
17 #define CONTACTS_ENTRY "contacts"
18
19 typedef struct _Contacts_List {
20         Eina_List *list;
21         Eina_Bool dirty;
22         Ecore_Poller *save_poller;
23 } Contacts_List;
24
25 typedef struct _Contacts {
26         char *path;
27         char *bkp;
28         Eet_Data_Descriptor *edd;
29         Eet_Data_Descriptor *edd_list;
30         Elm_Genlist_Item_Class *itc, *group;
31         Evas_Object *genlist, *layout, *details;
32         Contacts_List *c_list;
33 } Contacts;
34
35 struct _Contact_Info {
36         const char *first_name;
37         const char *last_name;
38         const char *full_name; /* not in edd */
39         const char *mobile;
40         const char *home;
41         const char *work;
42         const char *picture;
43
44         Contacts *contacts; /* not in edd */
45         Elm_Object_Item *it; /* not in edd */
46         Eina_Inlist *on_del_cbs; /* not in edd */
47         Ecore_Idler *changed_idler; /* not in edd */
48         struct {
49                 Eina_Inlist *listeners;
50                 Eina_List *deleted;
51                 int walking;
52         } on_changed_cbs; /* not in edd */
53 };
54
55 typedef struct _Contact_Info_On_Del_Ctx {
56         EINA_INLIST;
57         void (*cb)(void *, const Contact_Info *);
58         const void *data;
59 } Contact_Info_On_Del_Ctx;
60
61 typedef struct _Contact_Info_On_Changed_Ctx {
62         EINA_INLIST;
63         void (*cb)(void *, Contact_Info *);
64         const void *data;
65         Eina_Bool deleted;
66 } Contact_Info_On_Changed_Ctx;
67
68 Contact_Info *contact_search(Evas_Object *obj, const char *number, const char **type)
69 {
70         Contact_Info *c_info;
71         Eina_List *l;
72         Contacts *contacts;
73
74         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
75         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
76         contacts = evas_object_data_get(obj, "contacts.ctx");
77         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
78
79         EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
80                 if (strcmp(number, c_info->mobile) == 0) {
81                         if (type)
82                                 *type = "Mobile";
83                         return c_info;
84                 } else if (strcmp(number, c_info->work) == 0) {
85                         if (type)
86                                 *type = "Work";
87                         return c_info;
88                 } else if (strcmp(number, c_info->home) == 0) {
89                         if (type)
90                                 *type = "Home";
91                         return c_info;
92                 }
93         }
94
95         return NULL;
96 }
97
98 const char *contact_info_full_name_get(const Contact_Info *c)
99 {
100         Contact_Info *c2;
101
102         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
103
104         if (c->full_name)
105                 return c->full_name;
106
107         c2 = (Contact_Info *)c;
108         c2->full_name = eina_stringshare_printf("%s %s",
109                                                 c->first_name, c->last_name);
110         return c->full_name;
111 }
112
113 const char *contact_info_first_name_get(const Contact_Info *c)
114 {
115         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
116         return c->first_name;
117 }
118
119 const char *contact_info_last_name_get(const Contact_Info *c)
120 {
121         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
122         return c->last_name;
123 }
124
125 const char *contact_info_picture_get(const Contact_Info *c)
126 {
127         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
128         return c->picture;
129 }
130
131 const char *contact_info_detail_get(const Contact_Info *c, const char *type)
132 {
133         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
134         EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
135         if (strcmp(type, "Mobile") == 0)
136                 return c->mobile;
137         else if (strcmp(type, "Work") == 0)
138                 return c->work;
139         else if (strcmp(type, "Home") == 0)
140                 return c->home;
141         else
142                 return NULL;
143 }
144
145 const char *contact_info_number_check(const Contact_Info *c, const char *number)
146 {
147         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
148         EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE);
149
150         if (strcmp(number, c->mobile) == 0)
151                 return "Mobile";
152         else if (strcmp(number, c->work) == 0)
153                 return "Work";
154         else if (strcmp(number, c->home) == 0)
155                 return "Home";
156
157         return NULL;
158 }
159
160 static Eina_Bool _contacts_save_do(void *data)
161 {
162         Contacts *contacts = data;
163         Eet_File *efile;
164
165         contacts->c_list->save_poller = NULL;
166         contacts->c_list->dirty = EINA_FALSE;
167
168         ecore_file_unlink(contacts->bkp);
169         ecore_file_mv(contacts->path, contacts->bkp);
170         efile = eet_open(contacts->path, EET_FILE_MODE_WRITE);
171         EINA_SAFETY_ON_NULL_GOTO(efile, failed);
172         if (!(eet_data_write(efile,
173                                 contacts->edd_list, CONTACTS_ENTRY,
174                                 contacts->c_list, EET_COMPRESSION_DEFAULT)))
175                 ERR("Could in the contacts database");
176
177         DBG("wrote %s", contacts->path);
178         eet_close(efile);
179         return EINA_FALSE;
180
181 failed:
182         ecore_file_mv(contacts->bkp, contacts->path);
183         return EINA_FALSE;
184 }
185
186 static void _contacts_save(Contacts *contacts)
187 {
188         if (contacts->c_list->save_poller)
189                 return;
190
191         contacts->c_list->save_poller = ecore_poller_add
192                 (ECORE_POLLER_CORE, 32, _contacts_save_do, contacts);
193 }
194
195 static Eina_Bool _contact_info_changed_idler(void *data)
196 {
197         Contact_Info *c = data;
198         Contact_Info_On_Changed_Ctx *ctx;
199
200         c->changed_idler = NULL;
201
202         c->on_changed_cbs.walking++;
203         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
204                 if (ctx->deleted)
205                         continue;
206                 ctx->cb((void *)ctx->data, c);
207         }
208         c->on_changed_cbs.walking--;
209
210         _contacts_save(c->contacts);
211
212         if (c->on_changed_cbs.walking > 0)
213                 return EINA_FALSE;
214
215         EINA_LIST_FREE(c->on_changed_cbs.deleted, ctx) {
216                 c->on_changed_cbs.listeners = eina_inlist_remove(
217                         c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
218                 free(ctx);
219         }
220
221         return EINA_FALSE;
222 }
223
224 static void _contact_info_changed(Contact_Info *c)
225 {
226         if (c->changed_idler)
227                 return;
228         c->changed_idler = ecore_idler_add(_contact_info_changed_idler, c);
229 }
230
231 Eina_Bool contact_info_picture_set(Contact_Info *c, const char *filename)
232 {
233         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
234         EINA_SAFETY_ON_NULL_RETURN_VAL(filename, EINA_FALSE);
235
236         DBG("c=%p, was=%s, new=%s", c, c->picture, filename);
237
238         if (eina_stringshare_replace(&(c->picture), filename))
239                 _contact_info_changed(c);
240
241         return EINA_TRUE;
242 }
243
244 Eina_Bool contact_info_first_name_set(Contact_Info *c, const char *name)
245 {
246         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
247         EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
248
249         DBG("c=%p, was=%s, new=%s", c, c->first_name, name);
250
251         if (eina_stringshare_replace(&(c->first_name), name)) {
252                 eina_stringshare_replace(&(c->full_name), NULL);
253                 _contact_info_changed(c);
254         }
255
256         return EINA_TRUE;
257 }
258
259 Eina_Bool contact_info_last_name_set(Contact_Info *c, const char *name)
260 {
261         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
262         EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
263
264         DBG("c=%p, was=%s, new=%s", c, c->last_name, name);
265
266         if (eina_stringshare_replace(&(c->last_name), name)) {
267                 eina_stringshare_replace(&(c->full_name), NULL);
268                 _contact_info_changed(c);
269         }
270
271         return EINA_TRUE;
272 }
273
274 Eina_Bool contact_info_detail_set(Contact_Info *c, const char *type, const char *number)
275 {
276         Eina_Bool changed = EINA_FALSE;
277
278         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
279         EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE);
280         EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE);
281
282         DBG("c=%p, type=%s, number=%s", c, type, number);
283
284         if (strcmp(type, "Mobile") == 0)
285                 changed = eina_stringshare_replace(&(c->mobile), number);
286         else if (strcmp(type, "Work") == 0)
287                 changed = eina_stringshare_replace(&(c->work), number);
288         else if (strcmp(type, "Home") == 0)
289                 changed = eina_stringshare_replace(&(c->home), number);
290         else {
291                 ERR("Unknown type: %s, number: %s", type, number);
292                 return EINA_FALSE;
293         }
294
295         if (changed)
296                 _contact_info_changed(c);
297
298         return EINA_TRUE;
299
300 }
301
302 void contact_info_on_changed_callback_add(Contact_Info *c,
303                                         void (*cb)(void *data, Contact_Info *c),
304                                         const void *data)
305 {
306         Contact_Info_On_Changed_Ctx *ctx;
307
308         EINA_SAFETY_ON_NULL_RETURN(c);
309         EINA_SAFETY_ON_NULL_RETURN(cb);
310
311         ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
312         EINA_SAFETY_ON_NULL_RETURN(ctx);
313         ctx->cb = cb;
314         ctx->data = data;
315
316         c->on_changed_cbs.listeners = eina_inlist_append(
317                 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
318 }
319
320 void contact_info_on_changed_callback_del(Contact_Info *c,
321                                         void (*cb)(void *data, Contact_Info *c),
322                                         const void *data)
323 {
324         Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
325         EINA_SAFETY_ON_NULL_RETURN(c);
326         EINA_SAFETY_ON_NULL_RETURN(cb);
327
328         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
329                 if (ctx->cb == cb && ctx->data == data) {
330                         found = ctx;
331                         break;
332                 }
333         }
334
335         if (!found)
336                 return;
337
338         if (c->on_changed_cbs.walking > 0) {
339                 found->deleted = EINA_TRUE;
340                 c->on_changed_cbs.deleted = eina_list_append(
341                         c->on_changed_cbs.deleted, found);
342                 return;
343         }
344
345         c->on_changed_cbs.listeners = eina_inlist_remove(
346                 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
347         free(found);
348 }
349
350
351 void contact_info_on_del_callback_add(Contact_Info *c,
352                                         void (*cb)(void *data, const Contact_Info *c),
353                                         const void *data)
354 {
355         Contact_Info_On_Del_Ctx *ctx;
356
357         EINA_SAFETY_ON_NULL_RETURN(c);
358         EINA_SAFETY_ON_NULL_RETURN(cb);
359
360         ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
361         EINA_SAFETY_ON_NULL_RETURN(ctx);
362         ctx->cb = cb;
363         ctx->data = data;
364
365         c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
366 }
367
368 void contact_info_on_del_callback_del(Contact_Info *c,
369                                         void (*cb)(void *data, const Contact_Info *c),
370                                         const void *data)
371 {
372         Contact_Info_On_Del_Ctx *ctx, *found = NULL;
373         EINA_SAFETY_ON_NULL_RETURN(c);
374         EINA_SAFETY_ON_NULL_RETURN(cb);
375
376         EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
377                 if (ctx->cb == cb && ctx->data == data) {
378                         found = ctx;
379                         break;
380                 }
381         }
382
383         if (!found)
384                 return;
385
386         c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
387                                                 EINA_INLIST_GET(found));
388
389         free(found);
390 }
391
392 static void _contact_info_on_del_dispatch(Contact_Info *c)
393 {
394         Eina_Inlist *lst = c->on_del_cbs;
395         c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
396         while (lst) {
397                 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
398                         (lst, Contact_Info_On_Del_Ctx);
399
400                 lst = eina_inlist_remove(lst, lst);
401
402                 ctx->cb((void *)ctx->data, c);
403                 free(ctx);
404         }
405 }
406
407 static void _contact_info_free(Contact_Info *c_info);
408
409 void contact_info_del(Contact_Info *c)
410 {
411         Contacts *contacts;
412
413         EINA_SAFETY_ON_NULL_RETURN(c);
414
415         _contact_info_on_del_dispatch(c);
416
417         if (c->it)
418                 elm_object_item_del(c->it);
419
420         contacts = c->contacts;
421         contacts->c_list->list = eina_list_remove(contacts->c_list->list, c);
422         contacts->c_list->dirty = EINA_TRUE;
423         _contacts_save(contacts);
424
425         _contact_info_free(c);
426 }
427
428 static void _contacts_info_descriptor_init(Eet_Data_Descriptor **edd,
429                                                 Eet_Data_Descriptor **edd_list)
430 {
431         Eet_Data_Descriptor_Class eddc;
432
433         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contact_Info);
434         *edd = eet_data_descriptor_stream_new(&eddc);
435
436         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contacts_List);
437         *edd_list = eet_data_descriptor_stream_new(&eddc);
438
439         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
440                                         "picture", picture, EET_T_STRING);
441         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
442                                         "work", work, EET_T_STRING);
443         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
444                                         "home", home, EET_T_STRING);
445         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
446                                         "mobile", mobile, EET_T_STRING);
447         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
448                                         "first_name", first_name, EET_T_STRING);
449         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
450                                         "last_name", last_name, EET_T_STRING);
451
452         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Contacts_List, "list", list,
453                                         *edd);
454 }
455
456 static void _contact_info_free(Contact_Info *c_info)
457 {
458         EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
459
460         if (c_info->on_changed_cbs.deleted) {
461                 ERR("contact still have changed deleted listeners: %p %s %s",
462                         c_info, c_info->first_name, c_info->last_name);
463                 eina_list_free(c_info->on_changed_cbs.deleted);
464         }
465
466         if (c_info->on_changed_cbs.listeners) {
467                 while (c_info->on_changed_cbs.listeners) {
468                         Contact_Info_On_Changed_Ctx *ctx;
469
470                         ctx = EINA_INLIST_CONTAINER_GET(
471                                 c_info->on_changed_cbs.listeners,
472                                 Contact_Info_On_Changed_Ctx);
473                         c_info->on_changed_cbs.listeners = eina_inlist_remove
474                                 (c_info->on_changed_cbs.listeners,
475                                         c_info->on_changed_cbs.listeners);
476                         free(ctx);
477                 }
478         }
479
480         if (c_info->changed_idler)
481                 ecore_idler_del(c_info->changed_idler);
482
483         eina_stringshare_del(c_info->first_name);
484         eina_stringshare_del(c_info->last_name);
485         eina_stringshare_del(c_info->full_name);
486         eina_stringshare_del(c_info->mobile);
487         eina_stringshare_del(c_info->home);
488         eina_stringshare_del(c_info->work);
489         eina_stringshare_del(c_info->picture);
490         free(c_info);
491 }
492
493 static void _on_del(void *data, Evas *e __UNUSED__,
494                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
495 {
496         Contacts *contacts = data;
497         Contact_Info *c_info;
498
499         if (contacts->c_list->save_poller)
500                 ecore_poller_del(contacts->c_list->save_poller);
501         if (contacts->c_list->dirty)
502                 _contacts_save_do(contacts);
503
504         eet_data_descriptor_free(contacts->edd);
505         eet_data_descriptor_free(contacts->edd_list);
506         EINA_LIST_FREE(contacts->c_list->list, c_info) {
507                 _contact_info_on_del_dispatch(c_info);
508                 _contact_info_free(c_info);
509         }
510         free(contacts->c_list);
511         elm_genlist_item_class_free(contacts->itc);
512         elm_genlist_item_class_free(contacts->group);
513         free(contacts->path);
514         free(contacts->bkp);
515         free(contacts);
516         eet_shutdown();
517 }
518
519 static void _on_number_clicked(void *data, Evas_Object *obj,
520                                 void *event_inf __UNUSED__)
521 {
522         const char *number = data;
523         Evas_Object *contacts = evas_object_data_get(obj, "contacts");
524         printf("contacts=%p, number=%p\n", contacts, number);
525         evas_object_smart_callback_call(contacts, "selected", (void *)number);
526 }
527
528 static void _on_item_click(void *data, Evas_Object *obj __UNUSED__,
529                                 void *event_inf)
530 {
531         Contacts *contacts = data;
532         Elm_Object_Item *item = event_inf;
533         Evas_Object *details, *btn, *photo;
534         Contact_Info *c_info;
535         char *phone;
536
537         details = contacts->details;
538         c_info = elm_object_item_data_get(item);
539         elm_genlist_item_selected_set(item, EINA_FALSE);
540         elm_layout_box_remove_all(details, "box.phones", EINA_TRUE);
541
542         elm_object_part_text_set(details, "text.name", c_info->first_name);
543         elm_object_part_text_set(details, "text.last.name", c_info->last_name);
544
545         photo = picture_icon_get(details, c_info->picture);
546         elm_object_part_content_set(details, "swallow.photo", photo);
547
548         btn = elm_button_add(details);
549         EINA_SAFETY_ON_NULL_RETURN(btn);
550         elm_object_style_set(btn, "contacts");
551         phone = phone_format(c_info->mobile);
552         elm_object_part_text_set(btn, "elm.text.type", "Mobile");
553         elm_object_part_text_set(btn, "elm.text.phone", phone);
554         free(phone);
555         evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
556                                                 EVAS_HINT_EXPAND);
557         evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
558         evas_object_show(btn);
559         evas_object_data_set(btn, "contacts", contacts->layout);
560         evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
561                                         c_info->mobile);
562         elm_layout_box_append(details, "box.phones", btn);
563
564         btn = elm_button_add(details);
565         EINA_SAFETY_ON_NULL_RETURN(btn);
566         elm_object_style_set(btn, "contacts");
567         phone = phone_format(c_info->home);
568         elm_object_part_text_set(btn, "elm.text.type", "Home");
569         elm_object_part_text_set(btn, "elm.text.phone", phone);
570         free(phone);
571         evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
572                                                 EVAS_HINT_EXPAND);
573         evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
574         evas_object_show(btn);
575         evas_object_data_set(btn, "contacts", contacts->layout);
576         evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
577                                         c_info->home);
578         elm_layout_box_append(details, "box.phones", btn);
579
580         btn = elm_button_add(details);
581         EINA_SAFETY_ON_NULL_RETURN(btn);
582         elm_object_style_set(btn, "contacts");
583         phone = phone_format(c_info->work);
584         elm_object_part_text_set(btn, "elm.text.type", "Work");
585         elm_object_part_text_set(btn, "elm.text.phone", phone);
586         free(phone);
587         evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
588                                                 EVAS_HINT_EXPAND);
589         evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
590         evas_object_show(btn);
591         evas_object_data_set(btn, "contacts", contacts->layout);
592         evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
593                                         c_info->work);
594         elm_layout_box_append(details, "box.phones", btn);
595         elm_object_signal_emit(contacts->layout, "show,details", "gui");
596 }
597
598 static int _sort_by_name_cb(const void *v1, const void *v2)
599 {
600         const Contact_Info *c1, *c2;
601         int r;
602
603         c1 = v1;
604         c2 = v2;
605
606         if (!c1) return 1;
607         if (!c2) return -1;
608
609         r = strcmp(c1->first_name, c2->first_name);
610         if (r == 0)
611                 return strcmp(c1->last_name, c2->last_name);
612
613         return r;
614 }
615
616 static void _contacts_read(Contacts *contacts)
617 {
618         Contact_Info *c_info;
619         Eina_List *l;
620         Eet_File *efile;
621         Elm_Object_Item *it = NULL;
622         char group;
623         efile = eet_open(contacts->path, EET_FILE_MODE_READ);
624
625         if (efile) {
626                 contacts->c_list = eet_data_read(efile, contacts->edd_list,
627                                                         CONTACTS_ENTRY);
628                 eet_close(efile);
629         }
630
631         if (!contacts->c_list) {
632                 efile = eet_open(contacts->bkp, EET_FILE_MODE_READ);
633                 if (efile) {
634                         contacts->c_list = eet_data_read(
635                                 efile, contacts->edd_list, CONTACTS_ENTRY);
636                         eet_close(efile);
637                 }
638         }
639
640         if (!contacts->c_list)
641                 contacts->c_list = calloc(1, sizeof(Contacts_List));
642
643         EINA_SAFETY_ON_NULL_RETURN(contacts->c_list);
644         contacts->c_list->list = eina_list_sort(contacts->c_list->list, 0,
645                                                 _sort_by_name_cb);
646         group = '\0';
647         EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
648                 if (!c_info)
649                         continue;
650                 if (group != c_info->first_name[0]) {
651                         group = c_info->first_name[0];
652                         it = elm_genlist_item_append(contacts->genlist,
653                                                         contacts->group,
654                                                         c_info, NULL,
655                                                         ELM_GENLIST_ITEM_GROUP,
656                                                         NULL,
657                                                         NULL);
658                         elm_genlist_item_select_mode_set(it, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
659                 }
660                 it = elm_genlist_item_append(contacts->genlist,contacts->itc,
661                                                 c_info, it,
662                                                 ELM_GENLIST_ITEM_NONE,
663                                                 _on_item_click, contacts);
664                 c_info->it = it;
665                 c_info->contacts = contacts;
666         }
667 }
668
669 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
670                                 const char *part)
671 {
672         Contact_Info *c_info = data;
673
674         if (strncmp(part, "text.contacts.", strlen("text.contacts.")))
675                 return NULL;
676
677         part += strlen("text.contacts.");
678
679         if (strcmp(part, "name") == 0)
680                 return strdup(c_info->first_name);
681         else if (strcmp(part, "last") == 0)
682                 return strdup(c_info->last_name);
683         else if (strcmp(part, "full") == 0)
684                 return strdup(contact_info_full_name_get(c_info));
685
686         ERR("Unexpected part name: %s", part);
687         return NULL;
688 }
689
690 static char *_group_label_get(void *data, Evas_Object *obj __UNUSED__,
691                                 const char *part __UNUSED__)
692 {
693         Contact_Info *c_info = data;
694         char buf[2];
695         snprintf(buf, sizeof(buf), "%c", c_info->first_name[0]);
696         return strdup(buf);
697 }
698
699
700 static Evas_Object *_item_content_get(void *data,
701                                         Evas_Object *obj,
702                                         const char *part)
703 {
704         Contact_Info *c_info = data;
705         Evas_Object *photo;
706
707         if (strncmp(part, "swallow.", strlen("swallow.")))
708                 return NULL;
709
710         part += strlen("swallow.");
711
712         if (strcmp(part, "photo") != 0) {
713                 ERR("Unexpected part name: %s", part);
714                 return NULL;
715         }
716
717         photo = picture_icon_get(obj, c_info->picture);
718
719         return photo;
720 }
721
722 static void _on_back_clicked(void *data, Evas_Object *obj __UNUSED__,
723                                 const char *emission __UNUSED__,
724                                 const char *source __UNUSED__)
725 {
726         Evas_Object *layout = data;
727         elm_object_signal_emit(layout, "hide,details", "gui");
728 }
729
730 Evas_Object *contacts_add(Evas_Object *parent)
731 {
732         int r;
733         const char *config_path;
734         char base_dir[PATH_MAX], *path;
735         Contacts *contacts;
736         Evas_Object *obj, *genlist, *details;
737         Elm_Genlist_Item_Class *itc, *group;
738
739         eet_init();
740         contacts = calloc(1, sizeof(Contacts));
741         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
742
743
744         details = layout_add(parent, "contacts_details");
745         EINA_SAFETY_ON_NULL_GOTO(details, err_layout);
746
747         obj = layout_add(parent, "contacts_bg");
748         EINA_SAFETY_ON_NULL_GOTO(obj, err_obj);
749
750         genlist = elm_genlist_add(obj);
751         EINA_SAFETY_ON_NULL_GOTO(genlist, err_genlist);
752         elm_object_style_set(genlist, "contacts");
753         elm_genlist_homogeneous_set(genlist, EINA_TRUE);
754
755         itc = elm_genlist_item_class_new();
756         EINA_SAFETY_ON_NULL_GOTO(itc, err_genlist);
757         itc->item_style = "contacts";
758         itc->func.text_get =  _item_label_get;
759         itc->func.content_get = _item_content_get;
760         itc->func.state_get = NULL;
761         itc->func.del = NULL;
762
763         group = elm_genlist_item_class_new();
764         EINA_SAFETY_ON_NULL_GOTO(group, err_group);
765         group->item_style = "group_contacts";
766         group->func.text_get = _group_label_get;
767         group->func.content_get = NULL;
768         group->func.state_get = NULL;
769         group->func.del = NULL;
770         contacts->group = group;
771         contacts->genlist = genlist;
772         contacts->itc = itc;
773         contacts->layout = obj;
774         contacts->details = details;
775
776         elm_object_part_content_set(obj, "elm.swallow.genlist", genlist);
777         elm_object_part_content_set(obj, "elm.swallow.details", details);
778
779         elm_object_signal_callback_add(details, "clicked,back", "gui",
780                                         _on_back_clicked, obj);
781
782         config_path = efreet_config_home_get();
783         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
784                         PACKAGE_NAME);
785         ecore_file_mkpath(base_dir);
786
787         r = asprintf(&path,  "%s/%s/contacts.eet", config_path, PACKAGE_NAME);
788
789         if (r < 0)
790                 goto err_path;
791         contacts->path = path;
792
793         r = asprintf(&path,  "%s/%s/contacts.eet.bkp", config_path,
794                         PACKAGE_NAME);
795
796         if (r < 0)
797                 goto err_bkp;
798         contacts->bkp = path;
799
800         _contacts_info_descriptor_init(&contacts->edd, &contacts->edd_list);
801         _contacts_read(contacts);
802         EINA_SAFETY_ON_NULL_GOTO(contacts->c_list, err_read);
803
804         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
805                                         contacts);
806
807         evas_object_data_set(obj, "contacts.ctx", contacts);
808         return obj;
809
810 err_read:
811         eet_data_descriptor_free(contacts->edd);
812         eet_data_descriptor_free(contacts->edd_list);
813         free(contacts->bkp);
814 err_bkp:
815         free(contacts->path);
816 err_path:
817         elm_genlist_item_class_free(group);
818 err_group:
819         elm_genlist_item_class_free(itc);
820 err_genlist:
821         free(obj);
822 err_obj:
823         free(details);
824 err_layout:
825         free(contacts);
826         eet_shutdown();
827         return NULL;
828 }