4 #include <Elementary.h>
10 #include "contacts-ofono-efl.h"
13 #ifndef EET_COMPRESSION_DEFAULT
14 #define EET_COMPRESSION_DEFAULT 1
17 #define CONTACTS_ENTRY "contacts"
19 typedef struct _Contacts_List {
22 Ecore_Poller *save_poller;
25 typedef struct _Contacts {
28 Eet_Data_Descriptor *edd;
29 Eet_Data_Descriptor *edd_list;
30 Elm_Genlist_Item_Class *itc, *group;
31 Evas_Object *genlist, *layout, *details;
32 Contacts_List *c_list;
35 struct _Contact_Info {
36 const char *first_name;
37 const char *last_name;
38 const char *full_name; /* not in edd */
44 Contacts *contacts; /* not in edd */
45 Elm_Object_Item *it; /* not in edd */
46 Eina_Inlist *on_del_cbs; /* not in edd */
47 Ecore_Idler *changed_idler; /* not in edd */
49 Eina_Inlist *listeners;
52 } on_changed_cbs; /* not in edd */
55 typedef struct _Contact_Info_On_Del_Ctx {
57 void (*cb)(void *, const Contact_Info *);
59 } Contact_Info_On_Del_Ctx;
61 typedef struct _Contact_Info_On_Changed_Ctx {
63 void (*cb)(void *, Contact_Info *);
66 } Contact_Info_On_Changed_Ctx;
68 Contact_Info *contact_search(Evas_Object *obj, const char *number, const char **type)
74 EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
75 EINA_SAFETY_ON_NULL_RETURN_VAL(number, NULL);
76 contacts = evas_object_data_get(obj, "contacts.ctx");
77 EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
79 EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
80 if (strcmp(number, c_info->mobile) == 0) {
84 } else if (strcmp(number, c_info->work) == 0) {
88 } else if (strcmp(number, c_info->home) == 0) {
98 const char *contact_info_full_name_get(const Contact_Info *c)
102 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
107 c2 = (Contact_Info *)c;
108 c2->full_name = eina_stringshare_printf("%s %s",
109 c->first_name, c->last_name);
113 const char *contact_info_first_name_get(const Contact_Info *c)
115 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
116 return c->first_name;
119 const char *contact_info_last_name_get(const Contact_Info *c)
121 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
125 const char *contact_info_picture_get(const Contact_Info *c)
127 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
131 const char *contact_info_detail_get(const Contact_Info *c, const char *type)
133 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
134 EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
135 if (strcmp(type, "Mobile") == 0)
137 else if (strcmp(type, "Work") == 0)
139 else if (strcmp(type, "Home") == 0)
145 const char *contact_info_number_check(const Contact_Info *c, const char *number)
147 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
148 EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE);
150 if (strcmp(number, c->mobile) == 0)
152 else if (strcmp(number, c->work) == 0)
154 else if (strcmp(number, c->home) == 0)
160 static Eina_Bool _contacts_save_do(void *data)
162 Contacts *contacts = data;
165 contacts->c_list->save_poller = NULL;
166 contacts->c_list->dirty = EINA_FALSE;
168 ecore_file_unlink(contacts->bkp);
169 ecore_file_mv(contacts->path, contacts->bkp);
170 efile = eet_open(contacts->path, EET_FILE_MODE_WRITE);
171 EINA_SAFETY_ON_NULL_GOTO(efile, failed);
172 if (!(eet_data_write(efile,
173 contacts->edd_list, CONTACTS_ENTRY,
174 contacts->c_list, EET_COMPRESSION_DEFAULT)))
175 ERR("Could in the contacts database");
177 DBG("wrote %s", contacts->path);
182 ecore_file_mv(contacts->bkp, contacts->path);
186 static void _contacts_save(Contacts *contacts)
188 if (contacts->c_list->save_poller)
191 contacts->c_list->save_poller = ecore_poller_add
192 (ECORE_POLLER_CORE, 32, _contacts_save_do, contacts);
195 static Eina_Bool _contact_info_changed_idler(void *data)
197 Contact_Info *c = data;
198 Contact_Info_On_Changed_Ctx *ctx;
200 c->changed_idler = NULL;
202 c->on_changed_cbs.walking++;
203 EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
206 ctx->cb((void *)ctx->data, c);
208 c->on_changed_cbs.walking--;
210 _contacts_save(c->contacts);
212 if (c->on_changed_cbs.walking > 0)
215 EINA_LIST_FREE(c->on_changed_cbs.deleted, ctx) {
216 c->on_changed_cbs.listeners = eina_inlist_remove(
217 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
224 static void _contact_info_changed(Contact_Info *c)
226 if (c->changed_idler)
228 c->changed_idler = ecore_idler_add(_contact_info_changed_idler, c);
231 Eina_Bool contact_info_picture_set(Contact_Info *c, const char *filename)
233 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
234 EINA_SAFETY_ON_NULL_RETURN_VAL(filename, EINA_FALSE);
236 DBG("c=%p, was=%s, new=%s", c, c->picture, filename);
238 if (eina_stringshare_replace(&(c->picture), filename))
239 _contact_info_changed(c);
244 Eina_Bool contact_info_first_name_set(Contact_Info *c, const char *name)
246 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
247 EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
249 DBG("c=%p, was=%s, new=%s", c, c->first_name, name);
251 if (eina_stringshare_replace(&(c->first_name), name)) {
252 eina_stringshare_replace(&(c->full_name), NULL);
253 _contact_info_changed(c);
259 Eina_Bool contact_info_last_name_set(Contact_Info *c, const char *name)
261 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
262 EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
264 DBG("c=%p, was=%s, new=%s", c, c->last_name, name);
266 if (eina_stringshare_replace(&(c->last_name), name)) {
267 eina_stringshare_replace(&(c->full_name), NULL);
268 _contact_info_changed(c);
274 Eina_Bool contact_info_detail_set(Contact_Info *c, const char *type, const char *number)
276 Eina_Bool changed = EINA_FALSE;
278 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
279 EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE);
280 EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE);
282 DBG("c=%p, type=%s, number=%s", c, type, number);
284 if (strcmp(type, "Mobile") == 0)
285 changed = eina_stringshare_replace(&(c->mobile), number);
286 else if (strcmp(type, "Work") == 0)
287 changed = eina_stringshare_replace(&(c->work), number);
288 else if (strcmp(type, "Home") == 0)
289 changed = eina_stringshare_replace(&(c->home), number);
291 ERR("Unknown type: %s, number: %s", type, number);
296 _contact_info_changed(c);
302 void contact_info_on_changed_callback_add(Contact_Info *c,
303 void (*cb)(void *data, Contact_Info *c),
306 Contact_Info_On_Changed_Ctx *ctx;
308 EINA_SAFETY_ON_NULL_RETURN(c);
309 EINA_SAFETY_ON_NULL_RETURN(cb);
311 ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
312 EINA_SAFETY_ON_NULL_RETURN(ctx);
316 c->on_changed_cbs.listeners = eina_inlist_append(
317 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
320 void contact_info_on_changed_callback_del(Contact_Info *c,
321 void (*cb)(void *data, Contact_Info *c),
324 Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
325 EINA_SAFETY_ON_NULL_RETURN(c);
326 EINA_SAFETY_ON_NULL_RETURN(cb);
328 EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
329 if (ctx->cb == cb && ctx->data == data) {
338 if (c->on_changed_cbs.walking > 0) {
339 found->deleted = EINA_TRUE;
340 c->on_changed_cbs.deleted = eina_list_append(
341 c->on_changed_cbs.deleted, found);
345 c->on_changed_cbs.listeners = eina_inlist_remove(
346 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
351 void contact_info_on_del_callback_add(Contact_Info *c,
352 void (*cb)(void *data, const Contact_Info *c),
355 Contact_Info_On_Del_Ctx *ctx;
357 EINA_SAFETY_ON_NULL_RETURN(c);
358 EINA_SAFETY_ON_NULL_RETURN(cb);
360 ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
361 EINA_SAFETY_ON_NULL_RETURN(ctx);
365 c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
368 void contact_info_on_del_callback_del(Contact_Info *c,
369 void (*cb)(void *data, const Contact_Info *c),
372 Contact_Info_On_Del_Ctx *ctx, *found = NULL;
373 EINA_SAFETY_ON_NULL_RETURN(c);
374 EINA_SAFETY_ON_NULL_RETURN(cb);
376 EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
377 if (ctx->cb == cb && ctx->data == data) {
386 c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
387 EINA_INLIST_GET(found));
392 static void _contact_info_on_del_dispatch(Contact_Info *c)
394 Eina_Inlist *lst = c->on_del_cbs;
395 c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
397 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
398 (lst, Contact_Info_On_Del_Ctx);
400 lst = eina_inlist_remove(lst, lst);
402 ctx->cb((void *)ctx->data, c);
407 static void _contact_info_free(Contact_Info *c_info);
409 void contact_info_del(Contact_Info *c)
413 EINA_SAFETY_ON_NULL_RETURN(c);
415 _contact_info_on_del_dispatch(c);
418 elm_object_item_del(c->it);
420 contacts = c->contacts;
421 contacts->c_list->list = eina_list_remove(contacts->c_list->list, c);
422 contacts->c_list->dirty = EINA_TRUE;
423 _contacts_save(contacts);
425 _contact_info_free(c);
428 static void _contacts_info_descriptor_init(Eet_Data_Descriptor **edd,
429 Eet_Data_Descriptor **edd_list)
431 Eet_Data_Descriptor_Class eddc;
433 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contact_Info);
434 *edd = eet_data_descriptor_stream_new(&eddc);
436 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contacts_List);
437 *edd_list = eet_data_descriptor_stream_new(&eddc);
439 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
440 "picture", picture, EET_T_STRING);
441 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
442 "work", work, EET_T_STRING);
443 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
444 "home", home, EET_T_STRING);
445 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
446 "mobile", mobile, EET_T_STRING);
447 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
448 "first_name", first_name, EET_T_STRING);
449 EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Contact_Info,
450 "last_name", last_name, EET_T_STRING);
452 EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Contacts_List, "list", list,
456 static void _contact_info_free(Contact_Info *c_info)
458 EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
460 if (c_info->on_changed_cbs.deleted) {
461 ERR("contact still have changed deleted listeners: %p %s %s",
462 c_info, c_info->first_name, c_info->last_name);
463 eina_list_free(c_info->on_changed_cbs.deleted);
466 if (c_info->on_changed_cbs.listeners) {
467 while (c_info->on_changed_cbs.listeners) {
468 Contact_Info_On_Changed_Ctx *ctx;
470 ctx = EINA_INLIST_CONTAINER_GET(
471 c_info->on_changed_cbs.listeners,
472 Contact_Info_On_Changed_Ctx);
473 c_info->on_changed_cbs.listeners = eina_inlist_remove
474 (c_info->on_changed_cbs.listeners,
475 c_info->on_changed_cbs.listeners);
480 if (c_info->changed_idler)
481 ecore_idler_del(c_info->changed_idler);
483 eina_stringshare_del(c_info->first_name);
484 eina_stringshare_del(c_info->last_name);
485 eina_stringshare_del(c_info->full_name);
486 eina_stringshare_del(c_info->mobile);
487 eina_stringshare_del(c_info->home);
488 eina_stringshare_del(c_info->work);
489 eina_stringshare_del(c_info->picture);
493 static void _on_del(void *data, Evas *e __UNUSED__,
494 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
496 Contacts *contacts = data;
497 Contact_Info *c_info;
499 if (contacts->c_list->save_poller)
500 ecore_poller_del(contacts->c_list->save_poller);
501 if (contacts->c_list->dirty)
502 _contacts_save_do(contacts);
504 eet_data_descriptor_free(contacts->edd);
505 eet_data_descriptor_free(contacts->edd_list);
506 EINA_LIST_FREE(contacts->c_list->list, c_info) {
507 _contact_info_on_del_dispatch(c_info);
508 _contact_info_free(c_info);
510 free(contacts->c_list);
511 elm_genlist_item_class_free(contacts->itc);
512 elm_genlist_item_class_free(contacts->group);
513 free(contacts->path);
519 static void _on_number_clicked(void *data, Evas_Object *obj,
520 void *event_inf __UNUSED__)
522 const char *number = data;
523 Evas_Object *contacts = evas_object_data_get(obj, "contacts");
524 printf("contacts=%p, number=%p\n", contacts, number);
525 evas_object_smart_callback_call(contacts, "selected", (void *)number);
528 static void _on_item_click(void *data, Evas_Object *obj __UNUSED__,
531 Contacts *contacts = data;
532 Elm_Object_Item *item = event_inf;
533 Evas_Object *details, *btn, *photo;
534 Contact_Info *c_info;
537 details = contacts->details;
538 c_info = elm_object_item_data_get(item);
539 elm_genlist_item_selected_set(item, EINA_FALSE);
540 elm_layout_box_remove_all(details, "box.phones", EINA_TRUE);
542 elm_object_part_text_set(details, "text.name", c_info->first_name);
543 elm_object_part_text_set(details, "text.last.name", c_info->last_name);
545 photo = picture_icon_get(details, c_info->picture);
546 elm_object_part_content_set(details, "swallow.photo", photo);
548 btn = elm_button_add(details);
549 EINA_SAFETY_ON_NULL_RETURN(btn);
550 elm_object_style_set(btn, "contacts");
551 phone = phone_format(c_info->mobile);
552 elm_object_part_text_set(btn, "elm.text.type", "Mobile");
553 elm_object_part_text_set(btn, "elm.text.phone", phone);
555 evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
557 evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
558 evas_object_show(btn);
559 evas_object_data_set(btn, "contacts", contacts->layout);
560 evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
562 elm_layout_box_append(details, "box.phones", btn);
564 btn = elm_button_add(details);
565 EINA_SAFETY_ON_NULL_RETURN(btn);
566 elm_object_style_set(btn, "contacts");
567 phone = phone_format(c_info->home);
568 elm_object_part_text_set(btn, "elm.text.type", "Home");
569 elm_object_part_text_set(btn, "elm.text.phone", phone);
571 evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
573 evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
574 evas_object_show(btn);
575 evas_object_data_set(btn, "contacts", contacts->layout);
576 evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
578 elm_layout_box_append(details, "box.phones", btn);
580 btn = elm_button_add(details);
581 EINA_SAFETY_ON_NULL_RETURN(btn);
582 elm_object_style_set(btn, "contacts");
583 phone = phone_format(c_info->work);
584 elm_object_part_text_set(btn, "elm.text.type", "Work");
585 elm_object_part_text_set(btn, "elm.text.phone", phone);
587 evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND,
589 evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
590 evas_object_show(btn);
591 evas_object_data_set(btn, "contacts", contacts->layout);
592 evas_object_smart_callback_add(btn, "clicked", _on_number_clicked,
594 elm_layout_box_append(details, "box.phones", btn);
595 elm_object_signal_emit(contacts->layout, "show,details", "gui");
598 static int _sort_by_name_cb(const void *v1, const void *v2)
600 const Contact_Info *c1, *c2;
609 r = strcmp(c1->first_name, c2->first_name);
611 return strcmp(c1->last_name, c2->last_name);
616 static void _contacts_read(Contacts *contacts)
618 Contact_Info *c_info;
621 Elm_Object_Item *it = NULL;
623 efile = eet_open(contacts->path, EET_FILE_MODE_READ);
626 contacts->c_list = eet_data_read(efile, contacts->edd_list,
631 if (!contacts->c_list) {
632 efile = eet_open(contacts->bkp, EET_FILE_MODE_READ);
634 contacts->c_list = eet_data_read(
635 efile, contacts->edd_list, CONTACTS_ENTRY);
640 if (!contacts->c_list)
641 contacts->c_list = calloc(1, sizeof(Contacts_List));
643 EINA_SAFETY_ON_NULL_RETURN(contacts->c_list);
644 contacts->c_list->list = eina_list_sort(contacts->c_list->list, 0,
647 EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
650 if (group != c_info->first_name[0]) {
651 group = c_info->first_name[0];
652 it = elm_genlist_item_append(contacts->genlist,
655 ELM_GENLIST_ITEM_GROUP,
658 elm_genlist_item_select_mode_set(it, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
660 it = elm_genlist_item_append(contacts->genlist,contacts->itc,
662 ELM_GENLIST_ITEM_NONE,
663 _on_item_click, contacts);
665 c_info->contacts = contacts;
669 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
672 Contact_Info *c_info = data;
674 if (strncmp(part, "text.contacts.", strlen("text.contacts.")))
677 part += strlen("text.contacts.");
679 if (strcmp(part, "name") == 0)
680 return strdup(c_info->first_name);
681 else if (strcmp(part, "last") == 0)
682 return strdup(c_info->last_name);
683 else if (strcmp(part, "full") == 0)
684 return strdup(contact_info_full_name_get(c_info));
686 ERR("Unexpected part name: %s", part);
690 static char *_group_label_get(void *data, Evas_Object *obj __UNUSED__,
691 const char *part __UNUSED__)
693 Contact_Info *c_info = data;
695 snprintf(buf, sizeof(buf), "%c", c_info->first_name[0]);
700 static Evas_Object *_item_content_get(void *data,
704 Contact_Info *c_info = data;
707 if (strncmp(part, "swallow.", strlen("swallow.")))
710 part += strlen("swallow.");
712 if (strcmp(part, "photo") != 0) {
713 ERR("Unexpected part name: %s", part);
717 photo = picture_icon_get(obj, c_info->picture);
722 static void _on_back_clicked(void *data, Evas_Object *obj __UNUSED__,
723 const char *emission __UNUSED__,
724 const char *source __UNUSED__)
726 Evas_Object *layout = data;
727 elm_object_signal_emit(layout, "hide,details", "gui");
730 Evas_Object *contacts_add(Evas_Object *parent)
733 const char *config_path;
734 char base_dir[PATH_MAX], *path;
736 Evas_Object *obj, *genlist, *details;
737 Elm_Genlist_Item_Class *itc, *group;
740 contacts = calloc(1, sizeof(Contacts));
741 EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
744 details = layout_add(parent, "contacts_details");
745 EINA_SAFETY_ON_NULL_GOTO(details, err_layout);
747 obj = layout_add(parent, "contacts_bg");
748 EINA_SAFETY_ON_NULL_GOTO(obj, err_obj);
750 genlist = elm_genlist_add(obj);
751 EINA_SAFETY_ON_NULL_GOTO(genlist, err_genlist);
752 elm_object_style_set(genlist, "contacts");
753 elm_genlist_homogeneous_set(genlist, EINA_TRUE);
755 itc = elm_genlist_item_class_new();
756 EINA_SAFETY_ON_NULL_GOTO(itc, err_genlist);
757 itc->item_style = "contacts";
758 itc->func.text_get = _item_label_get;
759 itc->func.content_get = _item_content_get;
760 itc->func.state_get = NULL;
761 itc->func.del = NULL;
763 group = elm_genlist_item_class_new();
764 EINA_SAFETY_ON_NULL_GOTO(group, err_group);
765 group->item_style = "group_contacts";
766 group->func.text_get = _group_label_get;
767 group->func.content_get = NULL;
768 group->func.state_get = NULL;
769 group->func.del = NULL;
770 contacts->group = group;
771 contacts->genlist = genlist;
773 contacts->layout = obj;
774 contacts->details = details;
776 elm_object_part_content_set(obj, "elm.swallow.genlist", genlist);
777 elm_object_part_content_set(obj, "elm.swallow.details", details);
779 elm_object_signal_callback_add(details, "clicked,back", "gui",
780 _on_back_clicked, obj);
782 config_path = efreet_config_home_get();
783 snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
785 ecore_file_mkpath(base_dir);
787 r = asprintf(&path, "%s/%s/contacts.eet", config_path, PACKAGE_NAME);
791 contacts->path = path;
793 r = asprintf(&path, "%s/%s/contacts.eet.bkp", config_path,
798 contacts->bkp = path;
800 _contacts_info_descriptor_init(&contacts->edd, &contacts->edd_list);
801 _contacts_read(contacts);
802 EINA_SAFETY_ON_NULL_GOTO(contacts->c_list, err_read);
804 evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
807 evas_object_data_set(obj, "contacts.ctx", contacts);
811 eet_data_descriptor_free(contacts->edd);
812 eet_data_descriptor_free(contacts->edd_list);
815 free(contacts->path);
817 elm_genlist_item_class_free(group);
819 elm_genlist_item_class_free(itc);