history: implement deletion of items.
[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                 if (!call_info->name || call_info->name[0] == '\0')
635                         return phone_format(call_info->line_id);
636                 return strdup(call_info->name);
637         }
638
639         if (!strcmp(part, "time")) {
640                 if ((call_info->completed) && (call_info->end_time))
641                         return date_format(call_info->end_time);
642                 return date_format(call_info->start_time);
643         }
644
645         /* TODO: Fetch phone type from contacts information*/
646         if (!strcmp(part, "type"))
647                 return strdup("TODO:TELEPHONE TYPE");
648
649         ERR("Unexpected text part: %s", part);
650         return NULL;
651 }
652
653
654 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
655                                         const char  *part)
656 {
657         Call_Info *call_info = data;
658
659         if (!strcmp(part, "missed"))
660                 return !call_info->completed;
661         else if (!strcmp(part, "completed"))
662                 return call_info->completed;
663         else if (!strcmp(part, "outgoing"))
664                 return !call_info->incoming;
665         else if (!strcmp(part, "incoming"))
666                 return call_info->incoming;
667
668         ERR("Unexpected state part: %s", part);
669         return EINA_FALSE;
670 }
671
672 static void _on_clicked(void *data, Evas_Object *obj __UNUSED__,
673                         const char *emission, const char *source __UNUSED__)
674 {
675         History *ctx = data;
676
677         EINA_SAFETY_ON_NULL_RETURN(emission);
678         emission += strlen("clicked,");
679
680         DBG("ctx=%p, signal: %s", ctx, emission);
681
682         if (!strcmp(emission, "all"))
683                 elm_object_signal_emit(obj, "show,all", "gui");
684         else if (!strcmp(emission, "missed"))
685                 elm_object_signal_emit(obj, "show,missed", "gui");
686         else if (!strcmp(emission, "clear"))
687                 _history_clear(ctx);
688         else if (!strcmp(emission, "edit")) {
689                 elm_object_signal_emit(obj, "toggle,on,edit", "gui");
690                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_TRUE);
691                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_TRUE);
692         } else if (!strcmp(emission, "edit,done")) {
693                 elm_object_signal_emit(obj, "toggle,off,edit", "gui");
694                 elm_genlist_decorate_mode_set(ctx->genlist_all, EINA_FALSE);
695                 elm_genlist_decorate_mode_set(ctx->genlist_missed, EINA_FALSE);
696         }
697 }
698
699 static void _on_more_clicked(void *data __UNUSED__, Evas_Object *obj __UNUSED__,
700                                 const char *emission __UNUSED__,
701                                 const char *source __UNUSED__)
702 {
703         DBG("TODO");
704 }
705
706 static void _on_del_clicked(void *data, Evas_Object *obj __UNUSED__,
707                                 void *event_info __UNUSED__)
708 {
709         Call_Info *call_info = data;
710         DBG("call_info=%p, items all=%p missed=%p",
711                 call_info, call_info->it_all, call_info->it_missed);
712         _history_call_info_del(call_info);
713 }
714
715 static Evas_Object *_item_content_get(void *data, Evas_Object *obj,
716                                         const char *part)
717 {
718         Evas_Object *btn = NULL;
719
720         if (strcmp(part, "call.swallow.more") == 0) {
721                 btn = gui_layout_add(obj, "history/img");
722                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
723                 elm_object_signal_callback_add(btn, "clicked,more", "gui",
724                                                 _on_more_clicked, NULL);
725                 evas_object_propagate_events_set(btn, EINA_FALSE);
726         } else if (strcmp(part, "call.swallow.delete") == 0) {
727                 btn = elm_button_add(obj);
728                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
729                 elm_object_style_set(btn, "history-delete");
730                 elm_object_text_set(btn, "delete");
731                 evas_object_smart_callback_add(btn, "clicked", _on_del_clicked,
732                                                 data);
733                 evas_object_propagate_events_set(btn, EINA_FALSE);
734         } else
735                 ERR("unknown content part '%s'", part);
736
737         return btn;
738 }
739
740 static void _on_list_slide_enter(void *data __UNUSED__,
741                                         Evas_Object *obj,
742                                         void *event_info)
743 {
744         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
745         DBG("cancel decorated item=%p", it);
746         if (it)
747                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
748
749         it = event_info;
750         EINA_SAFETY_ON_NULL_RETURN(it);
751         DBG("it=%p", it);
752         elm_genlist_item_decorate_mode_set(it, "slide", EINA_TRUE);
753 }
754
755 static void _on_list_slide_cancel(void *data __UNUSED__,
756                                         Evas_Object *obj,
757                                         void *event_info __UNUSED__)
758 {
759         Elm_Object_Item *it = elm_genlist_decorated_item_get(obj);
760         DBG("it=%p", it);
761         if (it)
762                 elm_genlist_item_decorate_mode_set(it, "slide", EINA_FALSE);
763 }
764
765 Evas_Object *history_add(Evas_Object *parent)
766 {
767         int r;
768         History *history;
769         Evas *e;
770         const char *config_path;
771         char *path, base_dir[PATH_MAX];
772         Elm_Genlist_Item_Class *itc;
773         Evas_Object *obj, *genlist_all, *genlist_missed;
774
775         eet_init();
776         ecore_file_init();
777         history = calloc(1, sizeof(History));
778         EINA_SAFETY_ON_NULL_RETURN_VAL(history, NULL);
779
780         history->self = obj = gui_layout_add(parent, "history_bg");
781         EINA_SAFETY_ON_NULL_GOTO(obj, err_layout);
782
783         genlist_all = elm_genlist_add(obj);
784         EINA_SAFETY_ON_NULL_GOTO(genlist_all, err_object_new);
785         elm_object_style_set(genlist_all, "history");
786
787         /* TODO: */
788         evas_object_smart_callback_add(genlist_all, "drag,start,right",
789                                         _on_list_slide_enter, history);
790         evas_object_smart_callback_add(genlist_all, "drag,start,left",
791                                         _on_list_slide_cancel, history);
792         evas_object_smart_callback_add(genlist_all, "drag,start,down",
793                                         _on_list_slide_cancel, history);
794         evas_object_smart_callback_add(genlist_all, "drag,start,up",
795                                         _on_list_slide_cancel, history);
796
797         genlist_missed = elm_genlist_add(obj);
798         EINA_SAFETY_ON_NULL_GOTO(genlist_missed, err_object_new);
799         elm_object_style_set(genlist_missed, "history");
800
801         evas_object_smart_callback_add(genlist_missed, "drag,start,right",
802                                         _on_list_slide_enter, history);
803         evas_object_smart_callback_add(genlist_missed, "drag,start,left",
804                                         _on_list_slide_cancel, history);
805         evas_object_smart_callback_add(genlist_missed, "drag,start,down",
806                                         _on_list_slide_cancel, history);
807         evas_object_smart_callback_add(genlist_missed, "drag,start,up",
808                                         _on_list_slide_cancel, history);
809
810         itc = elm_genlist_item_class_new();
811         EINA_SAFETY_ON_NULL_GOTO(itc, err_object_new);
812         itc->item_style = "history";
813         itc->func.text_get = _item_label_get;
814         itc->func.content_get = _item_content_get;
815         itc->func.state_get = _item_state_get;
816         itc->func.del = NULL;
817         itc->decorate_all_item_style = "history-delete";
818         itc->decorate_item_style = "history-delete";
819         history->genlist_all = genlist_all;
820         history->genlist_missed = genlist_missed;
821         history->itc = itc;
822
823         elm_object_part_content_set(obj, "elm.swallow.all", genlist_all);
824         elm_object_part_content_set(obj, "elm.swallow.missed", genlist_missed);
825         elm_object_signal_emit(obj, "show,all", "gui");
826         elm_object_signal_callback_add(obj, "clicked,*", "gui",
827                                         _on_clicked, history);
828
829         config_path = efreet_config_home_get();
830         snprintf(base_dir, sizeof(base_dir), "%s/%s", config_path,
831                         PACKAGE_NAME);
832         ecore_file_mkpath(base_dir);
833         r = asprintf(&path,  "%s/%s/history.eet", config_path, PACKAGE_NAME);
834
835         if (r < 0)
836                 goto err_item_class;
837
838         history->path = path;
839         r = asprintf(&path,  "%s/%s/history.eet.bkp", config_path,
840                         PACKAGE_NAME);
841
842         if (r < 0)
843                 goto err_path;
844
845         history->bkp = path;
846
847         _history_call_info_descriptor_init(&history->edd, &history->edd_list);
848         _history_call_log_read(history);
849         EINA_SAFETY_ON_NULL_GOTO(history->calls, err_log_read);
850         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
851                                         history);
852         evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide,
853                                         history);
854         evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show,
855                                         history);
856
857         e = evas_object_evas_get(obj);
858         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
859                                 _on_win_focus_out, history);
860         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_IN,
861                                 _on_win_focus_in, history);
862
863         callback_node_call_changed =
864                 ofono_call_changed_cb_add(_history_call_changed, history);
865         callback_node_call_removed =
866                 ofono_call_removed_cb_add(_history_call_removed, history);
867
868         return obj;
869
870 err_log_read:
871         free(history->bkp);
872         eet_data_descriptor_free(history->edd);
873         eet_data_descriptor_free(history->edd_list);
874 err_path:
875         free(history->path);
876 err_item_class:
877         elm_genlist_item_class_free(itc);
878 err_object_new:
879         free(obj);
880 err_layout:
881         free(history);
882         ecore_file_shutdown();
883         eet_shutdown();
884         return NULL;
885 }