3 * Copyright 2012 Samsung Electronics Co., Ltd
5 * Licensed under the Flora License, Version 1.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.tizenopensource.org/license
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <Elementary.h>
22 #include <extended-elm.h>
25 #include <memo_string.h>
28 #include <contacts-ug.h>
29 #include <memo_autolink.h>
33 static void _autolink_popup_response_cb(void *data, Evas_Object *obj, void *event_info);
35 /* BEGIN CONVERTE BETWEEN enum anchor_t AND string */
37 static char *_autolink_anchor_type_to_string(enum anchor_t type)
51 /* END CONVERTE BETWEEN enum anchor_t AND string */
53 /* BEGIN POPUP CALLBACK */
55 static void _autolink_send_email_selected_cb(void *data, Evas_Object *obj, void *event_info)
57 struct autolink_data_t *ald = (struct autolink_data_t *)data;
58 service_h service = NULL;
59 service_create(&service);
60 service_add_extra_data(service, "RUN_TYPE", "5");
61 service_add_extra_data(service, "TO", ald->info);
63 ug_launch_common(service, UG_NAME_EMAIL);
64 _autolink_popup_response_cb(ald, ald->popup, NULL);
67 static void _autolink_send_message_selected_cb(void *data, Evas_Object *obj, void *event_info)
69 struct autolink_data_t *ald = (struct autolink_data_t *)data;
71 ug_launch_common_var(UG_NAME_MESSAGE, "TO", ald->info, NULL);
72 _autolink_popup_response_cb(ald, ald->popup, NULL);
75 static void _autolink_vioce_call_selected_cb(void *data, Evas_Object *obj, void *event_info)
77 struct autolink_data_t *ald = (struct autolink_data_t *)data;
78 bundle *bd = bundle_create();
79 char telnum[255] = {0,};
81 appsvc_set_operation(bd, APPSVC_OPERATION_CALL);
82 snprintf(telnum, sizeof(telnum), "tel:%s", ald->info);
83 appsvc_set_uri(bd, telnum);
84 appsvc_run_service(bd, 0, NULL, NULL);
87 _autolink_popup_response_cb(ald, ald->popup, NULL);
90 static void _autolink_video_call_selected_cb(void *data, Evas_Object *obj, void *event_info)
92 struct autolink_data_t *ald = (struct autolink_data_t *)data;
93 service_h service = NULL;
94 service_create(&service);
95 service_add_extra_data(service, "KEY_CALL_TYPE", "mo");
96 service_add_extra_data(service, "KEY_CALL_HANDLE", "1");
97 service_add_extra_data(service, "KEY_CALLING_PARTY_NUMBER", ald->info);
98 service_add_extra_data(service, "KEY_CLI_CAUSE", "-1");
99 service_add_extra_data(service, "KEY_FORWARDED", "-1");
100 service_set_package(service, AUL_NAME_VEDIO_CALL);
101 service_send_launch_request(service, NULL, NULL);
103 service_destroy(service);
104 _autolink_popup_response_cb(ald, ald->popup, NULL);
107 static void _autolink_email_add_to_contact_selected_cb(void *data, Evas_Object *obj,
110 struct autolink_data_t *ald = (struct autolink_data_t *)data;
113 snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_ADD_WITH_EMAIL);
115 service_h service = NULL;
116 service_create(&service);
117 service_add_extra_data(service, CT_UG_BUNDLE_TYPE, buf);
118 service_add_extra_data(service, CT_UG_BUNDLE_EMAIL, ald->info);
120 ug_launch_common(service, UG_CONTACTS_DETAILS);
121 _autolink_popup_response_cb(ald, ald->popup, NULL);
124 static void _autolink_phone_add_to_contact_selected_cb(void *data, Evas_Object *obj,
127 struct autolink_data_t *ald = (struct autolink_data_t *)data;
130 snprintf(buf, sizeof(buf), "%d", CT_UG_REQUEST_ADD_WITH_NUM);
132 service_h service = NULL;
133 service_create(&service);
134 service_add_extra_data(service, CT_UG_BUNDLE_TYPE, buf);
135 service_add_extra_data(service, CT_UG_BUNDLE_NUM, ald->info);
137 ug_launch_common(service, UG_CONTACTS_DETAILS);
138 _autolink_popup_response_cb(ald, ald->popup, NULL);
141 /* END POPUP CALLBACK */
143 static struct anchor_popup_item_t g_email_list[] = {
144 {"IDS_MEMO_OPT_SEND_EMAIL", _autolink_send_email_selected_cb, "memo"},
145 {"IDS_MEMO_BODY_ADD_TO_CONTACT", _autolink_email_add_to_contact_selected_cb, "memo"},
148 static struct anchor_popup_item_t g_phone_list[] = {
149 {"IDS_COM_BODY_VOICE_CALL", _autolink_vioce_call_selected_cb, "sys_string"},
150 {"IDS_COM_BODY_SEND_MESSAGE", _autolink_send_message_selected_cb, "sys_string"},
151 {"IDS_COM_BODY_VIDEO_CALL", _autolink_video_call_selected_cb, "sys_string"},
152 {"IDS_MEMO_BODY_ADD_TO_CONTACT", _autolink_phone_add_to_contact_selected_cb, "memo"},
155 static char *_autolink_gl_label_get(void *data, Evas_Object *obj, const char *part)
157 char *label = (char *)data;
158 if (strcmp(part, "elm.text") == 0) {
159 return strdup(label);
164 static void _autolink_popup_response_cb(void *data, Evas_Object *obj, void *event_info)
168 assert(data != NULL);
169 struct autolink_data_t *ald = (struct autolink_data_t *)data;
170 if (ald->popup != NULL) {
171 evas_object_del(ald->popup);
174 assert(ald->info != NULL);
181 void _autolink_popup_show(char *title, struct autolink_data_t *ald)
183 struct anchor_popup_item_t *list = NULL;
185 Evas_Object *box = NULL;
190 count = ARRAY_SIZE(g_email_list);
194 count = ARRAY_SIZE(g_phone_list);
200 Evas_Object *popup = elm_popup_add(ald->win_main);
202 elm_object_style_set(popup, "min_menustyle");
203 elm_object_part_text_set(popup, "title,text", title);
204 Evas_Object *btn1 = elm_button_add(popup);
205 elm_object_text_set(btn1, MEMO_I18N_CLOSE);
206 elm_object_part_content_set(popup, "button1", btn1);
207 evas_object_smart_callback_add(btn1, "clicked", _autolink_popup_response_cb, ald);
208 evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
210 static Elm_Genlist_Item_Class itc;
211 memset(&itc, 0, sizeof(Elm_Genlist_Item_Class));
212 itc.item_style = "1text";
213 itc.func.text_get = _autolink_gl_label_get;
215 Evas_Object *genlist = elm_genlist_add(popup);
216 evas_object_smart_callback_add(genlist, "selected", NULL, NULL);
217 evas_object_size_hint_weight_set(genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
218 evas_object_size_hint_align_set(genlist, EVAS_HINT_FILL, EVAS_HINT_FILL);
221 for (i = 0; i < count; ++i) {
222 elm_genlist_item_append(genlist, &itc, dgettext(list[i].domain, list[i].label),
223 NULL, ELM_GENLIST_ITEM_NONE, list[i].response, ald);
226 evas_object_show(genlist);
227 box = elm_box_add(popup);
228 evas_object_size_hint_min_set(box, 0, (count>3 ? 3 : count)*71);
229 elm_box_pack_end(box, genlist);
230 evas_object_show(box);
231 elm_object_content_set(popup, box);
232 evas_object_show(popup);
235 void autolink_anchor_clicked_cb(void *data, Evas_Object *obj, void *event_info)
237 Elm_Entry_Anchor_Info *ei = (Elm_Entry_Anchor_Info *)event_info;
238 autolink_data *ald = SMALLOC(autolink_data);
241 int info_len = strlen(ei->name) + 1;
242 ald->info = calloc(1, info_len);
243 if (ald->info == NULL) {
248 ald->win_main = (Evas_Object *)data;
249 /* retrieve anchor info, eg `url|www phone_num|8888 email|abc@k.com` */
250 switch (ei->name[0]) {
252 ald->type = ANCHOR_URL;
255 ald->type = ANCHOR_PHONE;
258 ald->type = ANCHOR_EMAIL;
261 ald->type = NOT_ANCHOR;
264 const char *start = ei->name + strlen(_autolink_anchor_type_to_string(ald->type)) +1;
265 const char *end = strchr(ei->name, ' ');
266 strncpy(ald->info, start, end - start);
267 LOGD("Anchor clicked (%s)\n", ald->info);
269 if ((ald->type == ANCHOR_EMAIL) || (ald->type == ANCHOR_PHONE)){
270 _autolink_popup_show(ald->info, ald);
274 if (ald->type == ANCHOR_URL) {
275 aul_open_content(ald->info); /* launch browser */
281 static Eina_Bool str_is_begin_with(const char *str, const char *prefix)
283 if (strlen(str) < strlen(prefix)) {
288 for (i = 0; i < strlen(prefix); ++i) {
289 if (str[i] != prefix[i]) {
297 /* TYPE CHECKER BEGIN */
299 * [Checker Function Specification]
300 * These function get target type from beginning of string.
301 * The only parameter str is the string that want to be checked.
302 * Let's assume the returned value is r. There are 3 cases:
303 * r > 0 : From beginning to r of str match the target type.
304 * r = 0 : Str doesn't have target type, no longer check.
305 * r < 0 : It doesn't match from beginning, but from -r position maybe matched.
306 * It will be checked next time.
308 static int _autolink_url_checker(const char* str)
310 char *prefix[] = {"http://", "www.", "wap."};
313 for (i = 0; i < ARRAY_SIZE(prefix); ++i) {
314 if (str_is_begin_with(str, prefix[i])) { /* Begin with specified prefix */
316 while(str[j] != '\0' && !isspace(str[j])) {
317 if (str[j] == '<') { /* tag begin */
323 if(j == strlen(prefix[i])) { /* only with prefix alone */
331 /* not matched, get next position */
332 #if 0 /* Need correct */
335 for (i = 0; i < ARRAY_SIZE(prefix); ++i) {
336 p = strstr(str, prefix[i]);
340 } else if (next > p - str) {
350 static int _autolink_email_checker(const char* str)
353 p = strchr(str, '@');
357 } else if (p == str) { /* '@' at beginning */
362 if (next == '@') { /* consecutive '@' */
363 return -(p + 2 - str);
364 } else if (next == '.') { /* "@." */
365 return -(p + 2 - str);
366 } else if (next == '\0') { /* '@' at end of string */
367 return -(p + 1 - str);
368 } else if (isspace(next) || next == '<') { /* '@' at end of word */
369 return -(p + 2 - str);
372 /* check if valid before @ */
376 return -(q + 1 - str);
379 if (*q == '>' || *q == '/') { /* tag end and other illega character */
380 return -(q + 1 - str);
383 if (*q == '.' && ( q == str || *(q - 1) == '.')) {
384 return -(q + 1 - str);
390 /* get the end position of email */
392 while (*q != '\0' && !isspace(*q)) {
393 if (*q == '.' && *(q + 1) == '.') {
397 if (*q == '<') { /* tag begin */
406 static int _autolink_phone_checker(const char *str)
408 /* search beginning */
410 for (i = 0; str[i] != '\0'; ++i) {
411 /* digit, '+', '*' and '#' at the beginning of phone is valid. */
412 if (isdigit(str[i]) || str[i] == '+' || str[i] == '*' || str[i] == '#') {
421 for (i = 1; str[i] != '\0'; ++i) {
422 if (isdigit(str[i]) || str[i] == '*' || str[i] == '#') {
424 } else if (str[i] == '-') {
425 if (str[i - 1] == '-' || str[i - 1] == ' ') { /* "--" or " -" */
429 } else if (str[i] == ' ') {
430 if (str[i - 1] == '-') { /* "- " */
433 } else if (str[i - 1] == ' ' && isdigit(str[i + 1])) { /* consecutive ' ' */
435 while (isdigit(str[i + 1 + j])) { ++j; }
437 while (str[i] == ' ') { i--; }
441 } else { /* other character */
454 /* TYEP CHECKER END */
456 static void _autolink_box_anchor(GString *text, char *type, char *content)
459 if (strcmp(type, "phone_num") == 0) { /* remove '-' and ' ' in phone num. */
460 buf = (char *)malloc(strlen(content) + 1);
463 while (content[i] != '\0') {
464 if (content[i] == '-' || content[i] == ' ') {
477 g_string_append_printf(text,
478 "<color=#72b1f2FF><a href=%s|%s underline=on underline_color=#72b1f2FF>%s</a></color>",
481 if (buf != content) {
486 GString *autolink_add_anchor(const char *content)
489 assert(content != NULL);
491 const char *p = content;
493 GString *text = g_string_new("");
494 GString *buf = g_string_new("");
497 int (*check)(const char* str);
501 {_autolink_url_checker, "url", EINA_TRUE},
502 {_autolink_email_checker, "email", EINA_TRUE},
503 {_autolink_phone_checker, "phone_num", EINA_TRUE},
508 if (p[0] == '<') { /* tag begin */
509 gt = strchr(p, '>'); /* search tag end */
510 assert(gt != NULL); /* assert '<' and '>' are matched */
511 g_string_append_len(text, p, gt - p + 1);
518 for (i = 0; i < ARRAY_SIZE(checkers); ++i) {
519 if (checkers[i].invoke == EINA_TRUE) {
520 r = checkers[i].check(p);
521 if (r == 0) { /* no longer check */
522 checkers[i].invoke = EINA_FALSE;
523 } else if (r > 0) { /* Get */
524 g_string_assign(buf, ""); /* reset buffer */
525 g_string_append_len(buf, p, r);
526 _autolink_box_anchor(text, checkers[i].type, buf->str);
530 if (next_pos <= 0 || next_pos > -r) {
538 g_string_append(text, p);
540 } else if ( i == ARRAY_SIZE(checkers)) {
541 g_string_append_len(text, p, next_pos);
547 g_string_free(buf, TRUE);
548 printf("\t\t********** %s\n", text->str);