4 #include <Elementary.h>
14 #ifndef EET_COMPRESSION_DEFAULT
15 #define EET_COMPRESSION_DEFAULT 1
18 #define CONTACTS_ENTRY "contacts"
20 typedef struct _Contacts_List {
23 Ecore_Poller *save_poller;
26 typedef struct _Contacts {
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;
36 struct _Contact_Info {
37 const char *first_name;
38 const char *last_name;
39 const char *full_name; /* not in edd */
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 */
50 Eina_Inlist *listeners;
53 } on_changed_cbs; /* not in edd */
56 typedef struct _Contact_Info_On_Del_Ctx {
58 void (*cb)(void *, const Contact_Info *);
60 } Contact_Info_On_Del_Ctx;
62 typedef struct _Contact_Info_On_Changed_Ctx {
64 void (*cb)(void *, Contact_Info *);
67 } Contact_Info_On_Changed_Ctx;
69 Contact_Info *contact_search(Evas_Object *obj, const char *number, const char **type)
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);
80 EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
81 if (strcmp(number, c_info->mobile) == 0) {
85 } else if (strcmp(number, c_info->work) == 0) {
89 } else if (strcmp(number, c_info->home) == 0) {
99 const char *contact_info_full_name_get(const Contact_Info *c)
103 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
108 c2 = (Contact_Info *)c;
109 c2->full_name = eina_stringshare_printf("%s %s",
110 c->first_name, c->last_name);
114 const char *contact_info_first_name_get(const Contact_Info *c)
116 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
117 return c->first_name;
120 const char *contact_info_last_name_get(const Contact_Info *c)
122 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
126 const char *contact_info_picture_get(const Contact_Info *c)
128 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
132 const char *contact_info_detail_get(const Contact_Info *c, const char *type)
134 EINA_SAFETY_ON_NULL_RETURN_VAL(c, NULL);
135 EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL);
136 if (strcmp(type, "Mobile") == 0)
138 else if (strcmp(type, "Work") == 0)
140 else if (strcmp(type, "Home") == 0)
146 const char *contact_info_number_check(const Contact_Info *c, const char *number)
148 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
149 EINA_SAFETY_ON_NULL_RETURN_VAL(number, EINA_FALSE);
151 if (strcmp(number, c->mobile) == 0)
153 else if (strcmp(number, c->work) == 0)
155 else if (strcmp(number, c->home) == 0)
161 static Eina_Bool _contacts_save_do(void *data)
163 Contacts *contacts = data;
166 contacts->c_list->save_poller = NULL;
167 contacts->c_list->dirty = EINA_FALSE;
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");
178 DBG("wrote %s", contacts->path);
183 ecore_file_mv(contacts->bkp, contacts->path);
187 static void _contacts_save(Contacts *contacts)
189 if (contacts->c_list->save_poller)
192 contacts->c_list->save_poller = ecore_poller_add
193 (ECORE_POLLER_CORE, 32, _contacts_save_do, contacts);
196 static Eina_Bool _contact_info_changed_idler(void *data)
198 Contact_Info *c = data;
199 Contact_Info_On_Changed_Ctx *ctx;
201 c->changed_idler = NULL;
203 c->on_changed_cbs.walking++;
204 EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
207 ctx->cb((void *)ctx->data, c);
209 c->on_changed_cbs.walking--;
211 _contacts_save(c->contacts);
213 if (c->on_changed_cbs.walking > 0)
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));
225 static void _contact_info_changed(Contact_Info *c)
227 if (c->changed_idler)
229 c->changed_idler = ecore_idler_add(_contact_info_changed_idler, c);
232 Eina_Bool contact_info_picture_set(Contact_Info *c, const char *filename)
234 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
235 EINA_SAFETY_ON_NULL_RETURN_VAL(filename, EINA_FALSE);
237 DBG("c=%p, was=%s, new=%s", c, c->picture, filename);
239 if (eina_stringshare_replace(&(c->picture), filename))
240 _contact_info_changed(c);
245 Eina_Bool contact_info_first_name_set(Contact_Info *c, const char *name)
247 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
248 EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
250 DBG("c=%p, was=%s, new=%s", c, c->first_name, name);
252 if (eina_stringshare_replace(&(c->first_name), name)) {
253 eina_stringshare_replace(&(c->full_name), NULL);
254 _contact_info_changed(c);
260 Eina_Bool contact_info_last_name_set(Contact_Info *c, const char *name)
262 EINA_SAFETY_ON_NULL_RETURN_VAL(c, EINA_FALSE);
263 EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
265 DBG("c=%p, was=%s, new=%s", c, c->last_name, name);
267 if (eina_stringshare_replace(&(c->last_name), name)) {
268 eina_stringshare_replace(&(c->full_name), NULL);
269 _contact_info_changed(c);
275 Eina_Bool contact_info_detail_set(Contact_Info *c, const char *type, const char *number)
277 Eina_Bool changed = EINA_FALSE;
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);
283 DBG("c=%p, type=%s, number=%s", c, type, number);
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);
292 ERR("Unknown type: %s, number: %s", type, number);
297 _contact_info_changed(c);
303 void contact_info_on_changed_callback_add(Contact_Info *c,
304 void (*cb)(void *data, Contact_Info *c),
307 Contact_Info_On_Changed_Ctx *ctx;
309 EINA_SAFETY_ON_NULL_RETURN(c);
310 EINA_SAFETY_ON_NULL_RETURN(cb);
312 ctx = calloc(1, sizeof(Contact_Info_On_Changed_Ctx));
313 EINA_SAFETY_ON_NULL_RETURN(ctx);
317 c->on_changed_cbs.listeners = eina_inlist_append(
318 c->on_changed_cbs.listeners, EINA_INLIST_GET(ctx));
321 void contact_info_on_changed_callback_del(Contact_Info *c,
322 void (*cb)(void *data, Contact_Info *c),
325 Contact_Info_On_Changed_Ctx *ctx, *found = NULL;
326 EINA_SAFETY_ON_NULL_RETURN(c);
327 EINA_SAFETY_ON_NULL_RETURN(cb);
329 EINA_INLIST_FOREACH(c->on_changed_cbs.listeners, ctx) {
330 if (ctx->cb == cb && ctx->data == data) {
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);
346 c->on_changed_cbs.listeners = eina_inlist_remove(
347 c->on_changed_cbs.listeners, EINA_INLIST_GET(found));
352 void contact_info_on_del_callback_add(Contact_Info *c,
353 void (*cb)(void *data, const Contact_Info *c),
356 Contact_Info_On_Del_Ctx *ctx;
358 EINA_SAFETY_ON_NULL_RETURN(c);
359 EINA_SAFETY_ON_NULL_RETURN(cb);
361 ctx = calloc(1, sizeof(Contact_Info_On_Del_Ctx));
362 EINA_SAFETY_ON_NULL_RETURN(ctx);
366 c->on_del_cbs = eina_inlist_append(c->on_del_cbs, EINA_INLIST_GET(ctx));
369 void contact_info_on_del_callback_del(Contact_Info *c,
370 void (*cb)(void *data, const Contact_Info *c),
373 Contact_Info_On_Del_Ctx *ctx, *found = NULL;
374 EINA_SAFETY_ON_NULL_RETURN(c);
375 EINA_SAFETY_ON_NULL_RETURN(cb);
377 EINA_INLIST_FOREACH(c->on_del_cbs, ctx) {
378 if (ctx->cb == cb && ctx->data == data) {
387 c->on_del_cbs = eina_inlist_remove(c->on_del_cbs,
388 EINA_INLIST_GET(found));
393 static void _contact_info_on_del_dispatch(Contact_Info *c)
395 Eina_Inlist *lst = c->on_del_cbs;
396 c->on_del_cbs = NULL; /* avoid contact_info_on_del_callback_del() */
398 Contact_Info_On_Del_Ctx *ctx = EINA_INLIST_CONTAINER_GET
399 (lst, Contact_Info_On_Del_Ctx);
401 lst = eina_inlist_remove(lst, lst);
403 ctx->cb((void *)ctx->data, c);
408 static void _contact_info_free(Contact_Info *c_info);
410 void contact_info_del(Contact_Info *c)
414 EINA_SAFETY_ON_NULL_RETURN(c);
416 _contact_info_on_del_dispatch(c);
419 elm_object_item_del(c->it);
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);
426 _contact_info_free(c);
429 static void _contacts_info_descriptor_init(Eet_Data_Descriptor **edd,
430 Eet_Data_Descriptor **edd_list)
432 Eet_Data_Descriptor_Class eddc;
434 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contact_Info);
435 *edd = eet_data_descriptor_stream_new(&eddc);
437 EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Contacts_List);
438 *edd_list = eet_data_descriptor_stream_new(&eddc);
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);
453 EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Contacts_List, "list", list,
457 static void _contact_info_free(Contact_Info *c_info)
459 EINA_SAFETY_ON_FALSE_RETURN(c_info->on_changed_cbs.walking == 0);
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);
467 if (c_info->on_changed_cbs.listeners) {
468 while (c_info->on_changed_cbs.listeners) {
469 Contact_Info_On_Changed_Ctx *ctx;
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);
481 if (c_info->changed_idler)
482 ecore_idler_del(c_info->changed_idler);
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);
494 static void _on_del(void *data, Evas *e __UNUSED__,
495 Evas_Object *obj __UNUSED__, void *event __UNUSED__)
497 Contacts *contacts = data;
498 Contact_Info *c_info;
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);
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);
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);
520 static void _on_number_clicked(void *data, Evas_Object *obj __UNUSED__,
521 void *event_inf __UNUSED__)
523 const char *number = data;
524 ofono_dial(number, NULL, NULL, NULL);
527 static void _on_item_click(void *data, Evas_Object *obj __UNUSED__,
530 Contacts *contacts = data;
531 Elm_Object_Item *item = event_inf;
532 Evas_Object *details, *btn, *photo;
533 Contact_Info *c_info;
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);
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);
544 photo = picture_icon_get(details, c_info->picture);
545 elm_object_part_content_set(details, "swallow.photo", photo);
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);
554 evas_object_size_hint_weight_set(btn, 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,
560 elm_layout_box_append(details, "box.phones", btn);
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);
569 evas_object_size_hint_weight_set(btn, 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,
575 elm_layout_box_append(details, "box.phones", btn);
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);
584 evas_object_size_hint_weight_set(btn, 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,
590 elm_layout_box_append(details, "box.phones", btn);
591 elm_object_signal_emit(contacts->layout, "show,details", "gui");
594 static int _sort_by_name_cb(const void *v1, const void *v2)
596 const Contact_Info *c1, *c2;
605 r = strcmp(c1->first_name, c2->first_name);
607 return strcmp(c1->last_name, c2->last_name);
612 static void _contacts_read(Contacts *contacts)
614 Contact_Info *c_info;
617 Elm_Object_Item *it = NULL;
619 efile = eet_open(contacts->path, EET_FILE_MODE_READ);
622 contacts->c_list = eet_data_read(efile, contacts->edd_list,
627 if (!contacts->c_list) {
628 efile = eet_open(contacts->bkp, EET_FILE_MODE_READ);
630 contacts->c_list = eet_data_read(
631 efile, contacts->edd_list, CONTACTS_ENTRY);
636 if (!contacts->c_list)
637 contacts->c_list = calloc(1, sizeof(Contacts_List));
639 EINA_SAFETY_ON_NULL_RETURN(contacts->c_list);
640 contacts->c_list->list = eina_list_sort(contacts->c_list->list, 0,
643 EINA_LIST_FOREACH(contacts->c_list->list, l, c_info) {
646 if (group != c_info->first_name[0]) {
647 group = c_info->first_name[0];
648 it = elm_genlist_item_append(contacts->genlist,
651 ELM_GENLIST_ITEM_GROUP,
654 elm_genlist_item_select_mode_set(it, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
656 it = elm_genlist_item_append(contacts->genlist,contacts->itc,
658 ELM_GENLIST_ITEM_NONE,
659 _on_item_click, contacts);
661 c_info->contacts = contacts;
665 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
668 Contact_Info *c_info = data;
670 if (strncmp(part, "text.contacts.", strlen("text.contacts.")))
673 part += strlen("text.contacts.");
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));
682 ERR("Unexpected part name: %s", part);
686 static char *_group_label_get(void *data, Evas_Object *obj __UNUSED__,
687 const char *part __UNUSED__)
689 Contact_Info *c_info = data;
691 snprintf(buf, sizeof(buf), "%c", c_info->first_name[0]);
696 static Evas_Object *_item_content_get(void *data,
700 Contact_Info *c_info = data;
703 if (strncmp(part, "swallow.", strlen("swallow.")))
706 part += strlen("swallow.");
708 if (strcmp(part, "photo") != 0) {
709 ERR("Unexpected part name: %s", part);
713 photo = picture_icon_get(obj, c_info->picture);
718 static void _on_back_clicked(void *data, Evas_Object *obj __UNUSED__,
719 const char *emission __UNUSED__,
720 const char *source __UNUSED__)
722 Evas_Object *layout = data;
723 elm_object_signal_emit(layout, "hide,details", "gui");
726 Evas_Object *contacts_add(Evas_Object *parent)
729 const char *config_path;
730 char base_dir[PATH_MAX], *path;
732 Evas_Object *obj, *genlist, *details;
733 Elm_Genlist_Item_Class *itc, *group;
736 contacts = calloc(1, sizeof(Contacts));
737 EINA_SAFETY_ON_NULL_RETURN_VAL(contacts, NULL);
740 details = gui_layout_add(parent, "contacts_details");
741 EINA_SAFETY_ON_NULL_GOTO(details, err_layout);
743 obj = gui_layout_add(parent, "contacts_bg");
744 EINA_SAFETY_ON_NULL_GOTO(obj, err_obj);
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);
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;
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;
769 contacts->layout = obj;
770 contacts->details = details;
772 elm_object_part_content_set(obj, "elm.swallow.genlist", genlist);
773 elm_object_part_content_set(obj, "elm.swallow.details", details);
775 elm_object_signal_callback_add(details, "clicked,back", "gui",
776 _on_back_clicked, obj);
778 config_path = efreet_config_home_get();
779 snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
781 ecore_file_mkpath(base_dir);
783 r = asprintf(&path, "%s/%s/contacts.eet", config_path, PACKAGE_NAME);
787 contacts->path = path;
789 r = asprintf(&path, "%s/%s/contacts.eet.bkp", config_path,
794 contacts->bkp = path;
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);
800 evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
803 evas_object_data_set(obj, "contacts.ctx", contacts);
807 eet_data_descriptor_free(contacts->edd);
808 eet_data_descriptor_free(contacts->edd_list);
811 free(contacts->path);
813 elm_genlist_item_class_free(group);
815 elm_genlist_item_class_free(itc);