Now all the phone is integrated with contacts. So now it's possible to
[profile/ivi/lemolo.git] / dialer / history.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 <time.h>
8 #include <limits.h>
9 #include <string.h>
10
11 #include "ofono.h"
12 #include "log.h"
13 #include "util.h"
14 #include "gui.h"
15
16 #ifndef EET_COMPRESSION_DEFAULT
17 #define EET_COMPRESSION_DEFAULT 1
18 #endif
19
20 #define HISTORY_ENTRY "history"
21
22 typedef struct _Call_Info_List {
23         Eina_List *list;
24         Eina_Bool dirty;
25 } Call_Info_List;
26
27 typedef struct _History {
28         char *path, *bkp;
29         Eet_Data_Descriptor *edd;
30         Eet_Data_Descriptor *edd_list;
31         Call_Info_List *calls;
32         Elm_Genlist_Item_Class *itc;
33         Evas_Object *self;
34         Evas_Object *clear_popup;
35         Evas_Object *genlist_all, *genlist_missed;
36         Ecore_Poller *updater;
37         double last_update;
38 } History;
39
40 typedef struct _Call_Info {
41         long long start_time;
42         long long end_time;
43         long long creation_time; /* not in edd */
44         const char *line_id;
45         const char *name;
46         Eina_Bool completed;
47         Eina_Bool incoming;
48         const OFono_Call *call; /* not in edd */
49         History *history;
50         Elm_Object_Item *it_all; /*not in edd */
51         Elm_Object_Item *it_missed; /*not in edd */
52 } Call_Info;
53
54 static OFono_Callback_List_Call_Node *callback_node_call_removed = NULL;
55 static OFono_Callback_List_Call_Node *callback_node_call_changed = NULL;
56
57 static Eina_Bool _history_time_updater(void *data)
58 {
59         History *ctx = data;
60         Elm_Object_Item *it;
61         double now = ecore_loop_time_get();
62         const double interval_threshold = 30.0;
63         const long long update_threshold = time(NULL) - WEEK - DAY;
64
65         /*
66          * NOTE ABOUT CONSTANTS:
67          *
68          * - interval_threshold: to avoid updating too often (window
69          *   lost and gained focus, object hidden or shown), we limit
70          *   updates to this minimum interval. The poller should run
71          *   every 60 seconds.
72          *
73          * - update_threshold: since we format strings over a week as
74          *   fixed string (often day-month-year, not relative to
75          *   today), we can stop flagging list items as updated. We
76          *   give it a day of slack so we can be sure to update every
77          *   item (for held and conferences, you may have items that
78          *   are close in time but slightly out of order as items are
79          *   prepended as the calls are removed from ofono, then
80          *   history is not strictly in 'time' order). We must
81          *   stop iterating after update_threshold so users that never
82          *   deleted history and have thousand items will not
83          *   uselessly update all the thousand items.
84          */
85
86         if (!ctx->calls->list) {
87                 ctx->updater = NULL;
88                 return EINA_FALSE;
89         }
90
91         if (now - ctx->last_update < interval_threshold)
92                 return EINA_TRUE;
93         ctx->last_update = now;
94
95         it = elm_genlist_first_item_get(ctx->genlist_all);
96         for (; it != NULL; it = elm_genlist_item_next_get(it)) {
97                 const Call_Info *call_info = elm_object_item_data_get(it);
98                 long long t = call_info->end_time;
99                 if (EINA_UNLIKELY(t == 0)) {
100                         t = call_info->start_time;
101                         if (EINA_UNLIKELY(t == 0))
102                                 t = call_info->creation_time;
103                 }
104                 if (EINA_UNLIKELY(t < update_threshold))
105                         break;
106                 elm_genlist_item_update(it);
107         }
108
109         it = elm_genlist_first_item_get(ctx->genlist_missed);
110         for (; it != NULL; it = elm_genlist_item_next_get(it)) {
111                 const Call_Info *call_info = elm_object_item_data_get(it);
112                 long long t = call_info->end_time;
113                 if (EINA_UNLIKELY(t == 0)) {
114                         t = call_info->start_time;
115                         if (EINA_UNLIKELY(t == 0))
116                                 t = call_info->creation_time;
117                 }
118                 if (EINA_UNLIKELY(t < update_threshold))
119                         break;
120                 elm_genlist_item_update(it);
121         }
122
123         return EINA_TRUE;
124 }
125
126 static void _history_time_updater_stop(History *history)
127 {
128         Evas *e = evas_object_evas_get(history->self);
129         Eina_Bool win_focused = evas_focus_state_get(e);
130         Eina_Bool obj_visible = evas_object_visible_get(history->self);
131
132         DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
133                 history->updater, win_focused, obj_visible);
134         if (!history->updater)
135                 return;
136         if (win_focused && obj_visible)
137                 return;
138
139         DBG("delete poller %p", history->updater);
140         ecore_poller_del(history->updater);
141         history->updater = NULL;
142 }
143
144 static void _history_time_updater_start(History *history)
145 {
146         Evas *e = evas_object_evas_get(history->self);
147         Eina_Bool win_focused = evas_focus_state_get(e);
148         Eina_Bool obj_visible = evas_object_visible_get(history->self);
149
150         DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
151                 history->updater, win_focused, obj_visible);
152         if (history->updater)
153                 return;
154         if (!history->calls->list)
155                 return;
156         if ((!win_focused) || (!obj_visible))
157                 return;
158
159         DBG("start poller");
160         /* ECORE_POLLER_CORE is 1/8th of second. */
161         history->updater = ecore_poller_add(ECORE_POLLER_CORE, 8 * 60,
162                                                 _history_time_updater,
163                                                 history);
164         _history_time_updater(history);
165 }
166
167 static Call_Info *_history_call_info_search(const History *history,
168                                                 const OFono_Call *call)
169 {
170         Call_Info *call_info;
171         Eina_List *l;
172         long long t = ofono_call_full_start_time_get(call);
173         const char *line_id = ofono_call_line_id_get(call); /* stringshare */
174
175         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
176                 if (call_info->call == call)
177                         return call_info;
178                 else if (!call_info->call) {
179                         if ((t > 0) && (call_info->start_time == t) &&
180                                 (line_id == call_info->line_id)) {
181                                 DBG("associated existing log %p %s (%lld) with "
182                                         "call %p %s (%lld)",
183                                         call_info,
184                                         call_info->line_id,
185                                         call_info->start_time,
186                                         call, line_id, t);
187                                 call_info->call = call;
188                                 return call_info;
189                         }
190                 }
191         }
192
193         return NULL;
194 }
195
196 static Eina_Bool _history_call_info_update(Call_Info *call_info)
197 {
198         OFono_Call_State state;
199
200         EINA_SAFETY_ON_NULL_RETURN_VAL(call_info->call, EINA_FALSE);
201         state = ofono_call_state_get(call_info->call);
202
203         if (state == OFONO_CALL_STATE_INCOMING ||
204                 state == OFONO_CALL_STATE_WAITING) {
205                 if (!call_info->incoming) {
206                         call_info->incoming = EINA_TRUE;
207                         return EINA_TRUE;
208                 }
209         } else if (state == OFONO_CALL_STATE_DIALING ||
210                         state == OFONO_CALL_STATE_ALERTING) {
211                 if (!call_info->incoming) {
212                         call_info->incoming = EINA_FALSE;
213                         return EINA_TRUE;
214                 }
215         } else if (state == OFONO_CALL_STATE_ACTIVE ||
216                         state == OFONO_CALL_STATE_HELD) {
217                 if (!call_info->completed) {
218                         call_info->start_time = ofono_call_full_start_time_get
219                                 (call_info->call);
220                         if (call_info->start_time == 0)
221                                 call_info->start_time = call_info->creation_time;
222
223                         call_info->completed = EINA_TRUE;
224                         return EINA_TRUE;
225                 }
226         }
227
228         return EINA_FALSE;
229 }
230
231 static void _dial_reply(void *data, OFono_Error err,
232                         OFono_Call *call __UNUSED__)
233 {
234         const char *number = data;
235
236         if (err != OFONO_ERROR_NONE) {
237                 char buf[1024];
238                 snprintf(buf, sizeof(buf), "Could not call: %s", number);
239                 gui_simple_popup("Error", buf);
240         }
241 }
242
243 static void _on_item_clicked(void *data, Evas_Object *obj __UNUSED__,
244                                 void *event_info)
245 {
246         Elm_Object_Item *it = event_info;
247         const char *number = data;
248
249         INF("call %s", number);
250         ofono_dial(number, NULL, _dial_reply, number);
251         elm_genlist_item_selected_set(it, EINA_FALSE);
252 }
253
254 static void _history_call_changed(void *data, OFono_Call *call)
255 {
256         History *history = data;
257         const char *line_id = ofono_call_line_id_get(call);
258         Call_Info *call_info;
259         OFono_Call_State state = ofono_call_state_get(call);
260
261         call_info = _history_call_info_search(history, call);
262         DBG("call=%p, id=%s, state=%d, completed=%d, incoming=%d, info=%p",
263                 call, line_id, state,
264                 call_info ? call_info->completed : EINA_FALSE,
265                 call_info ? call_info->incoming : EINA_FALSE,
266                 call_info);
267
268         if (call_info)
269                 goto end;
270
271         call_info = calloc(1, sizeof(Call_Info));
272         EINA_SAFETY_ON_NULL_RETURN(call_info);
273
274         call_info->call = call;
275         call_info->start_time = ofono_call_full_start_time_get(call);
276         call_info->creation_time = time(NULL);
277         if (call_info->start_time == 0)
278                 call_info->start_time = call_info->creation_time;
279         call_info->line_id = eina_stringshare_add(line_id);
280         call_info->name = eina_stringshare_add(ofono_call_name_get(call));
281         history->calls->list =
282                 eina_list_prepend(history->calls->list, call_info);
283         history->calls->dirty = EINA_TRUE;
284
285 end:
286         if (_history_call_info_update(call_info))
287                 history->calls->dirty = EINA_TRUE;
288 }
289
290 static void _history_call_log_save(History *history)
291 {
292         Eet_File *efile;
293
294         EINA_SAFETY_ON_NULL_RETURN(history->calls);
295         DBG("save history (%u calls, dirty: %d) to %s",
296                 eina_list_count(history->calls->list), history->calls->dirty,
297                 history->path);
298
299         ecore_file_unlink(history->bkp);
300         ecore_file_mv(history->path, history->bkp);
301         efile = eet_open(history->path, EET_FILE_MODE_WRITE);
302         EINA_SAFETY_ON_NULL_RETURN(efile);
303         if (!(eet_data_write(efile,
304                                 history->edd_list, HISTORY_ENTRY,
305                                 history->calls, EET_COMPRESSION_DEFAULT)))
306                 ERR("Could in the history log file");
307
308         eet_close(efile);
309 }
310
311 static void _history_call_removed(void *data, OFono_Call *call)
312 {
313         Elm_Object_Item *it;
314         History *history = data;
315         const char *line_id = ofono_call_line_id_get(call);
316         Call_Info *call_info;
317         time_t start;
318         char *tm;
319
320         call_info = _history_call_info_search(history, call);
321         DBG("call=%p, id=%s, info=%p", call, line_id, call_info);
322         EINA_SAFETY_ON_NULL_RETURN(call_info);
323
324         if (call_info->start_time == 0)
325                 call_info->start_time = call_info->creation_time;
326
327         start = call_info->start_time;
328         tm = ctime(&start);
329
330         call_info->end_time = time(NULL);
331         call_info->call = NULL;
332
333         if (call_info->completed)
334                 INF("Call end:  %s at %s", line_id, tm);
335         else {
336                 if (!call_info->incoming)
337                         INF("Not answered: %s at %s", line_id, tm);
338                 else {
339                         INF("Missed: %s at %s", line_id, tm);
340                         if (call_info->it_missed)
341                                 elm_genlist_item_update(call_info->it_missed);
342                         else {
343                                 it = elm_genlist_item_prepend
344                                         (history->genlist_missed,
345                                                 history->itc,
346                                                 call_info, NULL,
347                                                 ELM_GENLIST_ITEM_NONE,
348                                                 _on_item_clicked,
349                                                 call_info->line_id);
350                                 elm_genlist_item_show
351                                         (it, ELM_GENLIST_ITEM_SCROLLTO_IN);
352                                 call_info->it_missed = it;
353                                 call_info->history = history;
354                                 _history_time_updater_start(history);
355                         }
356                 }
357         }
358
359         history->calls->dirty = EINA_TRUE;
360         _history_call_log_save(history);
361
362         if (call_info->it_all)
363                 elm_genlist_item_update(call_info->it_all);
364         else {
365                 it = elm_genlist_item_prepend(history->genlist_all,
366                                                 history->itc,
367                                                 call_info, NULL,
368                                                 ELM_GENLIST_ITEM_NONE,
369                                                 _on_item_clicked,
370                                                 call_info->line_id);
371                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
372                 call_info->it_all = it;
373                 call_info->history = history;
374                 _history_time_updater_start(history);
375         }
376 }
377
378 static void _call_info_free(Call_Info *call_info)
379 {
380         eina_stringshare_del(call_info->line_id);
381         eina_stringshare_del(call_info->name);
382         free(call_info);
383 }
384
385 static void _on_del(void *data, Evas *e __UNUSED__,
386                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
387 {
388         History *history = data;
389         Call_Info *call_info;
390
391         if (history->updater)
392                 ecore_poller_del(history->updater);
393
394         if (history->calls->dirty)
395                 _history_call_log_save(history);
396
397         ofono_call_removed_cb_del(callback_node_call_removed);
398         ofono_call_changed_cb_del(callback_node_call_changed);
399         eet_data_descriptor_free(history->edd);
400         eet_data_descriptor_free(history->edd_list);
401         EINA_LIST_FREE(history->calls->list, call_info)
402                 _call_info_free(call_info);
403         free(history->calls);
404         elm_genlist_item_class_free(history->itc);
405         free(history->path);
406         free(history->bkp);
407         free(history);
408         ecore_file_shutdown();
409         eet_shutdown();
410 }
411
412 static void _on_hide(void *data, Evas *e __UNUSED__,
413                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
414 {
415         History *history = data;
416         DBG("history became hidden");
417         _history_time_updater_stop(history);
418 }
419
420 static void _on_show(void *data, Evas *e __UNUSED__,
421                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
422 {
423         History *history = data;
424         DBG("history became visible");
425         _history_time_updater_start(history);
426 }
427
428 static void _on_win_focus_out(void *data, Evas *e __UNUSED__,
429                                 void *event_info __UNUSED__)
430 {
431         History *history = data;
432         DBG("window is unfocused");
433         _history_time_updater_stop(history);
434 }
435
436 static void _on_win_focus_in(void *data, Evas *e __UNUSED__,
437                                 void *event_info __UNUSED__)
438 {
439         History *history = data;
440         DBG("window is focused");
441         _history_time_updater_start(history);
442 }
443
444 static void _history_call_info_descriptor_init(Eet_Data_Descriptor **edd,
445                                                 Eet_Data_Descriptor **edd_list)
446 {
447         Eet_Data_Descriptor_Class eddc;
448
449         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info);
450         *edd = eet_data_descriptor_stream_new(&eddc);
451
452         EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Call_Info_List);
453         *edd_list = eet_data_descriptor_stream_new(&eddc);
454
455         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
456                                         "completed", completed, EET_T_UCHAR);
457         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
458                                         "incoming", incoming, EET_T_UCHAR);
459
460         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
461                                         "start_time", start_time, EET_T_LONG_LONG);
462         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
463                                         "end_time", end_time, EET_T_LONG_LONG);
464         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
465                                         "line_id", line_id, EET_T_STRING);
466         EET_DATA_DESCRIPTOR_ADD_BASIC(*edd, Call_Info,
467                                         "name", name, EET_T_STRING);
468
469         EET_DATA_DESCRIPTOR_ADD_LIST(*edd_list, Call_Info_List, "list", list,
470                                         *edd);
471 }
472
473 static void _history_call_log_read(History *history)
474 {
475         Call_Info *call_info;
476         Eina_List *l;
477         Eet_File *efile;
478         Call_Info_List *calls = NULL;
479         Elm_Object_Item *it;
480
481         efile = eet_open(history->path, EET_FILE_MODE_READ);
482
483         if (efile) {
484                 calls = eet_data_read(efile, history->edd_list, HISTORY_ENTRY);
485                 eet_close(efile);
486         }
487
488         if (!calls) {
489                 efile = eet_open(history->bkp, EET_FILE_MODE_READ);
490                 if (efile) {
491                         calls = eet_data_read(efile, history->edd_list,
492                                                 HISTORY_ENTRY);
493                         eet_close(efile);
494                 }
495         }
496
497         if (!calls)
498                 calls = calloc(1, sizeof(Call_Info_List));
499
500         history->calls = calls;
501         EINA_SAFETY_ON_NULL_RETURN(history->calls);
502
503         EINA_LIST_FOREACH(history->calls->list, l, call_info) {
504                 it = elm_genlist_item_append(history->genlist_all,
505                                                 history->itc,
506                                                 call_info, NULL,
507                                                 ELM_GENLIST_ITEM_NONE,
508                                                 _on_item_clicked,
509                                                 call_info->line_id);
510                 call_info->it_all = it;
511                 call_info->history = history;
512
513                 if (call_info->completed)
514                         continue;
515
516                 it = elm_genlist_item_append(history->genlist_missed,
517                                                 history->itc, call_info, NULL,
518                                                 ELM_GENLIST_ITEM_NONE,
519                                                 _on_item_clicked,
520                                                 call_info->line_id);
521                 call_info->it_missed = it;
522                 call_info->history = history;
523         }
524
525         it = elm_genlist_first_item_get(history->genlist_all);
526         if (it)
527                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
528
529         it = elm_genlist_first_item_get(history->genlist_missed);
530         if (it)
531                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
532 }
533
534 static void _history_call_info_del(Call_Info *call_info)
535 {
536         History *ctx = call_info->history;
537
538         EINA_SAFETY_ON_NULL_RETURN(ctx);
539
540         call_info->call = NULL;
541         if (call_info->it_all)
542                 elm_object_item_del(call_info->it_all);
543         if (call_info->it_missed)
544                 elm_object_item_del(call_info->it_missed);
545
546         ctx->calls->list = eina_list_remove(ctx->calls->list, call_info);
547         ctx->calls->dirty = EINA_TRUE;
548         _history_call_log_save(ctx);
549
550         if ((!ctx->calls->list) && (ctx->updater)) {
551                 ecore_poller_del(ctx->updater);
552                 ctx->updater = NULL;
553         }
554
555         _call_info_free(call_info);
556 }
557
558 static void _history_clear_do(void *data, Evas_Object *obj __UNUSED__,
559                                 void *event_info __UNUSED__)
560 {
561         History *ctx = data;
562         Call_Info *call_info;
563
564         DBG("ctx=%p, deleting %u entries",
565                 ctx, eina_list_count(ctx->calls->list));
566
567         evas_object_del(ctx->clear_popup);
568         ctx->clear_popup = NULL;
569
570         elm_genlist_clear(ctx->genlist_all);
571         elm_genlist_clear(ctx->genlist_missed);
572
573         EINA_LIST_FREE(ctx->calls->list, call_info)
574                 _call_info_free(call_info);
575
576         ctx->calls->dirty = EINA_TRUE;
577         _history_call_log_save(ctx);
578
579         if (ctx->updater) {
580                 ecore_poller_del(ctx->updater);
581                 ctx->updater = NULL;
582         }
583
584         elm_object_signal_emit(ctx->self, "toggle,off,edit", "gui");
585 }
586
587 static void _history_clear_cancel(void *data, Evas_Object *obj __UNUSED__,
588                                         void *event_info __UNUSED__)
589 {
590         History *ctx = data;
591
592         DBG("ctx=%p", ctx);
593
594         evas_object_del(ctx->clear_popup);
595         ctx->clear_popup = NULL;
596 }
597
598 static void _history_clear(History *ctx)
599 {
600         Evas_Object *p, *bt;
601
602         EINA_SAFETY_ON_TRUE_RETURN(ctx->clear_popup != NULL);
603
604         ctx->clear_popup = p = elm_popup_add(ctx->self);
605         evas_object_size_hint_weight_set(p, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
606         elm_object_part_text_set(p, "title,text", "Clear History");
607         elm_object_text_set(p, "Do you want to clear all history entries?");
608
609         bt = elm_button_add(p);
610         elm_object_text_set(bt, "No");
611         elm_object_part_content_set(p, "button1", bt);
612         evas_object_smart_callback_add(bt, "clicked",
613                                         _history_clear_cancel, ctx);
614
615         bt = elm_button_add(p);
616         elm_object_text_set(bt, "Yes, Clear");
617         elm_object_part_content_set(p, "button2", bt);
618         evas_object_smart_callback_add(bt, "clicked", _history_clear_do, ctx);
619
620         evas_object_show(p);
621 }
622
623 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
624                                 const char *part)
625 {
626         Call_Info *call_info = data;
627
628         if (strncmp(part, "text.call", strlen("text.call")))
629                 return NULL;
630
631         part += strlen("text.call.");
632
633         if (!strcmp(part, "name")) {
634                 Contact_Info *info = gui_contact_search(call_info->line_id, NULL);
635                 if(!info)
636                         return strdup(call_info->line_id);
637                 return strdup(contact_info_name_get(info));
638         }
639
640         if (!strcmp(part, "time")) {
641                 if ((call_info->completed) && (call_info->end_time))
642                         return date_format(call_info->end_time);
643                 return date_format(call_info->start_time);
644         }
645
646         if (!strcmp(part, "type")) {
647                 const char *type;
648                 Contact_Info *info = gui_contact_search(call_info->line_id, &type);
649                 if (!info)
650                         return strdup("Unknown");
651                 return strdup(type);
652         }
653
654         ERR("Unexpected text part: %s", part);
655         return NULL;
656 }
657
658
659 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
660                                         const char  *part)
661 {
662         Call_Info *call_info = data;
663
664         if (!strcmp(part, "missed"))
665                 return !call_info->completed;
666         else if (!strcmp(part, "completed"))
667                 return call_info->completed;
668         else if (!strcmp(part, "outgoing"))
669                 return !call_info->incoming;
670         else if (!strcmp(part, "incoming"))
671                 return call_info->incoming;
672
673         ERR("Unexpected state part: %s", part);
674         return EINA_FALSE;
675 }
676
677 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
678                         const char *emission, const char *source __UNUSED__)
679 {
680         History *ctx = data;
681
682         EINA_SAFETY_ON_NULL_RETURN(emission);
683         emission += strlen("clicked,");
684
685         DBG("ctx=%p, signal: %s", ctx, emission);
686
687         if (!strcmp(emission, "all"))
688                 elm_object_signal_emit(obj, "show,all", "gui");
689         else if (!strcmp(emission, "missed"))
690                 elm_object_signal_emit(obj, "show,missed", "gui");
691         else if (!strcmp(emission, "clear"))
692                 _history_clear(ctx);
693         else if (!strcmp(emission, "edit")) {
694                 elm_object_signal_emit(obj, "toggle,on,edit", "gui");
695                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_TRUE);
696                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_TRUE);
697         } else if (!strcmp(emission, "edit,done")) {
698                 elm_object_signal_emit(obj, "toggle,off,edit", "gui");
699                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_FALSE);
700                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_FALSE);
701         }
702 }
703
704 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
705                                 const char *emission __UNUSED__,
706                                 const char *source __UNUSED__)
707 {
708         DBG("TODO");
709 }
710
711 static void _on_del_clicked(void *data, Evas_Object *obj __UNUSED__,
712                                 void *event_info __UNUSED__)
713 {
714         Call_Info *call_info = data;
715         DBG("call_info=%p, items all=%p missed=%p",
716                 call_info, call_info->it_all, call_info->it_missed);
717         _history_call_info_del(call_info);
718 }
719
720 static Evas_Object *_item_content_get(void *data, Evas_Object *obj,
721                                         const char *part)
722 {
723         Evas_Object *btn = NULL;
724
725         if (strcmp(part, "call.swallow.more") == 0) {
726                 btn = gui_layout_add(obj, "history/img");
727                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
728                 elm_object_signal_callback_add(btn, "clicked,more", "gui",
729                                                 _on_more_clicked, NULL);
730                 evas_object_propagate_events_set(btn, EINA_FALSE);
731         } else if (strcmp(part, "call.swallow.delete") == 0) {
732                 btn = elm_button_add(obj);
733                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
734                 elm_object_style_set(btn, "history-delete");
735                 elm_object_text_set(btn, "delete");
736                 evas_object_smart_callback_add(btn, "clicked", _on_del_clicked,
737                                                 data);
738                 evas_object_propagate_events_set(btn, EINA_FALSE);
739         } else
740                 ERR("unknown content part '%s'", part);
741
742         return btn;
743 }
744
745 static void _on_list_slide_enter(void *data __UNUSED__,
746                                         Evas_Object *obj,
747                                         void *event_info)
748 {
749         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
750         DBG("cancel decorated item=%p", it);
751         if (it)
752                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
753
754         it = event_info;
755         EINA_SAFETY_ON_NULL_RETURN(it);
756         DBG("it=%p", it);
757         elm_genlist_item_decorate_mode_set(it, "slide", EINA_TRUE);
758 }
759
760 static void _on_list_slide_cancel(void *data __UNUSED__,
761                                         Evas_Object *obj,
762                                         void *event_info __UNUSED__)
763 {
764         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
765         DBG("it=%p", it);
766         if (it)
767                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
768 }
769
770 Evas_Object *history_add(Evas_Object *parent)
771 {
772         int r;
773         History *history;
774         Evas *e;
775         const char *config_path;
776         char *path, base_dir[PATH_MAX];
777         Elm_Genlist_Item_Class *itc;
778         Evas_Object *obj, *genlist_all, *genlist_missed;
779
780         eet_init();
781         ecore_file_init();
782         history = calloc(1, sizeof(History));
783         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
784
785         history->self = obj = gui_layout_add(parent, "history_bg");
786         EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
787
788         genlist_all = elm_genlist_add(obj);
789         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
790         elm_object_style_set(genlist_all, "history");
791
792         /* TODO: */
793         evas_object_smart_callback_add(genlist_all, "drag,start,right",
794                                         _on_list_slide_enter, history);
795         evas_object_smart_callback_add(genlist_all, "drag,start,left",
796                                         _on_list_slide_cancel, history);
797         evas_object_smart_callback_add(genlist_all, "drag,start,down",
798                                         _on_list_slide_cancel, history);
799         evas_object_smart_callback_add(genlist_all, "drag,start,up",
800                                         _on_list_slide_cancel, history);
801
802         genlist_missed = elm_genlist_add(obj);
803         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
804         elm_object_style_set(genlist_missed, "history");
805
806         evas_object_smart_callback_add(genlist_missed, "drag,start,right",
807                                         _on_list_slide_enter, history);
808         evas_object_smart_callback_add(genlist_missed, "drag,start,left",
809                                         _on_list_slide_cancel, history);
810         evas_object_smart_callback_add(genlist_missed, "drag,start,down",
811                                         _on_list_slide_cancel, history);
812         evas_object_smart_callback_add(genlist_missed, "drag,start,up",
813                                         _on_list_slide_cancel, history);
814
815         itc = elm_genlist_item_class_new();
816         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
817         itc->item_style = "history";
818         itc->func.text_get = _item_label_get;
819         itc->func.content_get = _item_content_get;
820         itc->func.state_get = _item_state_get;
821         itc->func.del = NULL;
822         itc->decorate_all_item_style = "history-delete";
823         itc->decorate_item_style = "history-delete";
824         history->genlist_all = genlist_all;
825         history->genlist_missed = genlist_missed;
826         history->itc = itc;
827
828         elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
829         elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
830         elm_object_signal_emit(obj, "show,all", "gui");
831         elm_object_signal_callback_add(obj, "clicked,*", "gui",
832                                         _on_clicked, history);
833
834         config_path = efreet_config_home_get();
835         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
836                         PACKAGE_NAME);
837         ecore_file_mkpath(base_dir);
838         r = asprintf(&path,  "%s/%s/history.eet", config_path, PACKAGE_NAME);
839
840         if (r < 0)
841                 goto err_item_class;
842
843         history->path = path;
844         r = asprintf(&path,  "%s/%s/history.eet.bkp", config_path,
845                         PACKAGE_NAME);
846
847         if (r < 0)
848                 goto err_path;
849
850         history->bkp = path;
851
852         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
853         _history_call_log_read(history);
854         EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
855         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
856                                         history);
857         evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide,
858                                         history);
859         evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show,
860                                         history);
861
862         e = evas_object_evas_get(obj);
863         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
864                                 _on_win_focus_out, history);
865         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_IN,
866                                 _on_win_focus_in, history);
867
868         callback_node_call_changed =
869                 ofono_call_changed_cb_add(_history_call_changed, history);
870         callback_node_call_removed =
871                 ofono_call_removed_cb_add(_history_call_removed, history);
872
873         return obj;
874
875 err_log_read:
876         free(history->bkp);
877         eet_data_descriptor_free(history->edd);
878         eet_data_descriptor_free(history->edd_list);
879 err_path:
880         free(history->path);
881 err_item_class:
882         elm_genlist_item_class_free(itc);
883 err_object_new:
884         free(obj);
885 err_layout:
886         free(history);
887         ecore_file_shutdown();
888         eet_shutdown();
889         return NULL;
890 }