Fix TC-2372 Dialer crashes when BT phone is offline
[profile/ivi/lemolo.git] / messages / compose.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5 #include <Ecore.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include "log.h"
9 #include "util.h"
10 #include "gui.h"
11 #include "overview.h"
12 #include "ofono.h"
13
14 typedef struct _Compose
15 {
16         Evas_Object *layout;
17         Evas_Object *entry_msg;
18         Evas_Object *mb_entry;
19         Evas_Object *genlist;
20         Evas_Object *genlist_contacts;
21         Elm_Genlist_Item_Class *itc_inc;
22         Elm_Genlist_Item_Class *itc_out;
23         Elm_Genlist_Item_Class *itc_c_name;
24         Eina_Bool composing;
25         Eina_List *current_thread;
26         const char *number;
27         Eina_List *composing_numbers;
28         Ecore_Poller *updater;
29         double last_update;
30 } Compose;
31
32 typedef struct _Contact_Genlist {
33         const char *name;
34         const char *number;
35         const char *type;
36         const char *picture;
37         Compose *compose;
38 } Contact_Genlist;
39
40 static OFono_Callback_List_Incoming_SMS_Node *incoming_sms = NULL;
41 static OFono_Callback_List_Sent_SMS_Node *sent_sms = NULL;
42
43 static void _send_sms(Compose *compose);
44 static void _compose_timer_updater_start(Compose *compose);
45
46 static void _message_remove_from_genlist(Message *msg)
47 {
48         Elm_Object_Item *it;
49
50         it = message_object_item_get(msg);
51         EINA_SAFETY_ON_NULL_RETURN(it);
52         elm_object_item_del(it);
53 }
54
55 static void _message_from_file_delete(Compose *compose, Message *msg,
56                                         const char *contact)
57 {
58         Message *msg_aux;
59         Eina_List *last;
60
61         gui_message_from_file_delete(msg, contact);
62         _message_remove_from_genlist(msg);
63
64         last = eina_list_last(compose->current_thread);
65         msg_aux = eina_list_data_get(last);
66
67         if (msg_aux == msg) {
68                 msg_aux = eina_list_data_get(eina_list_prev(last));
69                 if (msg_aux != NULL)
70                         gui_overview_genlist_update(msg_aux, contact);
71         }
72         compose->current_thread = eina_list_remove(compose->current_thread,
73                                                         msg);
74         message_del(msg);
75 }
76
77 static void _on_del(void *data, Evas *e __UNUSED__,
78                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
79 {
80         Compose *compose = data;
81         Message *msg;
82         const char *number;
83
84         ofono_incoming_sms_cb_del(incoming_sms);
85         ofono_sent_sms_changed_cb_del(sent_sms);
86         elm_genlist_item_class_free(compose->itc_inc);
87         elm_genlist_item_class_free(compose->itc_out);
88         elm_genlist_item_class_free(compose->itc_c_name);
89         EINA_LIST_FREE(compose->current_thread, msg)
90                 message_del(msg);
91
92         EINA_LIST_FREE(compose->composing_numbers, number)
93                 eina_stringshare_del(number);
94
95         eina_stringshare_del(compose->number);
96         free(compose);
97 }
98
99 static void _compose_exit(Compose *compose)
100 {
101         Message *msg;
102         const char *number;
103
104         compose->composing = EINA_TRUE;
105         eina_stringshare_replace(&(compose->number), NULL);
106         elm_object_part_text_set(compose->layout, "elm.text.name",
107                                         "New Message");
108         elm_object_signal_emit(compose->layout, "hidden,genlist", "gui");
109         elm_object_signal_emit(compose->layout, "composing", "gui");
110         elm_genlist_clear(compose->genlist);
111         elm_genlist_clear(compose->genlist_contacts);
112
113         EINA_LIST_FREE(compose->current_thread, msg)
114                 message_del(msg);
115
116         EINA_LIST_FREE(compose->composing_numbers, number)
117                 eina_stringshare_del(number);
118
119         elm_object_part_text_set(compose->entry_msg, NULL, "");
120         elm_genlist_decorate_mode_set(compose->genlist, EINA_FALSE);
121         elm_multibuttonentry_clear(compose->mb_entry);
122         elm_object_signal_emit(compose->layout, "contacts,hidden", "gui");
123         elm_object_signal_emit(compose->layout, "names_count,hidden", "gui");
124         gui_compose_exit();
125 }
126
127 static void _on_layout_clicked(void *data, Evas_Object *o,
128                         const char *emission, const char *source __UNUSED__)
129 {
130         Compose *compose = data;
131         DBG("signal: %s", emission);
132
133         EINA_SAFETY_ON_FALSE_RETURN(eina_str_has_prefix(emission, "clicked,"));
134         emission += strlen("clicked,");
135
136         if (strcmp(emission, "back") == 0)
137                 _compose_exit(compose);
138         else if (strcmp(emission, "send_msg") == 0)
139                 _send_sms(compose);
140         else if (strcmp(emission, "edit") == 0) {
141                 elm_object_signal_emit(o, "toggle,on,edit", "gui");
142                 elm_genlist_decorate_mode_set(compose->genlist, EINA_TRUE);
143         } else if (strcmp(emission, "edit,done") == 0) {
144                 elm_object_signal_emit(o, "toggle,off,edit", "gui");
145                 elm_genlist_decorate_mode_set(compose->genlist, EINA_FALSE);
146         } else if (strcmp(emission, "clear") == 0) {
147                 const char *contact = compose->number;
148                 gui_overview_all_contact_messages_clear(contact);
149                 Message *msg;
150                 EINA_LIST_FREE(compose->current_thread, msg) {
151                         _message_remove_from_genlist(msg);
152                         message_del(msg);
153                 }
154                 elm_object_signal_emit(o, "toggle,off,edit", "gui");
155         } else
156                 ERR("Unkown emission: %s", emission);
157 }
158
159 static void _send_sms_reply(void *data __UNUSED__, OFono_Error error,
160                                 OFono_Sent_SMS *sms)
161 {
162         if (error != OFONO_ERROR_NONE) {
163                 ERR("Error when trying to send a new message");
164                 return;
165         }
166
167         DBG("SMS Sent to: %s, message: %s", ofono_sent_sms_destination_get(sms),
168                 ofono_sent_sms_message_get(sms));
169 }
170
171 static void _send_sms(Compose *compose)
172 {
173         const char *msg_content;
174         char *msg_utf;
175         Message *msg;
176         Elm_Object_Item *it;
177         Contact_Info *c_info;
178         const char *to;
179         Eina_List *l;
180
181         if (elm_entry_is_empty(compose->entry_msg)) {
182                 DBG("Returning, empty message");
183                 return;
184         }
185
186         if (compose->composing && (!compose->composing_numbers)) {
187                 DBG("Returning, no contacts to send");
188                 return;
189         }
190
191         msg_content = elm_object_part_text_get(compose->entry_msg, NULL);
192
193         msg_utf = elm_entry_markup_to_utf8(msg_content);
194
195         msg = message_new(time(NULL), msg_utf,
196                                 EINA_FALSE, OFONO_SENT_SMS_STATE_PENDING);
197         EINA_SAFETY_ON_NULL_RETURN(msg);
198         message_data_set(msg, compose);
199         it = elm_genlist_item_append(compose->genlist, compose->itc_out,
200                                         msg, NULL, ELM_GENLIST_ITEM_NONE, NULL,
201                                         NULL);
202         elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
203         message_object_item_set(msg, it);
204         elm_object_signal_emit(compose->layout, "show,genlist", "gui");
205
206         compose->current_thread =
207                 eina_list_append(compose->current_thread, msg);
208         elm_object_part_text_set(compose->entry_msg, NULL, "");
209
210         _compose_timer_updater_start(compose);
211
212         if (!compose->composing) {
213                 ofono_sms_send(compose->number, msg_utf, _send_sms_reply, NULL);
214                 DBG("New Message to: %s content: %s", compose->number, msg_utf);
215                 c_info = gui_contact_search(compose->number, NULL);
216
217                 if (!c_info)
218                         elm_object_part_text_set(compose->layout,
219                                                         "elm.text.name",
220                                                         compose->number);
221                 else {
222                         to = contact_info_full_name_get(c_info);
223                         elm_object_part_text_set(compose->layout,
224                                                         "elm.text.name",
225                                                         to);
226                 }
227         } else {
228                 const char *names = NULL, *name;
229                 char size[5];
230                 const Eina_List *items;
231                 int i = 0;
232                 items = elm_multibuttonentry_items_get(compose->mb_entry);
233                 EINA_SAFETY_ON_NULL_GOTO(items, exit);
234
235                 EINA_LIST_FOREACH(compose->composing_numbers, l, to) {
236                         ofono_sms_send(to, msg_utf, _send_sms_reply, NULL);
237                         DBG("New Message to: %s content: %s", to, msg_utf);
238                         it = eina_list_nth(items, i);
239                         name = elm_object_item_part_text_get(it, NULL);
240                         if (i == 0)
241                                 names = eina_stringshare_add(name);
242                         else
243                                 names = eina_stringshare_printf("%s, %s",
244                                                                 names, name);
245                         i++;
246                 }
247
248                 elm_object_part_text_set(compose->layout,
249                                                 "elm.text.name", names);
250                 snprintf(size, sizeof(size), "%d",
251                                 eina_list_count(compose->composing_numbers));
252                 elm_object_signal_emit(compose->layout, "names_count,show",
253                                         "gui");
254                 elm_object_part_text_set(compose->layout,
255                                                 "elm.text.names_count",
256                                                 size);
257                 eina_stringshare_del(names);
258         }
259 exit:
260         free(msg_utf);
261 }
262
263 static char *_item_c_name_label_get(void *data, Evas_Object *obj __UNUSED__,
264                                         const char *part)
265 {
266         Contact_Genlist *c_genlist = data;
267
268         if (strncmp(part, "text.contact.", strlen("text.contact.")))
269                 return NULL;
270
271         part += strlen("text.contact.");
272
273         if (strcmp(part, "name") == 0)
274                 return strdup(c_genlist->name);
275         else if (strcmp(part, "number") == 0)
276                 return strdup(c_genlist->number);
277         else if (strcmp(part, "type") == 0)
278                 return strdup(c_genlist->type);
279
280         ERR("Unexpected text part: %s", part);
281         return NULL;
282 }
283
284 static Evas_Object *_item_c_name_content_get(void *data, Evas_Object *obj,
285                                                 const char *part)
286 {
287         Contact_Genlist *c_genlist = data;
288
289         if (strncmp(part, "elm.swallow.", strlen("elm.swallow.")))
290                 return NULL;
291
292         part += strlen("elm.swallow.");
293
294         if (strcmp(part, "photo") == 0)
295                 return picture_icon_get(obj, c_genlist->picture);
296
297         ERR("Unexpected part name: %s", part);
298         return NULL;
299 }
300
301
302 static char *_item_label_get(void *data, Evas_Object *obj __UNUSED__,
303                                 const char *part)
304 {
305         Message *msg = data;
306
307         if (strncmp(part, "text.msg.", strlen("text.msg.")))
308                 return NULL;
309
310         part += strlen("text.msg.");
311
312         if (strcmp(part, "content") == 0) {
313                 const char *content = message_content_get(msg);
314                 if (!content)
315                         return NULL;
316                 return elm_entry_utf8_to_markup(content);
317         }
318
319         if (strcmp(part, "time") == 0) {
320                 time_t t_msg = message_time_get(msg);
321                 if (t_msg == -1)
322                         return NULL;
323                 return date_format(t_msg);
324         }
325
326         ERR("Unexpected text part: %s", part);
327         return NULL;
328 }
329
330 static Eina_Bool _item_state_get(void *data, Evas_Object *obj __UNUSED__,
331                 const char *part)
332 {
333         Message *msg = data;
334
335         if (strcmp(part, "sent") == 0) {
336                 if (message_state_get(msg) == OFONO_SENT_SMS_STATE_SENT)
337                         return EINA_TRUE;
338                 return EINA_FALSE;
339         } else if (strcmp(part, "failed") == 0) {
340                 if (message_state_get(msg) ==  OFONO_SENT_SMS_STATE_FAILED)
341                         return EINA_TRUE;
342                 return EINA_FALSE;
343         }
344
345         ERR("Unexpected state part: %s", part);
346         return EINA_FALSE;
347 }
348
349 static void _sms_size_calc(const char *str, int *size, int *max)
350 {
351         Eina_Bool all_isprint = EINA_TRUE;
352         const char *s;
353
354         for (s = str; *s != '\0'; s++) {
355                 if (!isprint(*s)) {
356                         all_isprint = EINA_FALSE;
357                         break;
358                 }
359         }
360
361         *size = strlen(str);
362
363         if (all_isprint) {
364                 if (*size <= 160)
365                         *max = 160;
366                 else
367                         *max = ((*size / 153) + 1) * 153;
368         } else {
369                 if (*size <= 70)
370                         *max = 70;
371                 else
372                         *max = ((*size / 67) + 1) * 67;
373         }
374 }
375
376 static void _on_text_changed(void *data, Evas_Object *obj,
377                         void *event_info __UNUSED__)
378 {
379         Compose *compose = data;
380         const char *msg = elm_object_part_text_get(obj, NULL);
381         Evas_Object *ed;
382         char *msg_utf8;
383         int size, max;
384         char buf[PATH_MAX];
385         Edje_Message_Int_Set *ed_msg;
386
387         if (!msg)
388                 return;
389
390         msg_utf8 = elm_entry_markup_to_utf8(msg);
391         _sms_size_calc(msg_utf8, &size, &max);
392         snprintf(buf,sizeof(buf), "%d", size);
393         elm_object_part_text_set(compose->layout, "elm.text.size", buf);
394         snprintf(buf,sizeof(buf), "%d", max);
395         elm_object_part_text_set(compose->layout, "elm.text.max_size", buf);
396         free(msg_utf8);
397
398         ed_msg = alloca(sizeof(Edje_Message_Float_Set) + sizeof(int));
399         ed_msg->count = 2;
400         ed_msg->val[0] = size;
401         ed_msg->val[1] = max;
402         ed = elm_layout_edje_get(compose->layout);
403         edje_object_message_send(ed, EDJE_MESSAGE_INT_SET, 1, ed_msg);
404 }
405
406 static void _incoming_sms_cb(void *data, unsigned int sms_class,
407                                 time_t timestamp, const char *sender,
408                                 const char *message)
409 {
410         Compose *compose = data;
411         Message *msg;
412         Elm_Object_Item *it;
413
414         /* Users can only send class 1. This is OFono/GSM detail */
415         if (sms_class != 1)
416                 return;
417
418         if (compose->number && strcmp(compose->number, sender) != 0)
419                 return;
420
421         msg = message_new(timestamp, message, EINA_FALSE,
422                                 OFONO_SENT_SMS_STATE_SENT);
423
424         EINA_SAFETY_ON_NULL_RETURN(msg);
425         message_data_set(msg, compose);
426         it = elm_genlist_item_append(compose->genlist, compose->itc_inc, msg,
427                                         NULL, ELM_GENLIST_ITEM_NONE, NULL,
428                                         NULL);
429         elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
430         message_object_item_set(msg, it);
431         compose->current_thread =
432                 eina_list_append(compose->current_thread, msg);
433
434         _compose_timer_updater_start(compose);
435 }
436
437 static Eina_Bool _compose_time_updater(void *data)
438 {
439         Compose *ctx = data;
440         double now = ecore_loop_time_get();
441         const double interval_threshold = 30.0;
442         Elm_Object_Item *it;
443         const long long update_threshold = time(NULL) - WEEK - DAY;
444
445         if (!ctx->current_thread) {
446                 ctx->updater = NULL;
447                 return EINA_FALSE;
448         }
449
450         if (now - ctx->last_update < interval_threshold)
451                 return EINA_TRUE;
452         ctx->last_update = now;
453
454         it = elm_genlist_first_item_get(ctx->genlist);
455         for (; it != NULL; it = elm_genlist_item_next_get(it)) {
456                 Message *msg = elm_object_item_data_get(it);
457                 long long t = message_time_get(msg);
458                 if (EINA_UNLIKELY(t == 0)) {
459                         t = message_time_get(msg);
460                 }
461                 if (EINA_UNLIKELY(t < update_threshold))
462                         break;
463                 elm_genlist_item_update(it);
464         }
465
466         return EINA_TRUE;
467 }
468
469 static void _compose_timer_updater_start(Compose *compose)
470 {
471         Evas *e = evas_object_evas_get(compose->layout);
472         Eina_Bool win_focused = evas_focus_state_get(e);
473         Eina_Bool obj_visible = evas_object_visible_get(compose->layout);
474
475         DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
476                 compose->updater, win_focused, obj_visible);
477         if (compose->updater)
478                 return;
479         if (!compose->current_thread)
480                 return;
481         if ((!win_focused) || (!obj_visible))
482                 return;
483
484         DBG("start poller messages");
485         /* ECORE_POLLER_CORE is 1/8th of second. */
486         compose->updater = ecore_poller_add(ECORE_POLLER_CORE, 8 * 60,
487                                                 _compose_time_updater,
488                                                 compose);
489         _compose_time_updater(compose);
490 }
491
492 static void _compose_time_updater_stop(Compose *compose)
493 {
494         Evas *e = evas_object_evas_get(compose->layout);
495         Eina_Bool win_focused = evas_focus_state_get(e);
496         Eina_Bool obj_visible = evas_object_visible_get(compose->layout);
497
498         DBG("poller %p, win_focused=%hhu, obj_visible=%hhu",
499                 compose->updater, win_focused, obj_visible);
500         if (!compose->updater)
501                 return;
502         if (win_focused && obj_visible)
503                 return;
504
505         DBG("delete poller %p", compose->updater);
506         ecore_poller_del(compose->updater);
507         compose->updater = NULL;
508 }
509
510 static void _on_show(void *data, Evas *e __UNUSED__,
511                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
512 {
513         Compose *compose = data;
514         DBG("Overview became visible");
515         _compose_timer_updater_start(compose);
516 }
517
518 static void _on_win_focus_in(void *data, Evas *e __UNUSED__,
519                                 void *event_info __UNUSED__)
520 {
521         Compose *compose = data;
522         DBG("window is focused");
523         _compose_timer_updater_start(compose);
524 }
525
526 static void _on_win_focus_out(void *data, Evas *e __UNUSED__,
527                                 void *event_info __UNUSED__)
528 {
529         Compose *compose = data;
530         DBG("window is unfocused");
531         _compose_time_updater_stop(compose);
532 }
533
534 static void _on_hide(void *data, Evas *e __UNUSED__,
535                         Evas_Object *obj __UNUSED__, void *event __UNUSED__)
536 {
537         Compose *compose = data;
538         DBG("Overview became hidden");
539         _compose_time_updater_stop(compose);
540 }
541
542 static void _on_del_clicked(void *data, Evas_Object *obj __UNUSED__,
543                                 void *event_info __UNUSED__)
544 {
545         Message *msg = data;
546         Compose *compose = message_data_get(msg);
547
548         EINA_SAFETY_ON_NULL_RETURN(compose);
549         _message_from_file_delete(compose, msg, compose->number);
550 }
551
552 static void _on_resend_clicked(void *data, Evas_Object *obj __UNUSED__,
553                                 void *event_info __UNUSED__)
554 {
555         Message *msg = data;
556         const char *content, *number;
557         Compose *compose = message_data_get(msg);
558
559         EINA_SAFETY_ON_NULL_RETURN(compose);
560
561         content = message_content_get(msg);
562         EINA_SAFETY_ON_NULL_RETURN(content);
563         number = message_phone_get(msg);
564         EINA_SAFETY_ON_NULL_RETURN(number);
565
566         INF("%s %s", number, content);
567         ofono_sms_send(number, content, _send_sms_reply, NULL);
568 }
569
570 static Evas_Object *_item_content_get(void *data, Evas_Object *obj,
571                                 const char *part)
572 {
573         Evas_Object *btn = NULL;
574         Message *msg = data;
575
576         if (strcmp(part, "msg.swallow.delete") == 0) {
577                 btn = elm_button_add(obj);
578                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
579                 elm_object_style_set(btn, "history-delete");
580                 elm_object_text_set(btn, "delete");
581                 evas_object_smart_callback_add(btn, "clicked", _on_del_clicked,
582                                                 msg);
583                 evas_object_propagate_events_set(btn, EINA_FALSE);
584         } else if (strcmp(part, "swallow.btn.resend") == 0) {
585                 btn = elm_button_add(obj);
586                 EINA_SAFETY_ON_NULL_RETURN_VAL(btn, NULL);
587                 elm_object_style_set(btn, "compose-resend");
588                 elm_object_text_set(btn, "Failed. Try Again?");
589                 evas_object_smart_callback_add(btn, "clicked", _on_resend_clicked,
590                                                 msg);
591                 evas_object_propagate_events_set(btn, EINA_FALSE);
592         } else
593                 ERR("unknown content part '%s'", part);
594
595         return btn;
596 }
597
598 static void _sent_sms_cb(void *data, OFono_Error error, OFono_Sent_SMS *sms)
599 {
600         Compose *compose = data;
601         Eina_List *l;
602         Message *msg;
603
604         if (error != OFONO_ERROR_NONE) {
605                 ERR("OFono error - Sending a SMS");
606                 return;
607         }
608
609         EINA_LIST_FOREACH(compose->current_thread, l, msg) {
610                 const char *m_sms, *m_msg;
611                 time_t t_msg, t_sms;
612                 unsigned char state;
613
614                 m_sms = ofono_sent_sms_message_get(sms);
615                 m_msg = message_content_get(msg);
616                 t_msg = message_time_get(msg);
617                 state = message_state_get(msg);
618                 t_sms = ofono_sent_sms_timestamp_get(sms);
619
620                 if ((m_sms == m_msg && t_msg == t_sms) ||
621                         (m_sms == m_msg && state == OFONO_SENT_SMS_STATE_FAILED)) {
622                         message_state_set(msg, ofono_sent_sms_state_get(sms));
623                         elm_genlist_item_update(message_object_item_get(msg));
624                         break;
625                 }
626         }
627 }
628
629 static void _on_c_genlist_clicked(void *data, Evas_Object *obj __UNUSED__,
630                                         void *event_info)
631 {
632         Elm_Object_Item *it = event_info;
633         Contact_Genlist *c_genlist = data;
634         Compose *compose = c_genlist->compose;
635
636         elm_genlist_item_selected_set(it, EINA_FALSE);
637         /* It will be used in _item_filter_cb so we will know if this
638          * number was added already
639          */
640         elm_multibuttonentry_item_prepend(compose->mb_entry, c_genlist->number,
641                                                 NULL, NULL);
642 }
643
644 Contact_Genlist *_contact_genlist_new(const char *name,
645                                         const char *number,
646                                         const char *type,
647                                         const char *picture,
648                                         Compose *compose)
649 {
650         Contact_Genlist *c_genlist = calloc(1, sizeof(Contact_Genlist));
651         EINA_SAFETY_ON_NULL_RETURN_VAL(c_genlist, NULL);
652         c_genlist->name = eina_stringshare_add(name);
653         c_genlist->number = eina_stringshare_add(number);
654         c_genlist->type = eina_stringshare_add(type);
655         c_genlist->picture = eina_stringshare_add(picture);
656         c_genlist->compose = compose;
657         return c_genlist;
658 }
659
660 static void _on_text_mb_entry_changed(void *data, Evas_Object *obj,
661                         void *event_info __UNUSED__)
662 {
663         Compose *compose = data;
664         Contact_Partial_Match *pm;
665         Eina_List *result, *l;
666         const char *query;
667
668         query = elm_object_part_text_get(obj, NULL);
669
670         elm_genlist_clear(compose->genlist_contacts);
671
672         if ((!query) || (*query == '\0')) {
673                 elm_object_signal_emit(compose->layout,
674                                         "contacts,hidden", "gui");
675                 return;
676         }
677
678         DBG("Searching for contact with number: %s", query);
679
680         result = gui_contact_partial_match_search(query);
681         if (!result) {
682                 DBG("No contact match: '%s'", query);
683                 elm_object_signal_emit(compose->layout,
684                                         "contacts,hidden", "gui");
685                 return;
686         }
687
688         EINA_LIST_FOREACH(result, l, pm) {
689                 const Contact_Info *c_info;
690                 const char *type, *name, *number, *picture;
691                 Contact_Genlist *c_genlist;
692
693                 type = contact_partial_match_type_get(pm);
694                 c_info = contact_partial_match_info_get(pm);
695                 name = contact_info_full_name_get(c_info);
696                 number = contact_info_detail_get(c_info, type);
697                 picture = contact_info_picture_get(c_info);
698
699                 c_genlist = _contact_genlist_new(name, number, type,
700                                                         picture, compose);
701                 EINA_SAFETY_ON_NULL_RETURN(c_genlist);
702                 elm_genlist_item_append(compose->genlist_contacts,
703                                         compose->itc_c_name,
704                                         c_genlist, NULL,
705                                         ELM_GENLIST_ITEM_NONE,
706                                         _on_c_genlist_clicked,
707                                         c_genlist);
708         }
709         contact_partial_match_search_free(result);
710
711         elm_object_signal_emit(compose->layout, "contacts,show", "gui");
712 }
713
714 static void _on_item_added(void *data, Evas_Object *obj __UNUSED__,
715                                         void *event_info)
716 {
717         Compose *compose = data;
718         const char *number, *name;
719         Contact_Info *c_info;
720         Elm_Object_Item *it = event_info;
721
722         number = elm_object_item_part_text_get(it, NULL);
723         DBG("Item added number = %s", number);
724
725         elm_object_signal_emit(compose->layout, "contacts,hidden", "gui");
726
727         if (!number)
728                 return;
729
730         c_info = gui_contact_search(number, NULL);
731
732         if (!c_info) {
733                 DBG("No contact found");
734                 return;
735         }
736
737         name = contact_info_full_name_get(c_info);
738         DBG("Number=%s Name=%s", number, name);
739         elm_object_item_part_text_set(it, NULL, name);
740 }
741
742 static Eina_Bool entry_is_number(const char *str)
743 {
744         int i;
745
746         for(i = 0; str[i] != '\0'; i++) {
747                 if (isdigit(str[i]) == 0)
748                         return EINA_FALSE;
749         }
750
751         return EINA_TRUE;
752 }
753
754 static Eina_Bool _item_filter_cb(Evas_Object *obj, const char* item_label,
755                                         void *item_data __UNUSED__,
756                                         void *data)
757 {
758         Evas_Object *entry;
759         Compose *compose = data;
760         const char *number;
761         Eina_List *l;
762
763         /* Did the user entered a phone number? */
764         if (entry_is_number(item_label)) {
765                 EINA_LIST_FOREACH(compose->composing_numbers, l, number) {
766                         DBG("current= %s, number= %s", number, item_label);
767                         if (strcmp(number, item_label) == 0) {
768                                 entry = elm_multibuttonentry_entry_get(obj);
769                                 elm_object_part_text_set(entry, NULL, "");
770                                 DBG("Filtered, number already in.");
771                                 return EINA_FALSE;
772                         }
773                 }
774                 number =  eina_stringshare_add(item_label);
775                 compose->composing_numbers =
776                         eina_list_append(compose->composing_numbers, number);
777                 return EINA_TRUE;
778         }
779
780         return EINA_FALSE;
781 }
782
783 static void _item_c_genlist_del(void *data, Evas_Object *obj __UNUSED__)
784 {
785         Contact_Genlist *c_genlist = data;
786
787         DBG("Deleting item: %p name: %s number: %s", c_genlist,
788                 c_genlist->name, c_genlist->number);
789         eina_stringshare_del(c_genlist->name);
790         eina_stringshare_del(c_genlist->number);
791         eina_stringshare_del(c_genlist->type);
792         eina_stringshare_del(c_genlist->picture);
793         free(c_genlist);
794 }
795
796 Evas_Object *compose_add(Evas_Object *parent)
797 {
798         Compose *compose;
799         Evas_Object *obj, *entry, *genlist, *mb_entry;
800         Evas *e;
801
802         compose = calloc(1, sizeof(Compose));
803         EINA_SAFETY_ON_NULL_RETURN_VAL(compose, NULL);
804
805         compose->layout = obj = layout_add(parent, "compose");
806         EINA_SAFETY_ON_NULL_GOTO(obj, err_obj);
807
808         evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _on_del,
809                                         compose);
810
811         elm_object_signal_callback_add(obj, "clicked,*", "gui",
812                                         _on_layout_clicked, compose);
813
814         evas_object_data_set(obj, "compose.ctx", compose);
815
816         entry = elm_entry_add(obj);
817         EINA_SAFETY_ON_NULL_GOTO(entry, err_entry);
818         elm_object_part_content_set(obj, "elm.swallow.message", entry);
819         evas_object_smart_callback_add(entry, "changed", _on_text_changed,
820                                         compose);
821         elm_object_style_set(entry, "compose");
822         elm_entry_editable_set(entry, EINA_TRUE);
823         elm_entry_scrollable_set(entry, EINA_TRUE);
824         compose->entry_msg = entry;
825
826         mb_entry = elm_multibuttonentry_add(obj);
827         EINA_SAFETY_ON_NULL_GOTO(mb_entry, err_entry);
828         elm_object_part_content_set(obj, "elm.swallow.destination", mb_entry);
829         elm_object_style_set(mb_entry, "compose");
830         elm_object_text_set(mb_entry, "To: ");
831         elm_object_part_text_set(mb_entry, "guide", "Tap to add recipient");
832         compose->mb_entry = mb_entry;
833
834         evas_object_smart_callback_add(mb_entry, "item,added",
835                                         _on_item_added, compose);
836         /* Don't let equal numbers be added */
837         elm_multibuttonentry_item_filter_append(mb_entry, _item_filter_cb,
838                                                 compose);
839
840         entry = elm_multibuttonentry_entry_get(mb_entry);
841         EINA_SAFETY_ON_NULL_GOTO(entry, err_entry);
842         evas_object_smart_callback_add(entry, "changed",
843                                         _on_text_mb_entry_changed,
844                                         compose);
845
846         genlist = elm_genlist_add(obj);
847         EINA_SAFETY_ON_NULL_GOTO(genlist, err_genlist);
848         elm_genlist_mode_set(genlist, ELM_LIST_COMPRESS);
849         elm_object_style_set(genlist, "compose");
850         elm_object_part_content_set(obj, "elm.swallow.genlist", genlist);
851         compose->genlist = genlist;
852
853         genlist = elm_genlist_add(obj);
854         EINA_SAFETY_ON_NULL_GOTO(genlist, err_genlist);
855         elm_object_style_set(genlist, "compose");
856         elm_object_part_content_set(obj, "elm.swallow.genlist.contacts",
857                                         genlist);
858         compose->genlist_contacts = genlist;
859         elm_object_focus_allow_set(genlist, EINA_FALSE);
860
861         elm_scroller_policy_set(compose->genlist,
862                                 ELM_SCROLLER_POLICY_OFF,
863                                 ELM_SCROLLER_POLICY_AUTO);
864         elm_scroller_policy_set(compose->genlist_contacts,
865                                 ELM_SCROLLER_POLICY_OFF,
866                                 ELM_SCROLLER_POLICY_AUTO);
867
868         compose->itc_inc = elm_genlist_item_class_new();
869         EINA_SAFETY_ON_NULL_GOTO(compose->itc_inc, err_itc);
870         compose->itc_inc->item_style = "messages-incoming";
871         compose->itc_inc->func.text_get = _item_label_get;
872         compose->itc_inc->func.content_get = _item_content_get;
873         compose->itc_inc->func.state_get = NULL;
874         compose->itc_inc->func.del = NULL;
875         compose->itc_inc->decorate_all_item_style = "incoming-delete";
876         compose->itc_inc->decorate_item_style = "incoming-delete";
877
878         compose->itc_out = elm_genlist_item_class_new();
879         EINA_SAFETY_ON_NULL_GOTO(compose->itc_out, err_itc_out);
880         compose->itc_out->item_style = "messages-outgoing";
881         compose->itc_out->func.text_get = _item_label_get;
882         compose->itc_out->func.content_get = _item_content_get;
883         compose->itc_out->func.state_get = _item_state_get;
884         compose->itc_out->func.del = NULL;
885         compose->itc_out->decorate_all_item_style = "outgoing-delete";
886         compose->itc_out->decorate_item_style = "outgoing-delete";
887
888         compose->itc_c_name = elm_genlist_item_class_new();
889         EINA_SAFETY_ON_NULL_GOTO(compose->itc_c_name, err_names);
890         compose->itc_c_name->item_style = "contacts-compose";
891         compose->itc_c_name->func.text_get = _item_c_name_label_get;
892         compose->itc_c_name->func.content_get = _item_c_name_content_get;
893         compose->itc_c_name->func.state_get = NULL;
894         compose->itc_c_name->func.del = _item_c_genlist_del;
895
896         elm_object_part_text_set(compose->layout, "elm.text.name",
897                                         "New Message");
898         elm_object_signal_emit(compose->layout, "hide,genlist", "gui");
899         compose->composing = EINA_TRUE;
900         elm_object_signal_emit(compose->layout, "composing", "gui");
901
902         incoming_sms = ofono_incoming_sms_cb_add(_incoming_sms_cb, compose);
903         sent_sms = ofono_sent_sms_changed_cb_add(_sent_sms_cb, compose);
904
905         evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide,
906                                         compose);
907         evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show,
908                                         compose);
909
910         e = evas_object_evas_get(obj);
911         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
912                                 _on_win_focus_out, compose);
913         evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_FOCUS_IN,
914                                 _on_win_focus_in, compose);
915
916         return obj;
917
918 err_names:
919         elm_genlist_item_class_free(compose->itc_out);
920 err_itc_out:
921         elm_genlist_item_class_free(compose->itc_inc);
922 err_itc:
923         evas_object_del(genlist);
924 err_genlist:
925         evas_object_del(entry);
926 err_entry:
927         evas_object_del(obj);
928 err_obj:
929         free(compose);
930         return NULL;
931 }
932
933 void compose_set(Evas_Object *obj, const char *number, const char *message,
934                         Eina_Bool do_auto)
935 {
936         Compose *compose;
937
938         EINA_SAFETY_ON_NULL_RETURN(obj);
939         EINA_SAFETY_ON_NULL_RETURN(number);
940         EINA_SAFETY_ON_NULL_RETURN(message);
941
942         compose = evas_object_data_get(obj, "compose.ctx");
943         EINA_SAFETY_ON_NULL_RETURN(compose);
944
945         ERR("TODO '%s' '%s' %d", number, message, do_auto);
946 }
947
948 void compose_messages_set(Evas_Object *obj, Eina_List *list, const char *number)
949 {
950         Compose *compose;
951         Message *msg;
952         Eina_List *l;
953         Elm_Genlist_Item_Class *itc;
954         Elm_Object_Item *it = NULL;
955         Contact_Info *c_info;
956
957         EINA_SAFETY_ON_NULL_RETURN(obj);
958         compose = evas_object_data_get(obj, "compose.ctx");
959         EINA_SAFETY_ON_NULL_RETURN(compose);
960
961         eina_stringshare_replace(&(compose->number), number);
962
963         elm_genlist_clear(compose->genlist);
964
965         EINA_LIST_FOREACH(list, l, msg) {
966                 Eina_Bool outgoing = message_outgoing_get(msg);
967
968                 if (outgoing)
969                         itc = compose->itc_out;
970                 else
971                         itc = compose->itc_inc;
972                 message_data_set(msg, compose);
973                 it = elm_genlist_item_append(compose->genlist, itc, msg, NULL,
974                                                 ELM_GENLIST_ITEM_NONE, NULL,
975                                                 NULL);
976                 message_object_item_set(msg, it);
977                 message_ref(msg);
978         }
979         if (it)
980                 elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_IN);
981
982         compose->current_thread = list;
983         elm_object_signal_emit(compose->layout, "show,genlist", "gui");
984
985         c_info = gui_contact_search(number, NULL);
986
987         if (!c_info)
988                 elm_object_part_text_set(compose->layout, "elm.text.name",
989                                                 number);
990         else
991                 elm_object_part_text_set(compose->layout, "elm.text.name",
992                                             contact_info_full_name_get(c_info));
993
994         compose->composing = EINA_FALSE;
995         _compose_timer_updater_start(compose);
996         elm_object_signal_emit(compose->layout, "viewing", "gui");
997 }