Version bump
[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 #include <string.h>
8
9 #include "log.h"
10 #include "ofono.h"
11 #include "contacts-ofono-efl.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 Eina_List *contact_info_all_numbers_get(const Contact_Info *c)
70 {
71         Eina_List *l = NULL;
72
73         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
74
75         if (c->mobile)
76                 l = eina_list_append(l, c->mobile);
77         if (c->work)
78                 l = eina_list_append(l, c->work);
79         if (c->home)
80                 l = eina_list_append(l, c->home);
81
82         return l;
83 }
84
85 static Eina_Bool _number_match(const char *n1, const char *n2)
86 {
87         if (eina_str_has_suffix(n1, n2))
88                 return EINA_TRUE;
89
90         return EINA_FALSE;
91 }
92
93 struct _Contact_Partial_Match
94 {
95         const Contact_Info *info;
96         const char *type;
97         Eina_Bool name_match : 1;
98 };
99
100 static void _partial_match_add(Eina_List **p_list, const char *type,
101                                 const Contact_Info *c_info,
102                                 Eina_Bool name_match)
103 {
104         Contact_Partial_Match *pm = malloc(sizeof(Contact_Partial_Match));
105         EINA_SAFETY_ON_NULL_RETURN(pm);
106         pm->info = c_info;
107         pm->type = type;
108         pm->name_match = name_match;
109         *p_list = eina_list_append(*p_list, pm);
110 }
111
112 static void _partial_name_match_add(Eina_List **p_list,
113                                         const Contact_Info *c_info)
114 {
115         if (c_info->mobile)
116                 _partial_match_add(p_list, "Mobile", c_info, EINA_TRUE);
117         if (c_info->work)
118                 _partial_match_add(p_list, "Work", c_info, EINA_TRUE);
119         if (c_info->home)
120                 _partial_match_add(p_list, "Home", c_info, EINA_TRUE);
121 }
122
123 static void _partial_number_match_add(Eina_List **p_list, const char *type,
124                                         const Contact_Info *c_info)
125 {
126         _partial_match_add(p_list, type, c_info, EINA_FALSE);
127 }
128
129 Eina_List *contact_partial_match_search(Evas_Object *obj, const char *query)
130 {
131         const Contact_Info *c_info;
132         const Contacts *contacts;
133         Eina_List *ret = NULL, *l;
134         int i, j;
135         Eina_Bool name_search = EINA_FALSE;
136         char *query_number;
137
138         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
139         EINA_SAFETY_ON_NULL_RETURN_VAL(query, NULL);
140         contacts = evas_object_data_get(obj, "contacts.ctx");
141         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
142
143         /* Check if it is numeric */
144         query_number = alloca(strlen(query) + 1);
145         EINA_SAFETY_ON_NULL_RETURN_VAL(query_number, NULL);
146         for (i = 0, j = 0; query[i] != '\0'; i++) {
147                 if (isalpha(query[i])) {
148                         name_search = EINA_TRUE;
149                         break;
150                 } else if (isdigit(query[i]))
151                         query_number[j++] = query[i];
152         }
153
154         if (name_search) {
155                 EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
156                         const char *full_name;
157
158                         full_name = contact_info_full_name_get(c_info);
159                         if (strcasestr(full_name, query))
160                                 _partial_name_match_add(&ret, c_info);
161                 }
162         } else {
163                 query_number[j] = '\0';
164                 EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
165                         if (_number_match(c_info->mobile, query_number))
166                                 _partial_number_match_add(&ret, "Mobile",
167                                                                 c_info);
168
169                         if (_number_match(c_info->work, query_number))
170                                 _partial_number_match_add(&ret, "Work",
171                                                                 c_info);
172
173                         if (_number_match(c_info->home, query_number))
174                                 _partial_number_match_add(&ret, "Home",
175                                                                 c_info);
176                 }
177         }
178
179         return ret;
180 }
181
182 void contact_partial_match_search_free(Eina_List *results)
183 {
184         Contact_Partial_Match *pm;
185         EINA_LIST_FREE(results, pm)
186                 free(pm);
187 }
188
189 const char *contact_partial_match_type_get(const Contact_Partial_Match *pm)
190 {
191         EINA_SAFETY_ON_NULL_RETURN_VAL(pm, NULL);
192         return pm->type;
193 }
194
195 const Contact_Info *contact_partial_match_info_get(const Contact_Partial_Match *pm)
196 {
197         EINA_SAFETY_ON_NULL_RETURN_VAL(pm, NULL);
198         return pm->info;
199 }
200
201 Eina_Bool contact_partial_match_name_match_get(const Contact_Partial_Match *pm)
202 {
203         EINA_SAFETY_ON_NULL_RETURN_VAL(pm, EINA_FALSE);
204         return pm->name_match;
205 }
206
207 Contact_Info *contact_search(Evas_Object *obj, const char *number, const char **type)
208 {
209         Contact_Info *c_info;
210         Eina_List *l;
211         Contacts *contacts;
212
213         EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
214         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
215         contacts = evas_object_data_get(obj, "contacts.ctx");
216         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
217
218         EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
219                 if (strcmp(number, c_info->mobile) == 0) {
220                         if (type)
221                                 *type = "Mobile";
222                         return c_info;
223                 } else if (strcmp(number, c_info->work) == 0) {
224                         if (type)
225                                 *type = "Work";
226                         return c_info;
227                 } else if (strcmp(number, c_info->home) == 0) {
228                         if (type)
229                                 *type = "Home";
230                         return c_info;
231                 }
232         }
233
234         return NULL;
235 }
236
237 const char *contact_info_full_name_get(const Contact_Info *c)
238 {
239         Contact_Info *c2;
240
241         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
242
243         if (c->full_name)
244                 return c->full_name;
245
246         c2 = (Contact_Info *)c;
247         c2->full_name = eina_stringshare_printf("%s %s",
248                                                 c->first_name, c->last_name);
249         return c->full_name;
250 }
251
252 const char *contact_info_first_name_get(const Contact_Info *c)
253 {
254         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
255         return c->first_name;
256 }
257
258 const char *contact_info_last_name_get(const Contact_Info *c)
259 {
260         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
261         return c->last_name;
262 }
263
264 const char *contact_info_picture_get(const Contact_Info *c)
265 {
266         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
267         return c->picture;
268 }
269
270 const char *contact_info_detail_get(const Contact_Info *c, const char *type)
271 {
272         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
273         EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
274         if (strcmp(type, "Mobile") == 0)
275                 return c->mobile;
276         else if (strcmp(type, "Work") == 0)
277                 return c->work;
278         else if (strcmp(type, "Home") == 0)
279                 return c->home;
280         else
281                 return NULL;
282 }
283
284 const char *contact_info_number_check(const Contact_Info *c, const char *number)
285 {
286         EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
287         EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
288
289         if (strcmp(number, c->mobile) == 0)
290                 return "Mobile";
291         else if (strcmp(number, c->work) == 0)
292                 return "Work";
293         else if (strcmp(number, c->home) == 0)
294                 return "Home";
295
296         return NULL;
297 }
298
299 static Eina_Bool _contacts_save_do(void *data)
300 {
301         Contacts *contacts = data;
302         Eet_File *efile;
303
304         contacts->c_list->save_poller = NULL;
305         contacts->c_list->dirty = EINA_FALSE;
306
307         ecore_file_unlink(contacts->bkp);
308         ecore_file_mv(contacts->path, contacts->bkp);
309         efile = eet_open(contacts->path, EET_FILE_MODE_WRITE);
310         EINA_SAFETY_ON_NULL_GOTO(efile, failed);
311         if (!(eet_data_write(efile,
312                                 contacts->edd_list, CONTACTS_ENTRY,
313                                 contacts->c_list, EET_COMPRESSION_DEFAULT)))
314                 ERR("Could in the contacts database");
315
316         DBG("wrote %s", contacts->path);
317         eet_close(efile);
318         return EINA_FALSE;
319
320 failed:
321         ecore_file_mv(contacts->bkp, contacts->path);
322         return EINA_FALSE;
323 }
324
325 static void _contacts_save(Contacts *contacts)
326 {
327         if (contacts->c_list->save_poller)
328                 return;
329
330         contacts->c_list->save_poller = ecore_poller_add
331                 (ECORE_POLLER_CORE, 32, _contacts_save_do, contacts);
332 }
333
334 static Eina_Bool _contact_info_changed_idler(void *data)
335 {
336         Contact_Info *c = data;
337         Contact_Info_On_Changed_Ctx *ctx;
338
339         c->changed_idler = NULL;
340
341         c->on_changed_cbs.walking++;
342         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
343                 if (ctx->deleted)
344                         continue;
345                 ctx->cb((void *)ctx->data, c);
346         }
347         c->on_changed_cbs.walking--;
348
349         _contacts_save(c->contacts);
350
351         if (c->on_changed_cbs.walking > 0)
352                 return EINA_FALSE;
353
354         EINA_LIST_FREE(c->on_changed_cbs.deleted, ctx) {
355                 c->on_changed_cbs.listeners = eina_inlist_remove(
356                         c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
357                 free(ctx);
358         }
359
360         return EINA_FALSE;
361 }
362
363 static void _contact_info_changed(Contact_Info *c)
364 {
365         if (c->changed_idler)
366                 return;
367         c->changed_idler = ecore_idler_add(_contact_info_changed_idler, c);
368 }
369
370 Eina_Bool contact_info_picture_set(Contact_Info *c, const char *filename)
371 {
372         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
373         EINA_SAFETY_ON_NULL_RETURN_VAL(filename, EINA_FALSE);
374
375         DBG("c=%p, was=%s, new=%s", c, c->picture, filename);
376
377         if (eina_stringshare_replace(&(c->picture), filename))
378                 _contact_info_changed(c);
379
380         return EINA_TRUE;
381 }
382
383 Eina_Bool contact_info_first_name_set(Contact_Info *c, const char *name)
384 {
385         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
386         EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
387
388         DBG("c=%p, was=%s, new=%s", c, c->first_name, name);
389
390         if (eina_stringshare_replace(&(c->first_name), name)) {
391                 eina_stringshare_replace(&(c->full_name), NULL);
392                 _contact_info_changed(c);
393         }
394
395         return EINA_TRUE;
396 }
397
398 Eina_Bool contact_info_last_name_set(Contact_Info *c, const char *name)
399 {
400         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
401         EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
402
403         DBG("c=%p, was=%s, new=%s", c, c->last_name, name);
404
405         if (eina_stringshare_replace(&(c->last_name), name)) {
406                 eina_stringshare_replace(&(c->full_name), NULL);
407                 _contact_info_changed(c);
408         }
409
410         return EINA_TRUE;
411 }
412
413 Eina_Bool contact_info_detail_set(Contact_Info *c, const char *type, const char *number)
414 {
415         Eina_Bool changed = EINA_FALSE;
416
417         EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
418         EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE);
419         EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE);
420
421         DBG("c=%p, type=%s, number=%s", c, type, number);
422
423         if (strcmp(type, "Mobile") == 0)
424                 changed = eina_stringshare_replace(&(c->mobile), number);
425         else if (strcmp(type, "Work") == 0)
426                 changed = eina_stringshare_replace(&(c->work), number);
427         else if (strcmp(type, "Home") == 0)
428                 changed = eina_stringshare_replace(&(c->home), number);
429         else {
430                 ERR("Unknown type: %s, number: %s", type, number);
431                 return EINA_FALSE;
432         }
433
434         if (changed)
435                 _contact_info_changed(c);
436
437         return EINA_TRUE;
438
439 }
440
441 void contact_info_on_changed_callback_add(Contact_Info *c,
442                                         void (*cb)(void *data, Contact_Info *c),
443                                         const void *data)
444 {
445         Contact_Info_On_Changed_Ctx *ctx;
446
447         EINA_SAFETY_ON_NULL_RETURN(c);
448         EINA_SAFETY_ON_NULL_RETURN(cb);
449
450         ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
451         EINA_SAFETY_ON_NULL_RETURN(ctx);
452         ctx->cb = cb;
453         ctx->data = data;
454
455         c->on_changed_cbs.listeners = eina_inlist_append(
456                 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
457 }
458
459 void contact_info_on_changed_callback_del(Contact_Info *c,
460                                         void (*cb)(void *data, Contact_Info *c),
461                                         const void *data)
462 {
463         Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
464         EINA_SAFETY_ON_NULL_RETURN(c);
465         EINA_SAFETY_ON_NULL_RETURN(cb);
466
467         EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
468                 if (ctx->cb == cb && ctx->data == data) {
469                         found = ctx;
470                         break;
471                 }
472         }
473
474         if (!found)
475                 return;
476
477         if (c->on_changed_cbs.walking > 0) {
478                 found->deleted = EINA_TRUE;
479                 c->on_changed_cbs.deleted = eina_list_append(
480                         c->on_changed_cbs.deleted, found);
481                 return;
482         }
483
484         c->on_changed_cbs.listeners = eina_inlist_remove(
485                 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
486         free(found);
487 }
488
489
490 void contact_info_on_del_callback_add(Contact_Info *c,
491                                         void (*cb)(void *data, const Contact_Info *c),
492                                         const void *data)
493 {
494         Contact_Info_On_Del_Ctx *ctx;
495
496         EINA_SAFETY_ON_NULL_RETURN(c);
497         EINA_SAFETY_ON_NULL_RETURN(cb);
498
499         ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
500         EINA_SAFETY_ON_NULL_RETURN(ctx);
501         ctx->cb = cb;
502         ctx->data = data;
503
504         c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
505 }
506
507 void contact_info_on_del_callback_del(Contact_Info *c,
508                                         void (*cb)(void *data, const Contact_Info *c),
509                                         const void *data)
510 {
511         Contact_Info_On_Del_Ctx *ctx, *found = NULL;
512         EINA_SAFETY_ON_NULL_RETURN(c);
513         EINA_SAFETY_ON_NULL_RETURN(cb);
514
515         EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
516                 if (ctx->cb == cb && ctx->data == data) {
517                         found = ctx;
518                         break;
519                 }
520         }
521
522         if (!found)
523                 return;
524
525         c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
526                                                 EINA_INLIST_GET(found));
527
528         free(found);
529 }
530
531 static void _contact_info_on_del_dispatch(Contact_Info *c)
532 {
533         Eina_Inlist *lst = c->on_del_cbs;
534         c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
535         while (lst) {
536                 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
537                         (lst, Contact_Info_On_Del_Ctx);
538
539                 lst = eina_inlist_remove(lst, lst);
540
541                 ctx->cb((void *)ctx->data, c);
542                 free(ctx);
543         }
544 }
545
546 static void _contact_info_free(Contact_Info *c_info);
547
548 void contact_info_del(Contact_Info *c)
549 {
550         Contacts *contacts;
551
552         EINA_SAFETY_ON_NULL_RETURN(c);
553
554         _contact_info_on_del_dispatch(c);
555
556         if (c->it)
557                 elm_object_item_del(c->it);
558
559         contacts = c->contacts;
560         contacts->c_list->list = eina_list_remove(contacts->c_list->list, c);
561         contacts->c_list->dirty = EINA_TRUE;
562         _contacts_save(contacts);
563
564         _contact_info_free(c);
565 }
566
567 static void _contacts_info_descriptor_init(Eet_Data_Descriptor **edd,
568                                                 Eet_Data_Descriptor **edd_list)
569 {
570         Eet_Data_Descriptor_Class eddc;
571
572         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contact_Info);
573         *edd = eet_data_descriptor_stream_new(&eddc);
574
575         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contacts_List);
576         *edd_list = eet_data_descriptor_stream_new(&eddc);
577
578         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
579                                         "picture", picture, EET_T_STRING);
580         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
581                                         "work", work, EET_T_STRING);
582         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
583                                         "home", home, EET_T_STRING);
584         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
585                                         "mobile", mobile, EET_T_STRING);
586         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
587                                         "first_name", first_name, EET_T_STRING);
588         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
589                                         "last_name", last_name, EET_T_STRING);
590
591         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Contacts_List, "list", list,
592                                         *edd);
593 }
594
595 static void _contact_info_free(Contact_Info *c_info)
596 {
597         EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
598
599         if (c_info->on_changed_cbs.deleted) {
600                 ERR("contact still have changed deleted listeners: %p %s %s",
601                         c_info, c_info->first_name, c_info->last_name);
602                 eina_list_free(c_info->on_changed_cbs.deleted);
603         }
604
605         if (c_info->on_changed_cbs.listeners) {
606                 while (c_info->on_changed_cbs.listeners) {
607                         Contact_Info_On_Changed_Ctx *ctx;
608
609                         ctx = EINA_INLIST_CONTAINER_GET(
610                                 c_info->on_changed_cbs.listeners,
611                                 Contact_Info_On_Changed_Ctx);
612                         c_info->on_changed_cbs.listeners = eina_inlist_remove
613                                 (c_info->on_changed_cbs.listeners,
614                                         c_info->on_changed_cbs.listeners);
615                         free(ctx);
616                 }
617         }
618
619         if (c_info->changed_idler)
620                 ecore_idler_del(c_info->changed_idler);
621
622         eina_stringshare_del(c_info->first_name);
623         eina_stringshare_del(c_info->last_name);
624         eina_stringshare_del(c_info->full_name);
625         eina_stringshare_del(c_info->mobile);
626         eina_stringshare_del(c_info->home);
627         eina_stringshare_del(c_info->work);
628         eina_stringshare_del(c_info->picture);
629         free(c_info);
630 }
631
632 static void _on_del(void *data, Evas *e __UNUSED__,
633                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
634 {
635         Contacts *contacts = data;
636         Contact_Info *c_info;
637
638         if (contacts->c_list->save_poller)
639                 ecore_poller_del(contacts->c_list->save_poller);
640         if (contacts->c_list->dirty)
641                 _contacts_save_do(contacts);
642
643         eet_data_descriptor_free(contacts->edd);
644         eet_data_descriptor_free(contacts->edd_list);
645         EINA_LIST_FREE(contacts->c_list->list, c_info) {
646                 _contact_info_on_del_dispatch(c_info);
647                 _contact_info_free(c_info);
648         }
649         free(contacts->c_list);
650         elm_genlist_item_class_free(contacts->itc);
651         elm_genlist_item_class_free(contacts->group);
652         free(contacts->path);
653         free(contacts->bkp);
654         free(contacts);
655         eet_shutdown();
656 }
657
658 static void _on_number_clicked(void *data, Evas_Object *obj,
659                                 void *event_inf __UNUSED__)
660 {
661         const char *number = data;
662         Evas_Object *contacts = evas_object_data_get(obj, "contacts");
663         printf("contacts=%p, number=%p\n", contacts, number);
664         evas_object_smart_callback_call(contacts, "selected", (void *)number);
665 }
666
667 static void _on_item_click(void *data, Evas_Object *obj __UNUSED__,
668                                 void *event_inf)
669 {
670         Contacts *contacts = data;
671         Elm_Object_Item *item = event_inf;
672         Evas_Object *details, *btn, *photo;
673         Contact_Info *c_info;
674         char *phone;
675
676         details = contacts->details;
677         c_info = elm_object_item_data_get(item);
678         elm_genlist_item_selected_set(item, EINA_FALSE);
679         elm_layout_box_remove_all(details, "box.phones", EINA_TRUE);
680
681         elm_object_part_text_set(details, "text.name", c_info->first_name);
682         elm_object_part_text_set(details, "text.last.name", c_info->last_name);
683
684         photo = picture_icon_get(details, c_info->picture);
685         elm_object_part_content_set(details, "swallow.photo", photo);
686
687         btn = elm_button_add(details);
688         EINA_SAFETY_ON_NULL_RETURN(btn);
689         elm_object_style_set(btn, "contacts");
690         phone = phone_format(c_info->mobile);
691         elm_object_part_text_set(btn, "elm.text.type", "Mobile");
692         elm_object_part_text_set(btn, "elm.text.phone", phone);
693         free(phone);
694         evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
695                                                 EVAS_HINT_EXPAND);
696         evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
697         evas_object_show(btn);
698         evas_object_data_set(btn, "contacts", contacts->layout);
699         evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
700                                         c_info->mobile);
701         elm_layout_box_append(details, "box.phones", btn);
702
703         btn = elm_button_add(details);
704         EINA_SAFETY_ON_NULL_RETURN(btn);
705         elm_object_style_set(btn, "contacts");
706         phone = phone_format(c_info->home);
707         elm_object_part_text_set(btn, "elm.text.type", "Home");
708         elm_object_part_text_set(btn, "elm.text.phone", phone);
709         free(phone);
710         evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
711                                                 EVAS_HINT_EXPAND);
712         evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
713         evas_object_show(btn);
714         evas_object_data_set(btn, "contacts", contacts->layout);
715         evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
716                                         c_info->home);
717         elm_layout_box_append(details, "box.phones", btn);
718
719         btn = elm_button_add(details);
720         EINA_SAFETY_ON_NULL_RETURN(btn);
721         elm_object_style_set(btn, "contacts");
722         phone = phone_format(c_info->work);
723         elm_object_part_text_set(btn, "elm.text.type", "Work");
724         elm_object_part_text_set(btn, "elm.text.phone", phone);
725         free(phone);
726         evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
727                                                 EVAS_HINT_EXPAND);
728         evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
729         evas_object_show(btn);
730         evas_object_data_set(btn, "contacts", contacts->layout);
731         evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
732                                         c_info->work);
733         elm_layout_box_append(details, "box.phones", btn);
734         elm_object_signal_emit(contacts->layout, "show,details", "gui");
735 }
736
737 static int _sort_by_name_cb(const void *v1, const void *v2)
738 {
739         const Contact_Info *c1, *c2;
740         int r;
741
742         c1 = v1;
743         c2 = v2;
744
745         if (!c1) return 1;
746         if (!c2) return -1;
747
748         r = strcmp(c1->first_name, c2->first_name);
749         if (r == 0)
750                 return strcmp(c1->last_name, c2->last_name);
751
752         return r;
753 }
754
755 static void _contacts_read(Contacts *contacts)
756 {
757         Contact_Info *c_info;
758         Eina_List *l;
759         Eet_File *efile;
760         Elm_Object_Item *it = NULL;
761         char group;
762         efile = eet_open(contacts->path, EET_FILE_MODE_READ);
763
764         if (efile) {
765                 contacts->c_list = eet_data_read(efile, contacts->edd_list,
766                                                         CONTACTS_ENTRY);
767                 eet_close(efile);
768         }
769
770         if (!contacts->c_list) {
771                 efile = eet_open(contacts->bkp, EET_FILE_MODE_READ);
772                 if (efile) {
773                         contacts->c_list = eet_data_read(
774                                 efile, contacts->edd_list, CONTACTS_ENTRY);
775                         eet_close(efile);
776                 }
777         }
778
779         if (!contacts->c_list)
780                 contacts->c_list = calloc(1, sizeof(Contacts_List));
781
782         EINA_SAFETY_ON_NULL_RETURN(contacts->c_list);
783         contacts->c_list->list = eina_list_sort(contacts->c_list->list, 0,
784                                                 _sort_by_name_cb);
785         group = '\0';
786         EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
787                 if (!c_info)
788                         continue;
789                 if (group != c_info->first_name[0]) {
790                         group = c_info->first_name[0];
791                         it = elm_genlist_item_append(contacts->genlist,
792                                                         contacts->group,
793                                                         c_info, NULL,
794                                                         ELM_GENLIST_ITEM_GROUP,
795                                                         NULL,
796                                                         NULL);
797                         elm_genlist_item_select_mode_set(it, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
798                 }
799                 it = elm_genlist_item_append(contacts->genlist,contacts->itc,
800                                                 c_info, it,
801                                                 ELM_GENLIST_ITEM_NONE,
802                                                 _on_item_click, contacts);
803                 c_info->it = it;
804                 c_info->contacts = contacts;
805         }
806 }
807
808 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
809                                 const char *part)
810 {
811         Contact_Info *c_info = data;
812
813         if (strncmp(part, "text.contacts.", strlen("text.contacts.")))
814                 return NULL;
815
816         part += strlen("text.contacts.");
817
818         if (strcmp(part, "name") == 0)
819                 return strdup(c_info->first_name);
820         else if (strcmp(part, "last") == 0)
821                 return strdup(c_info->last_name);
822         else if (strcmp(part, "full") == 0)
823                 return strdup(contact_info_full_name_get(c_info));
824
825         ERR("Unexpected part name: %s", part);
826         return NULL;
827 }
828
829 static char *_group_label_get(void *data, Evas_Object *obj __UNUSED__,
830                                 const char *part __UNUSED__)
831 {
832         Contact_Info *c_info = data;
833         char buf[2];
834         snprintf(buf, sizeof(buf), "%c", c_info->first_name[0]);
835         return strdup(buf);
836 }
837
838
839 static Evas_Object *_item_content_get(void *data,
840                                         Evas_Object *obj,
841                                         const char *part)
842 {
843         Contact_Info *c_info = data;
844         Evas_Object *photo;
845
846         if (strncmp(part, "swallow.", strlen("swallow.")))
847                 return NULL;
848
849         part += strlen("swallow.");
850
851         if (strcmp(part, "photo") != 0) {
852                 ERR("Unexpected part name: %s", part);
853                 return NULL;
854         }
855
856         photo = picture_icon_get(obj, c_info->picture);
857
858         return photo;
859 }
860
861 static void _on_back_clicked(void *data, Evas_Object *obj __UNUSED__,
862                                 const char *emission __UNUSED__,
863                                 const char *source __UNUSED__)
864 {
865         Evas_Object *layout = data;
866         elm_object_signal_emit(layout, "hide,details", "gui");
867 }
868
869 Evas_Object *contacts_add(Evas_Object *parent)
870 {
871         int r;
872         const char *config_path;
873         char base_dir[PATH_MAX], *path;
874         Contacts *contacts;
875         Evas_Object *obj, *genlist, *details;
876         Elm_Genlist_Item_Class *itc, *group;
877
878         eet_init();
879         contacts = calloc(1, sizeof(Contacts));
880         EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
881
882
883         details = layout_add(parent, "contacts_details");
884         EINA_SAFETY_ON_NULL_GOTO(details, err_layout);
885
886         obj = layout_add(parent, "contacts_bg");
887         EINA_SAFETY_ON_NULL_GOTO(obj, err_obj);
888
889         genlist = elm_genlist_add(obj);
890         EINA_SAFETY_ON_NULL_GOTO(genlist, err_genlist);
891         elm_object_style_set(genlist, "contacts");
892         elm_genlist_homogeneous_set(genlist, EINA_TRUE);
893
894         itc = elm_genlist_item_class_new();
895         EINA_SAFETY_ON_NULL_GOTO(itc, err_genlist);
896         itc->item_style = "contacts";
897         itc->func.text_get =  _item_label_get;
898         itc->func.content_get = _item_content_get;
899         itc->func.state_get = NULL;
900         itc->func.del = NULL;
901
902         group = elm_genlist_item_class_new();
903         EINA_SAFETY_ON_NULL_GOTO(group, err_group);
904         group->item_style = "group_contacts";
905         group->func.text_get = _group_label_get;
906         group->func.content_get = NULL;
907         group->func.state_get = NULL;
908         group->func.del = NULL;
909         contacts->group = group;
910         contacts->genlist = genlist;
911         contacts->itc = itc;
912         contacts->layout = obj;
913         contacts->details = details;
914
915         elm_object_part_content_set(obj, "elm.swallow.genlist", genlist);
916         elm_object_part_content_set(obj, "elm.swallow.details", details);
917
918         elm_object_signal_callback_add(details, "clicked,back", "gui",
919                                         _on_back_clicked, obj);
920
921         config_path = efreet_config_home_get();
922         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
923                         PACKAGE_NAME);
924         ecore_file_mkpath(base_dir);
925
926         r = asprintf(&path,  "%s/%s/contacts.eet", config_path, PACKAGE_NAME);
927
928         if (r < 0)
929                 goto err_path;
930         contacts->path = path;
931
932         r = asprintf(&path,  "%s/%s/contacts.eet.bkp", config_path,
933                         PACKAGE_NAME);
934
935         if (r < 0)
936                 goto err_bkp;
937         contacts->bkp = path;
938
939         _contacts_info_descriptor_init(&contacts->edd, &contacts->edd_list);
940         _contacts_read(contacts);
941         EINA_SAFETY_ON_NULL_GOTO(contacts->c_list, err_read);
942
943         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
944                                         contacts);
945
946         evas_object_data_set(obj, "contacts.ctx", contacts);
947         return obj;
948
949 err_read:
950         eet_data_descriptor_free(contacts->edd);
951         eet_data_descriptor_free(contacts->edd_list);
952         free(contacts->bkp);
953 err_bkp:
954         free(contacts->path);
955 err_path:
956         elm_genlist_item_class_free(group);
957 err_group:
958         elm_genlist_item_class_free(itc);
959 err_genlist:
960         free(obj);
961 err_obj:
962         free(details);
963 err_layout:
964         free(contacts);
965         eet_shutdown();
966         return NULL;
967 }