#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "enventor_private.h" #define QUEUE_SIZE 20 #define COMPSET_PAIR_MINIMUM 1 #define MAX_CONTEXT_STACK 40 #define MAX_KEYWORD_LENGTH 40 typedef struct ctx_lexem_thread_data_s ctx_lexem_td; typedef struct lexem_s { Eina_List *nodes; char **txt; int txt_count; int cursor_offset; int line_back; char **name; int name_count; int dot; } lexem; typedef struct autocomp_s { char queue[QUEUE_SIZE]; int queue_pos; lexem *lexem_root; lexem *lexem_ptr; Eet_File *source_file; edit_data *ed; Evas_Object *anchor; Evas_Object *list; Evas_Object *event_rect; Ecore_Thread *init_thread; ctx_lexem_td *cltd; Eina_Bool anchor_visible : 1; Eina_Bool initialized : 1; Eina_Bool enabled : 1; Eina_Bool dot_candidate : 1; Eina_Bool on_keygrab : 1; Eina_Bool term: 1; } autocomp_data; struct ctx_lexem_thread_data_s { char *utf8; int cur_pos; lexem *result; edit_data *ed; autocomp_data *ad; Ecore_Thread *thread; Eina_Bool list_show : 1; }; static autocomp_data *g_ad = NULL; /*****************************************************************************/ /* Internal method implementation */ /*****************************************************************************/ static Eet_Data_Descriptor *lex_desc = NULL; static void candidate_list_show(autocomp_data *ad); static void queue_reset(autocomp_data *ad); static void entry_anchor_off(autocomp_data *ad); static void anchor_key_down_cb(void *data, Evas *evas, Evas_Object *obj, void *event_info); static void eddc_init(void) { Eet_Data_Descriptor_Class eddc; EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, lexem); lex_desc = eet_data_descriptor_file_new(&eddc); EET_DATA_DESCRIPTOR_ADD_LIST(lex_desc, lexem, "nodes", nodes, lex_desc); EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(lex_desc, lexem, "txt", txt); EET_DATA_DESCRIPTOR_ADD_BASIC(lex_desc, lexem, "cursor_offset", cursor_offset, EET_T_INT); EET_DATA_DESCRIPTOR_ADD_BASIC(lex_desc, lexem, "line_back", line_back, EET_T_INT); EET_DATA_DESCRIPTOR_ADD_BASIC(lex_desc, lexem, "dot", dot, EET_T_INT); EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(lex_desc, lexem, "name", name); } static void autocomp_load(autocomp_data *ad) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/autocomp/autocomp.eet", eina_prefix_data_get(PREFIX)); if (ad->source_file) eet_close(ad->source_file); ad->source_file = eet_open(buf, EET_FILE_MODE_READ); ad->lexem_root = (lexem *)eet_data_read(ad->source_file, lex_desc, "node"); ad->lexem_ptr = ad->lexem_root; } static void init_thread_cb(void *data, Ecore_Thread *thread EINA_UNUSED) { autocomp_data *ad = data; eddc_init(); autocomp_load(ad); } static void lexem_tree_free(lexem *node) { if (!node) return; lexem *child; EINA_LIST_FREE(node->nodes, child) lexem_tree_free(child); free(node->txt); free(node->name); free(node); } static void context_lexem_thread_cb(void *data, Ecore_Thread *thread) { const int quot_len = QUOT_UTF8_LEN; ctx_lexem_td *td = (ctx_lexem_td *)data; if (!td->ad || !td->utf8) return; int cur_pos = td->cur_pos; if (cur_pos <= 1) return; Eina_List *l = NULL; Eina_List *nodes = td->ad->lexem_root->nodes; td->result = td->ad->lexem_root; int i = 0; int k = 0; int depth = 0; int context_len = 0; char stack[MAX_CONTEXT_STACK][MAX_KEYWORD_LENGTH]; char *utf8 = td->utf8; char *cur = utf8; char *end = cur + cur_pos; char *help_ptr = NULL; char *help_end_ptr = NULL; const char *quot = QUOT_UTF8; Eina_Bool inside_quot = EINA_FALSE; Eina_Bool find_flag = EINA_FALSE; Eina_Bool dot_lex = EINA_FALSE; //In case of sub items, it won't contain "collections". //We added it arbitrary. if (ecore_thread_check(thread)) return; if (!edit_is_main_file(td->ed)) { strncpy(stack[depth], "collections", MAX_KEYWORD_LENGTH); depth++; } while (cur && cur <= end) { if (ecore_thread_check(thread)) break; //Check inside quote if ((cur != end) && (!strncmp(cur, quot, quot_len))) { /* TODO: add exception for case '\"' */ inside_quot = !inside_quot; } if (inside_quot) { cur++; continue; } //Check inside comment if (*cur == '/') { if (cur[1] == '/') { cur = strchr(cur, '\n'); continue; } else if (cur[1] == '*') { cur = strstr(cur, "*/"); continue; } } //Case 1. Find a context and store it. if (*cur == '{') { //Skip non-alpha numberics. help_end_ptr = cur; while (!isalnum(*help_end_ptr)) help_end_ptr--; help_ptr = help_end_ptr; //Figure out current context keyword while ((help_ptr >= utf8) && (isalnum(*help_ptr) || (*help_ptr == '_'))) { help_ptr--; } if (help_ptr != utf8) help_ptr++; //Exceptional case for size. context_len = help_end_ptr - help_ptr + 1; if (context_len >= MAX_KEYWORD_LENGTH) { cur++; continue; } //Store this context. strncpy(stack[depth], help_ptr, context_len); if ((++depth) == MAX_CONTEXT_STACK) break; } //Case 2. Find a context and store it. if (*cur == '.') { Eina_Bool alpha_present = EINA_FALSE; help_end_ptr = cur - 1; //Backtrace to the beginning of a certain keyword. for (help_ptr = help_end_ptr; help_ptr && isalnum(*help_ptr); help_ptr--) { if (isalpha(*help_ptr)) alpha_present = EINA_TRUE; } //Hanlding Exception cases. if ((!alpha_present) || (!strncmp(help_ptr, quot, quot_len))) { cur++; continue; } if (help_ptr != utf8) help_ptr++; context_len = help_end_ptr - help_ptr + 1; if (context_len >= MAX_KEYWORD_LENGTH) { cur++; continue; } //Store this context. strncpy(stack[depth], help_ptr, context_len); strncpy(stack[depth], help_ptr, context_len); if ((++depth) == MAX_CONTEXT_STACK) break; dot_lex = EINA_TRUE; } //End of a dot lex context. //Reset the previous context if its out of scope. if (dot_lex && (*cur == ';')) { dot_lex = EINA_FALSE; memset(stack[depth], 0x0, MAX_KEYWORD_LENGTH); if (depth > 0) depth--; } //End of a context. Reset the previous context if its out of scope. if (*cur == '}') { memset(stack[depth], 0x0, MAX_KEYWORD_LENGTH); if (depth > 0) depth--; } cur++; } if (inside_quot) { td->result = NULL; return; } // Find current context where a cursor is inside of among the stack. for (i = 0; i < depth; i++) { find_flag = EINA_FALSE; EINA_LIST_FOREACH(nodes, l, td->result) { for (k = 0; k < td->result->name_count; k++) { //Ok, We go through this context. //FIXME: Need to compare elaborately? if (!strncmp(stack[i], td->result->name[k], strlen(td->result->name[k]))) { nodes = td->result->nodes; find_flag = EINA_TRUE; break; } } if (find_flag) break; } if (!find_flag) { td->result = NULL; return; } } } static void context_lexem_thread_end_cb(void *data, Ecore_Thread *thread EINA_UNUSED) { ctx_lexem_td *td = (ctx_lexem_td *)data; if (!td->ad || (td->ad->cltd != td)) { free(td->utf8); free(td); return; } td->ad->lexem_ptr = td->result; if (td->list_show || (td->result && td->result->dot && td->ad->dot_candidate)) { candidate_list_show(td->ad); } td->ad->dot_candidate = EINA_FALSE; td->ad->cltd = NULL; free(td->utf8); free(td); } static void context_lexem_get(autocomp_data *ad, Evas_Object *entry, Eina_Bool list_show) { const char *text = elm_entry_entry_get(entry); if (!text) { ad->lexem_ptr = ad->lexem_root; return; } if (ad->cltd) { ecore_thread_cancel(ad->cltd->thread); ad->cltd->ad = NULL; } ad->cltd = (ctx_lexem_td *)malloc(sizeof(ctx_lexem_td)); ad->cltd->utf8 = elm_entry_markup_to_utf8(text); ad->cltd->cur_pos = elm_entry_cursor_pos_get(entry); ad->cltd->ad = ad; ad->cltd->ed = ad->ed; ad->cltd->result = NULL; ad->cltd->list_show = list_show; ad->cltd->thread = ecore_thread_run(context_lexem_thread_cb, context_lexem_thread_end_cb, context_lexem_thread_end_cb, ad->cltd); } static void context_changed(autocomp_data *ad, Evas_Object *edit) { if ((!ad->enabled) || (!ad->initialized)) return; int cursor_position = elm_entry_cursor_pos_get(edit); if (!cursor_position) { ad->lexem_ptr = ad->lexem_root; return; } context_lexem_get(ad, edit, EINA_FALSE); } static void init_thread_cancel_cb(void *data, Ecore_Thread *thread EINA_UNUSED) { autocomp_data *ad = data; ad->init_thread = NULL; if (ad->term) autocomp_term(); } static void init_thread_end_cb(void *data, Ecore_Thread *thread) { autocomp_data *ad = data; ad->initialized = EINA_TRUE; init_thread_cancel_cb(data, thread); } static void key_grab_add(Evas_Object *keygrabber, const char *key) { if (!evas_object_key_grab(keygrabber, key, 0, 0, EINA_TRUE)) EINA_LOG_ERR(_("Failed to grab key - %s"), key); } static void key_grab_del(Evas_Object *keygrabber, const char *key) { evas_object_key_ungrab(keygrabber, key, 0, 0); } static void anchor_keygrab_set(autocomp_data *ad, Eina_Bool grab) { Evas_Object *anchor = ad->anchor; if (grab) { if (ad->on_keygrab) return; key_grab_add(anchor, "BackSpace"); key_grab_add(anchor, "Return"); key_grab_add(anchor, "Tab"); key_grab_add(anchor, "Up"); key_grab_add(anchor, "Down"); ad->on_keygrab = EINA_TRUE; } else { if (!ad->on_keygrab) return; key_grab_del(anchor, "BackSpace"); key_grab_del(anchor, "Return"); key_grab_del(anchor, "Tab"); key_grab_del(anchor, "Up"); key_grab_del(anchor, "Down"); ad->on_keygrab = EINA_FALSE; } } static void entry_anchor_off(autocomp_data *ad) { if (ad->anchor_visible) { elm_object_tooltip_hide(ad->anchor); /* Reset content_cb to have guarantee the callback call. If anchor is changed faster than tooltip hide, the callback won't be called since tooltip regards the content callback is same with before. */ elm_object_tooltip_content_cb_set(ad->anchor, NULL, NULL, NULL); } anchor_keygrab_set(ad, EINA_FALSE); ad->anchor_visible = EINA_FALSE; } static void anchor_unfocused_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { autocomp_data *ad = data; if (!ad) return; entry_anchor_off(ad); } static void queue_reset(autocomp_data *ad) { if ((ad->queue_pos == 0) && (!ad->anchor_visible)) return; ad->queue_pos = 0; memset(ad->queue, 0x0, sizeof(ad->queue)); entry_anchor_off(ad); } static void push_char(autocomp_data *ad, char c) { if (ad->queue_pos == QUEUE_SIZE) { memset(ad->queue, 0x0, sizeof(ad->queue)); ad->queue_pos = 0; } ad->queue[ad->queue_pos] = c; ad->queue_pos++; } static void pop_char(autocomp_data *ad, int cnt) { if (ad->queue_pos == -1) return; int i; for (i = 0; i < cnt; i++) { ad->queue[ad->queue_pos] = 0x0; if (ad->queue_pos == 0) break; ad->queue_pos--; } } static void insert_completed_text(autocomp_data *ad) { if (!ad->lexem_ptr) return; Elm_Object_Item *it = elm_list_selected_item_get(ad->list); lexem *candidate = elm_object_item_data_get(it); char **txt = candidate->txt; Evas_Object *entry = edit_entry_get(ad->ed); int space = edit_cur_indent_depth_get(ad->ed); int cursor_pos = elm_entry_cursor_pos_get(entry); //Insert the first line. const char *first_line = eina_stringshare_printf(txt[0], elm_object_item_part_text_get(it, NULL)); elm_entry_entry_insert(entry, first_line + (ad->queue_pos)); eina_stringshare_del(first_line); //Insert last lines if (candidate->txt_count > 1) { //Alloc Empty spaces char *p = alloca(space + 1); memset(p, ' ', space); p[space] = '\0'; int i; for (i = 1; i < (candidate->txt_count - 1); i++) { elm_entry_entry_insert(entry, p); elm_entry_entry_insert(entry, txt[i]); } elm_entry_entry_insert(entry, p); elm_entry_entry_insert(entry, txt[i]); } int cursor_pos2 = elm_entry_cursor_pos_get(entry); redoundo_data *rd = edit_redoundo_get(ad->ed); redoundo_entry_region_push(rd, cursor_pos, cursor_pos2); entry_anchor_off(ad); cursor_pos2 -= (candidate->cursor_offset + (candidate->line_back * space)); elm_entry_cursor_pos_set(entry, cursor_pos2); edit_line_increase(ad->ed, (candidate->txt_count - 1)); } static void list_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { autocomp_data *ad = data; ad->list = NULL; } static int list_item_compare(const void *data1, const void *data2) { Elm_Object_Item *it1 = (Elm_Object_Item *) data1; Elm_Object_Item *it2 = (Elm_Object_Item *) data2; const char *name1 = NULL, *name2 = NULL; if (!it1) return -1; if (!it2) return 1; name1 = elm_object_item_part_text_get(it1, NULL); if (!name1) return -1; name2 = elm_object_item_part_text_get(it2, NULL); if (!name2) return 1; return strcmp(name1, name2); } static Evas_Object * entry_tooltip_content_cb(void *data, Evas_Object *obj EINA_UNUSED, Evas_Object *tt EINA_UNUSED) { autocomp_data *ad = data; Eina_Bool found = EINA_FALSE; if (!ad->lexem_ptr) return NULL; ad->list = elm_list_add(obj); elm_object_focus_allow_set(ad->list, EINA_FALSE); elm_list_mode_set(ad->list, ELM_LIST_EXPAND); //compute list size to prevent over-sizing than enventor window. Evas_Coord y, y2, h; evas_object_geometry_get(edit_obj_get(ad->ed), NULL, &y, NULL, &h); evas_object_geometry_get(ad->anchor, NULL, &y2, NULL, NULL); Elm_Tooltip_Orient tooltip_orient = elm_object_tooltip_orient_get(ad->anchor); Evas_Coord mh; if (tooltip_orient == ELM_TOOLTIP_ORIENT_BOTTOM) mh = (h - y2); else mh = (y2 - y); evas_object_size_hint_max_set(ad->list, 999999, mh); //add keywords Eina_List *l; lexem *lexem_data; int k = 0; EINA_LIST_FOREACH(ad->lexem_ptr->nodes, l, lexem_data) { for (k = 0; k < lexem_data->name_count; k++) if (!strncmp(lexem_data->name[k], ad->queue, ad->queue_pos)) { elm_list_item_sorted_insert(ad->list, lexem_data->name[k], NULL, NULL, NULL, lexem_data, list_item_compare); found = EINA_TRUE; } } //select first item in default. Elm_Object_Item *it = elm_list_first_item_get(ad->list); if (it) elm_list_item_selected_set(it, EINA_TRUE); evas_object_event_callback_add(ad->list, EVAS_CALLBACK_DEL, list_del_cb, ad); if (!found) { entry_anchor_off(ad); return NULL; } elm_list_go(ad->list); evas_object_show(ad->list); return ad->list; } static void anchor_list_update(autocomp_data *ad) { lexem *data; Eina_List *l; Elm_Object_Item *it; Eina_Bool found = EINA_FALSE; elm_list_clear(ad->list); //Append new candidates int k = 0; EINA_LIST_FOREACH(ad->lexem_ptr->nodes, l, data) { for (k = 0; k < data->name_count; k++) if (!strncmp(data->name[k], ad->queue, ad->queue_pos)) { elm_list_item_sorted_insert(ad->list, data->name[k], NULL, NULL, NULL, data, list_item_compare); found = EINA_TRUE; } } if (!found) { entry_anchor_off(ad); return; } it = elm_list_first_item_get(ad->list); elm_list_item_selected_set(it, EINA_TRUE); elm_list_go(ad->list); } static void candidate_list_show(autocomp_data *ad) { if (!ad->lexem_ptr) { entry_anchor_off(ad); return; } Evas_Object *entry = edit_entry_get(ad->ed); //Update anchor position Evas_Coord x, y, h; Evas_Coord cx, cy, cw, ch; evas_object_geometry_get(entry, &x, &y, NULL, NULL); elm_entry_cursor_geometry_get(entry, &cx, &cy, &cw, &ch); evas_object_move(ad->anchor, cx + x, cy + y); evas_object_resize(ad->anchor, cw, ch); //Show the tooltip if (!ad->anchor_visible) { //Decide the Tooltip direction Elm_Tooltip_Orient tooltip_orient = ELM_TOOLTIP_ORIENT_BOTTOM; evas_object_geometry_get(edit_obj_get(ad->ed), NULL, NULL, NULL, &h); if ((cy + y) > (h / 2)) tooltip_orient = ELM_TOOLTIP_ORIENT_TOP; //Tooltip set elm_object_tooltip_content_cb_set(ad->anchor, entry_tooltip_content_cb, ad, NULL); elm_object_tooltip_orient_set(ad->anchor, tooltip_orient); elm_object_tooltip_show(ad->anchor); anchor_keygrab_set(ad, EINA_TRUE); ad->anchor_visible = EINA_TRUE; } //Already tooltip is visible, just update the list item else anchor_list_update(ad); } static void entry_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info) { autocomp_data *ad = data; if ((!ad) || (!ad->enabled)) return; Elm_Entry_Change_Info *info = event_info; if (info->insert) { if ((strlen(info->change.insert.content) > 1) || (info->change.insert.content[0] == ' ') || (info->change.insert.content[0] == '.')) { if (info->change.insert.content[0] == '.' && ad->queue_pos > 2) { ad->dot_candidate = EINA_TRUE; context_lexem_get(ad, obj, EINA_FALSE); } queue_reset(ad); } else { push_char(ad, info->change.insert.content[0]); candidate_list_show(ad); } } else { if (info->change.del.content[0] != ' ') { entry_anchor_off(ad); /* FIXME: abs() shouldn't be used here (size_t is unsigned) */ int cnt = abs(info->change.del.end - info->change.del.start); pop_char(ad, cnt); } } } static void entry_cursor_changed_manual_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { autocomp_data *ad = data; if (!ad) return; if (ad->anchor_visible) entry_anchor_off(ad); } static void entry_cursor_changed_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) { autocomp_data *ad = data; if (!ad) return; //Update anchor position Evas_Coord x, y, cx, cy, cw, ch; evas_object_geometry_get(obj, &x, &y, NULL, NULL); elm_entry_cursor_geometry_get(obj, &cx, &cy, &cw, &ch); evas_object_move(ad->anchor, cx + x, cy + y); evas_object_resize(ad->anchor, cw, ch); context_changed(ad, edit_entry_get(ad->ed)); } static void entry_press_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { autocomp_data *ad = data; if (!ad) return; if (ad->anchor_visible) entry_anchor_off(ad); } static void entry_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { autocomp_data *ad = data; if (!ad) return; if (ad->anchor_visible) entry_anchor_off(ad); } static void event_rect_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { autocomp_data *ad = data; if (!ad) return; if (ad->anchor_visible) entry_anchor_off(ad); } static void list_item_move(autocomp_data *ad, Eina_Bool up) { Evas_Object *entry = edit_entry_get(ad->ed); evas_object_smart_callback_del(entry, "unfocused", anchor_unfocused_cb); Elm_Object_Item *it = elm_list_selected_item_get(ad->list); if (up) it = elm_list_item_prev(it); else it = elm_list_item_next(it); if (it) { elm_list_item_selected_set(it, EINA_TRUE); elm_list_item_bring_in(it); } evas_object_smart_callback_add(entry, "unfocused", anchor_unfocused_cb, ad); } static void anchor_key_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { autocomp_data *ad = data; if (!ad->anchor_visible) return; Evas_Event_Key_Down *ev = event_info; //Cancel the auto complete. if (!strcmp(ev->key, "BackSpace")) { entry_anchor_off(ad); ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; return; } if (!strcmp(ev->key, "Return") || !strcmp(ev->key, "Tab")) { insert_completed_text(ad); queue_reset(ad); edit_syntax_color_partial_apply(ad->ed, -1); ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; return; } if (!strcmp(ev->key, "Up")) { list_item_move(ad, EINA_TRUE); ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; return; } if (!strcmp(ev->key, "Down")) { list_item_move(ad, EINA_FALSE); ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; return; } } /*****************************************************************************/ /* Externally accessible calls */ /*****************************************************************************/ void autocomp_list_show(void) { Evas_Object *entry; autocomp_data *ad = g_ad; if (!g_ad || !g_ad->enabled) return; entry = edit_entry_get(ad->ed); context_lexem_get(ad, entry, EINA_TRUE); } void autocomp_reset(void) { autocomp_data *ad = g_ad; if (!ad) return; queue_reset(ad); } void autocomp_target_set(edit_data *ed) { autocomp_data *ad = g_ad; if (!ad) return; if (ad->cltd) { ecore_thread_cancel(ad->cltd->thread); ad->cltd->ad = NULL; ad->cltd = NULL; } Evas_Object *entry; queue_reset(ad); //Unset previous target if (ad->ed) { entry = edit_entry_get(ad->ed); evas_object_smart_callback_del(entry, "changed,user", entry_changed_cb); evas_object_smart_callback_del(entry, "cursor,changed", entry_cursor_changed_cb); evas_object_smart_callback_del(entry, "cursor,changed,manual", entry_cursor_changed_manual_cb); evas_object_smart_callback_del(entry, "unfocused", anchor_unfocused_cb); evas_object_smart_callback_del(entry, "press", entry_press_cb); evas_object_event_callback_del(entry, EVAS_CALLBACK_MOVE, entry_move_cb); evas_object_del(ad->anchor); ad->anchor = NULL; ad->ed = NULL; } if (!ed) return; entry = edit_entry_get(ed); evas_object_smart_callback_add(entry, "changed,user", entry_changed_cb, ad); evas_object_smart_callback_add(entry, "cursor,changed,manual", entry_cursor_changed_manual_cb, ad); evas_object_smart_callback_add(entry, "cursor,changed", entry_cursor_changed_cb, ad); evas_object_smart_callback_add(entry, "unfocused", anchor_unfocused_cb, ad); evas_object_smart_callback_add(entry, "press", entry_press_cb, ad); evas_object_event_callback_add(entry, EVAS_CALLBACK_MOVE, entry_move_cb, ad); ad->anchor = elm_button_add(edit_obj_get(ed)); evas_object_event_callback_add(ad->anchor, EVAS_CALLBACK_KEY_DOWN, anchor_key_down_cb, ad); //event_rect catches mouse down event, which makes anchor off. if (!ad->event_rect) { Evas_Object *win = elm_object_top_widget_get(edit_obj_get(ed)); Evas *e = evas_object_evas_get(win); Evas_Object *rect = evas_object_rectangle_add(e); evas_object_repeat_events_set(rect, EINA_TRUE); evas_object_color_set(rect, 0, 0, 0, 0); evas_object_event_callback_add(rect, EVAS_CALLBACK_MOUSE_DOWN, event_rect_mouse_down_cb, ad); evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(win, rect); evas_object_show(rect); ad->event_rect = rect; } ad->ed = ed; } Eina_Bool autocomp_event_dispatch(const char *key) { autocomp_data *ad = g_ad; if (!ad) return EINA_FALSE; if (ad->anchor_visible) return EINA_FALSE; //Reset queue. if (!strcmp(key, "Up") || !strcmp(key, "Down") || !strcmp(key, "Left") || !strcmp(key, "Right")) queue_reset(ad); return EINA_FALSE; } void autocomp_init(void) { autocomp_data *ad = calloc(1, sizeof(autocomp_data)); if (!ad) { EINA_LOG_ERR("Failed to allocate Memory!"); return; } ad->init_thread = ecore_thread_run(init_thread_cb, init_thread_end_cb, init_thread_cancel_cb, ad); ad->queue_pos = 0; g_ad = ad; } void autocomp_term(void) { autocomp_data *ad = g_ad; autocomp_target_set(NULL); if (ad->init_thread) { ecore_thread_cancel(ad->init_thread); ad->term = EINA_TRUE; return; } if (ad->cltd) { ecore_thread_cancel(ad->cltd->thread); ad->cltd->ad = NULL; } evas_object_del(ad->event_rect); evas_object_del(ad->anchor); lexem_tree_free(ad->lexem_root); eet_data_descriptor_free(lex_desc); eet_close(ad->source_file); free(ad); g_ad = NULL; } void autocomp_enabled_set(Eina_Bool enabled) { autocomp_data *ad = g_ad; enabled = !!enabled; ad->enabled = enabled; } Eina_Bool autocomp_enabled_get(void) { autocomp_data *ad = g_ad; return ad->enabled; } const char ** autocomp_current_context_get(int *name_count) { autocomp_data *ad = g_ad; if (!ad->lexem_ptr || !ad->lexem_ptr->name) return NULL; *name_count = ad->lexem_ptr->name_count; return (const char **)ad->lexem_ptr->name; }