enventor/src/lib/auto_comp.c

1043 lines
27 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Enventor.h>
#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);
if (entry)
{
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;
}