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