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