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