jaehyun pushed a commit to branch master. http://git.enlightenment.org/tools/enventor.git/commit/?id=710621cc9b26c3c6242dab840900b155dfebdbf3
commit 710621cc9b26c3c6242dab840900b155dfebdbf3 Author: Jaehyun Cho <[email protected]> Date: Tue Jun 28 16:43:47 2016 +0900 Support keyword reference function. Display keyword reference if F5 is pressed while cursor is on the keyword. --- README | 3 +- configure.ac | 1 + data/Makefile.am | 2 +- data/help/SHORTCUT | 3 +- data/reference/Makefile.am | 5 + data/reference/reference.src | 35 ++ data/themes/default/layout_common.edc | 33 ++ src/bin/main.c | 9 +- src/lib/Makefile.am | 3 +- src/lib/enventor_object.eo | 2 + src/lib/enventor_private.h | 6 + src/lib/enventor_smart.c | 8 + src/lib/reference.c | 740 ++++++++++++++++++++++++++++++++++ 13 files changed, 845 insertions(+), 5 deletions(-) diff --git a/README b/README index 22a8103..75096e1 100644 --- a/README +++ b/README @@ -34,7 +34,8 @@ F1 - About F2 - New F3 - Save F4 - Load -F5 - Toggle Line Number +F5 - Keyword Reference +F6 - Toggle Line Number F7 - Toggle Tools F8 - Toggle Status F9 - Toggle File Browser diff --git a/configure.ac b/configure.ac index 7041bab..a1955bb 100644 --- a/configure.ac +++ b/configure.ac @@ -130,6 +130,7 @@ data/color/Makefile data/autocomp/Makefile data/sounds/Makefile data/help/Makefile +data/reference/Makefile pc/enventor.pc ]) diff --git a/data/Makefile.am b/data/Makefile.am index 8a1d4f4..af8d524 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,3 +1,3 @@ MAINTAINERCLEANFILES = Makefile.in -SUBDIRS = images themes templates desktop icon color sounds help autocomp +SUBDIRS = images themes templates desktop icon color sounds help autocomp reference diff --git a/data/help/SHORTCUT b/data/help/SHORTCUT index 1ddd997..39f153b 100644 --- a/data/help/SHORTCUT +++ b/data/help/SHORTCUT @@ -4,7 +4,8 @@ F2 - New</br> F3 - Save</br> F4 - Load</br> - F5 - Toggle Line Number</br> + F5 - Keyword Reference</br> + F6 - Toggle Line Number</br> F7 - Toggle Tools</br> F8 - Toggle Status</br> F9 - Toggle File Browser</br> diff --git a/data/reference/Makefile.am b/data/reference/Makefile.am new file mode 100644 index 0000000..4613fda --- /dev/null +++ b/data/reference/Makefile.am @@ -0,0 +1,5 @@ +MAINTAINERCLEANFILES = Makefile.in +filesdir = $(datadir)/$(PACKAGE)/reference +files_DATA = reference.src + +EXTRA_DIST = $(files_DATA) diff --git a/data/reference/reference.src b/data/reference/reference.src new file mode 100644 index 0000000..c4d806d --- /dev/null +++ b/data/reference/reference.src @@ -0,0 +1,35 @@ +collections { "<hilight>collections</hilight> represents a theme.</br>A <hilight>collections</hilight> block contains a list of group that composes a theme."; + group { "<hilight>group</hilight> represents a widget style.</br>A <hilight>group</hilight> block contains parts and programs that compose a widget style."; + parts { "<hilight>parts</hilight> represents visual elements.</br>A <hilight>parts</hilight> block contains a list of part that describes a basic design element."; + part { "<hilight>part</hilight> represents a basic design element such as an image, rectangle or text.</br>A <hilight>part</hilight> block contains a list of description that defines a state of the element."; + desc { "<hilight>desc</hilight> defines a state of a part.</br>A <hilight>desc</hilight> block contains properties of a part such as a size, position, color, image and text."; + } + } + image { "<hilight>image</hilight> represents an image part. <hilight>image</hilight> is used to display an image file.</br>A <hilight>image</hilight> block contains a list of description that defines a state of the image part."; + desc { "<hilight>desc</hilight> defines a state of a part.</br>A <hilight>desc</hilight> block inside of an image block contains properties of the image part such as an image resource file, size, position and color."; + } + } + rect { "<hilight>rect</hilight> represents a rectangle part. <hilight>rect</hilight> is used to draw a rectangle.</br>A <hilight>rect</hilight> block contains a list of description that defines a state of the rectangle part."; + desc { "<hilight>desc</hilight> defines a state of a part.</br>A <hilight>desc</hilight> block inside of a rect block contains properties of the rect part such as a size, position and color."; + } + } + text { "<hilight>text</hilight> represents a text part. <hilight>text</hilight> is used to write a simple string with single line in this part.</br>A <hilight>text</hilight> block contains a list of description that defines a state of the text part."; + desc { "<hilight>desc</hilight> defines a state of a part.</br>A <hilight>desc</hilight> block inside of a text block contains properties of the text part such as a text font, text size, text position and text color."; + } + } + textblock { "<hilight>textblock</hilight> represents a textblock part. <hilight>textblock</hilight> is used to write complex strings and mark-up elements with multiple lines in this part.</br>A <hilight>textblock</hilight> block contains a list of description that defines a state of the textblock part."; + desc { "<hilight>desc</hilight> defines a state of a part.</br>A <hilight>desc</hilight> block inside of a textblock block contains properties of the textblock part such as a text style, text font, text size, text position and text color."; + } + } + swallow { "<hilight>swallow</hilight> represents a swallow part. <hilight>swallow</hilight> is used to set a widget into this part.</br>A <hilight>swallow</hilight> block contains a list of description that defines a state of the swallow part."; + desc { "<hilight>desc</hilight> defines a state of a part.</br>A <hilight>desc</hilight> block inside of a swallow block contains properties of the swallow part such as size and position."; + } + } + } + programs { "<hilight>programs</hilight> defines how your interface reacts to events.</br>A <hilight>programs</hilight> block contains a list of program that reacts to events."; + program { "<hilight>program</hilight> defines how your interface reacts to events. <hilight>program</hilight> can change the state of parts and trigger/listen events.</br>A <hilight>program</hilight> block contains signal, source, action and target.</br>signal is an event that the program is waiting for.</br>source is a part that catches the signal. (e.g. mouse clicking area)</br>action defines what is going to do when the signal is caught. (e.g. changing the state of a part)</b [...] + } + } + } + } +} diff --git a/data/themes/default/layout_common.edc b/data/themes/default/layout_common.edc index bc1e5ba..a78263d 100644 --- a/data/themes/default/layout_common.edc +++ b/data/themes/default/layout_common.edc @@ -3371,3 +3371,36 @@ group { "edit_layout"; } } } + +#define REFERENCE_LAYOUT_WIDTH 300 +#define REFERENCE_LAYOUT_HEIGHT 100 +group { "reference_layout"; + data.item: "width" REFERENCE_LAYOUT_WIDTH; + data.item: "height" REFERENCE_LAYOUT_HEIGHT; + parts { + spacer { "base"; + scale: 1; + desc { "default"; + } + } + rect { "bg"; + scale: 1; + desc { "default"; + min: REFERENCE_LAYOUT_WIDTH REFERENCE_LAYOUT_HEIGHT; + max: REFERENCE_LAYOUT_WIDTH REFERENCE_LAYOUT_HEIGHT; + fixed: 1 1; + align: 0.0 0.0; + rel1.to: "base"; + rel2.to: "base"; + color: 128 128 128 255; + } + } + swallow { "elm.swallow.content"; + scale: 1; + desc { "default"; + rel1.to: "bg"; + rel2.to: "bg"; + } + } + } +} diff --git a/src/bin/main.c b/src/bin/main.c index 093f381..880d207 100644 --- a/src/bin/main.c +++ b/src/bin/main.c @@ -773,10 +773,17 @@ keygrabber_key_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, menu_edc_load(); return; } - //Line Number + //Keyword Reference if (!strcmp(ev->key, "F5")) { enventor_object_ctxpopup_dismiss(base_enventor_get()); + enventor_object_keyword_reference_show(base_enventor_get()); + return; + } + //Line Number + if (!strcmp(ev->key, "F6")) + { + enventor_object_ctxpopup_dismiss(base_enventor_get()); tools_lines_update(EINA_TRUE); return; } diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ace4c78..4206f10 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -45,7 +45,8 @@ libenventor_la_SOURCES = \ edj_viewer.c \ dummy_obj.c \ wireframes_obj.c \ - util.c + util.c \ + reference.c libenventor_la_CFLAGS = @ENVENTOR_CFLAGS@ libenventor_la_LIBADD = @ENVENTOR_LIBS@ diff --git a/src/lib/enventor_object.eo b/src/lib/enventor_object.eo index 80c28b5..df4a46b 100644 --- a/src/lib/enventor_object.eo +++ b/src/lib/enventor_object.eo @@ -204,6 +204,8 @@ class Enventor.Object (Elm.Widget, Efl.File) { } auto_complete_list_show { } + keyword_reference_show { + } } implements { class.constructor; diff --git a/src/lib/enventor_private.h b/src/lib/enventor_private.h index 3c3c5fc..18e7e7d 100644 --- a/src/lib/enventor_private.h +++ b/src/lib/enventor_private.h @@ -289,4 +289,10 @@ void edit_focus_set(edit_data *ed, Eina_Bool focus); /* util */ void mem_fail_msg(void); + +/* reference */ +void ref_init(void); +void ref_term(void); +void ref_show(edit_data *ed); + #endif diff --git a/src/lib/enventor_smart.c b/src/lib/enventor_smart.c index eb061fc..ab7449b 100644 --- a/src/lib/enventor_smart.c +++ b/src/lib/enventor_smart.c @@ -249,6 +249,7 @@ _enventor_object_efl_canvas_group_group_add(Eo *obj, Enventor_Object_Data *pd) build_init(); autocomp_init(); + ref_init(); edj_mgr_init(obj); build_err_noti_cb_set(build_err_noti_cb, pd); @@ -277,6 +278,7 @@ _enventor_object_efl_canvas_group_group_del(Eo *obj EINA_UNUSED, Enventor_Object eina_stringshare_del(pd->font_style); eina_stringshare_del(pd->group_name); autocomp_term(); + ref_term(); ecore_event_handler_del(pd->key_down_handler); ecore_event_handler_del(pd->key_up_handler); edj_mgr_term(); @@ -774,6 +776,12 @@ _enventor_object_programs_stop(Eo *obj EINA_UNUSED, view_programs_stop(VIEW_DATA); } +EOLIAN static void +_enventor_object_keyword_reference_show(Eo *obj EINA_UNUSED, + Enventor_Object_Data *pd) +{ + ref_show(pd->main_it->ed); +} /*****************************************************************************/ /* Externally accessible calls */ diff --git a/src/lib/reference.c b/src/lib/reference.c new file mode 100644 index 0000000..b57a763 --- /dev/null +++ b/src/lib/reference.c @@ -0,0 +1,740 @@ +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include <Enventor.h> +#include "enventor_private.h" + +/* This is a node for a list structure. + Each node has its parent name list. */ +typedef struct keyword_s +{ + char *name; + char *desc; + Eina_List *parent_name_list; //Parent keyword name list +} keyword_data; + +typedef struct ref_s +{ + Eina_File *source_file; + + Eina_List *keyword_list; //keyword_data list + + char *keyword_name; + char *keyword_desc; + + edit_data *ed; + Evas_Object *event_rect; + Evas_Object *layout; +} ref_data; + +static ref_data *g_md = NULL; + +/*****************************************************************************/ +/* Internal method implementation */ +/*****************************************************************************/ + +static void keyword_data_free(keyword_data **keyword); +static void keyword_list_free(Eina_List **keyword_list); +static void ref_event_rect_delete(void); +static void ref_layout_delete(void); + +static void +keyword_data_free(keyword_data **keyword) +{ + if (!(*keyword)) return; + + if ((*keyword)->name) + free((*keyword)->name); + if ((*keyword)->desc) + free((*keyword)->desc); + + char *parent_name; + EINA_LIST_FREE((*keyword)->parent_name_list, parent_name) + { + free(parent_name); + } +} + +static void +keyword_list_free(Eina_List **keyword_list) +{ + if (!(*keyword_list)) return; + + keyword_data *keyword; + EINA_LIST_FREE(*keyword_list, keyword) + { + keyword_data_free(&keyword); + } +} + +static char * +cursor_keyword_name_find(Evas_Object *entry) +{ + Evas_Object *tb = NULL; + Evas_Textblock_Cursor *cur_orig = NULL; + Evas_Textblock_Cursor *cur_begin = NULL; + Evas_Textblock_Cursor *cur_end = NULL; + int cur_orig_pos = 0; + int cur_begin_pos = 0; + char *cur_orig_ptr = NULL; + char *cur_begin_ptr = NULL; + char *cur_end_ptr = NULL; + char *cur_text = NULL; + char *keyword_name = NULL; + + tb = elm_entry_textblock_get(entry); + cur_orig = evas_object_textblock_cursor_get(tb); + if (!cur_orig) return NULL; + + //Show keyword reference only if cursor is located in a correct keyword. + cur_text = evas_textblock_cursor_content_get(cur_orig); + if (!cur_text || (!isalnum(*cur_text) && (*cur_text != '_'))) goto end; + free(cur_text); + cur_text = NULL; + cur_orig_pos = evas_textblock_cursor_pos_get(cur_orig); + + //Find beginning cursor position of the keyword. + cur_begin = evas_object_textblock_cursor_new(tb); + if (!cur_begin) goto end; + + evas_textblock_cursor_pos_set(cur_begin, cur_orig_pos); + if (!evas_textblock_cursor_word_start(cur_begin)) goto end; + + //Find ending cursor position of the keyword. + cur_end = evas_object_textblock_cursor_new(tb); + if (!cur_end) goto end; + + evas_textblock_cursor_pos_set(cur_end, cur_orig_pos); + if (!evas_textblock_cursor_word_end(cur_end)) goto end; + + /* Move ending cursor by one character because cursor_range_text_get does + not include character of ending cursor. */ + evas_textblock_cursor_char_next(cur_end); + cur_text = evas_textblock_cursor_range_text_get(cur_begin, cur_end, + EVAS_TEXTBLOCK_TEXT_PLAIN); + if (!cur_text) goto end; + + //Need to check if cursor text contains special character such as '.'. + cur_begin_pos = evas_textblock_cursor_pos_get(cur_begin); + cur_orig_ptr = cur_text + (cur_orig_pos - cur_begin_pos); + + //Find valid keyword beginning position from cursor text. + char *cur_ptr = cur_orig_ptr; + while (cur_ptr >= cur_text) + { + if (isalnum(*cur_ptr) || (*cur_ptr == '_')) + cur_begin_ptr = cur_ptr; + else + break; + + cur_ptr--; + } + + //Find valid keyword ending position from cursor text. + cur_ptr = cur_orig_ptr; + char *cur_text_end = cur_text + strlen(cur_text) - 1; + while (cur_ptr <= cur_text_end) + { + if (isalnum(*cur_ptr) || (*cur_ptr == '_')) + cur_end_ptr = cur_ptr; + else + break; + + cur_ptr++; + } + + keyword_name = strndup(cur_begin_ptr, (cur_end_ptr - cur_begin_ptr + 1)); + +end: + if (cur_begin) evas_textblock_cursor_free(cur_begin); + if (cur_end) evas_textblock_cursor_free(cur_end); + if (cur_text) free(cur_text); + + return keyword_name; +} + +/* Check two string lists have same strings. + Return EINA_TRUE if lists are same. + Return EINA_FALSE if lists are not same. */ +static Eina_Bool +str_list_same_check(Eina_List *str_list1, Eina_List *str_list2) +{ + if (!str_list1 && !str_list2) + return EINA_TRUE; + + if ((!str_list1 && str_list2) || (str_list1 && !str_list2)) + return EINA_FALSE; + + if (eina_list_count(str_list1) != eina_list_count(str_list2)) + return EINA_FALSE; + + while (str_list1 && str_list2) + { + char *str1 = eina_list_data_get(str_list1); + char *str2 = eina_list_data_get(str_list2); + + if (!str1 || !str2) return EINA_FALSE; + if (strcmp(str1, str2)) return EINA_FALSE; + + str_list1 = eina_list_next(str_list1); + str_list2 = eina_list_next(str_list2); + } + + return EINA_TRUE; +} + +static Eina_List * +keyword_parent_name_list_find(const char *text, const char *keyword_name) +{ + Eina_List *parent_name_list = NULL; + + if (!text) return NULL; + if (!keyword_name) return NULL; + + //Check from the end of the text. + char *ptr = (char *)(text + ((strlen(text) - 1) * sizeof(char))); + int height = 0; + int next_height = height + 1; + + const char *parent_begin = NULL; + const char *parent_end = NULL; + + while (text <= ptr) + { + if (*ptr == '{') + { + height++; + if (height == next_height) + { + ptr--; + if (text > ptr) break; + + parent_begin = NULL; + parent_end = NULL; + while (text <= ptr) + { + if (isspace(*ptr)) + { + if (parent_end) + { + parent_begin = ptr + 1; + break; + } + } + else + { + if (!parent_end) + parent_end = ptr; + } + ptr--; + } + if (!parent_end) + continue; + + if (parent_end && !parent_begin) + parent_begin = text; + + char *parent_name = strndup(parent_begin, + (parent_end - parent_begin + 1)); + parent_name_list = eina_list_append(parent_name_list, + parent_name); + next_height++; + } + } + else if (*ptr == '}') + { + height--; + } + ptr--; + } + + return parent_name_list; +} + +static keyword_data * +cursor_keyword_data_find(Evas_Object *entry, Eina_List *keyword_list, + const char *keyword_name) +{ + keyword_data *found_keyword = NULL; + + if (!entry) return NULL; + if (!keyword_list) return NULL; + if (!keyword_name) return NULL; + + //Get text before cursor position. + Evas_Object *tb = elm_entry_textblock_get(entry); + Evas_Object *cur_begin = evas_object_textblock_cursor_new(tb); + Evas_Object *cur_end = evas_object_textblock_cursor_get(tb); + char *utf8_text = NULL; + Eina_List *parent_name_list = NULL; + + /* FIXME: Getting text from range with EVAS_TEXTBLOCK_TEXT_PLAIN generates + garbage characters. + So please use EVAS_TEXTBLOCK_TEXT_PLAIN after the bug is fixed. */ + char *markup_text = + evas_textblock_cursor_range_text_get(cur_begin, cur_end, + EVAS_TEXTBLOCK_TEXT_MARKUP); + if (!markup_text) goto end; + + utf8_text = evas_textblock_text_markup_to_utf8(NULL, markup_text); + free(markup_text); + markup_text = NULL; + + //Find parent name list of the selected keyword in text. + parent_name_list = keyword_parent_name_list_find((const char *)utf8_text, + keyword_name); + Eina_List *l; + keyword_data *keyword; + EINA_LIST_FOREACH(keyword_list, l, keyword) + { + //Find a keyword which has the same name and the same parent names. + if (!strcmp(keyword->name, keyword_name) && + str_list_same_check(keyword->parent_name_list, parent_name_list)) + { + found_keyword = keyword; + break; + } + } + +end: + if (cur_begin) evas_textblock_cursor_free(cur_begin); + if (utf8_text) free(utf8_text); + if (parent_name_list) eina_list_free(parent_name_list); + + return found_keyword; +} + +static void +keyword_list_load(Eina_List **keyword_list, Eina_List **parent_name_list, + char **ptr) +{ + if (!keyword_list) return; + if (!parent_name_list) return; + if (!*ptr) return; + + int len = strlen(*ptr); + const char *text_end = *ptr + len; + + while (*ptr < text_end) + { + keyword_data *keyword = NULL; + char *keyword_name = NULL; + char *keyword_desc = NULL; + char *block_begin = NULL; + char *block_end = NULL; + char *parent_name = NULL; + + //Find beginning position of keyword block to check pointer. + block_begin = strstr(*ptr, "{"); + if (!block_begin) break; + + //Find ending position of keyword block to check pointer. + block_end = strstr(*ptr, "}"); + if (!block_end) break; + + if (block_begin > block_end) break; + + //Find keyword name. + char *keyword_name_begin = NULL; + char *keyword_name_end = NULL; + + for ( ; *ptr < block_begin; (*ptr)++) + { + if (!isspace(**ptr)) + { + if (!keyword_name_begin) + keyword_name_begin = *ptr; + else + keyword_name_end = *ptr; + } + } + if (!keyword_name_begin || !keyword_name_end) break; + + keyword_name = strndup(keyword_name_begin, + (keyword_name_end - keyword_name_begin + 1)); + if (!keyword_name) break; + (*ptr)++; //Move pointer position after "{". + + //Find keyword description. + char *keyword_desc_begin = NULL; + char *keyword_desc_end = NULL; + + keyword_desc_begin = strstr(*ptr, "\""); + if (!keyword_desc_begin) + { + free(keyword_name); + break; + } + keyword_desc_begin++; + + keyword_desc_end = strstr(keyword_desc_begin, "\";"); + if (!keyword_desc_end) + { + free(keyword_name); + break; + } + keyword_desc_end--; + + keyword_desc = + strndup((const char *)keyword_desc_begin, + (keyword_desc_end - keyword_desc_begin + 1)); + + if (!keyword_desc) + { + free(keyword_name); + break; + } + *ptr = keyword_desc_end + 3; //Move pointer position after "\";". + + //Set parent name list. + parent_name = keyword_name; + *parent_name_list = eina_list_append(*parent_name_list, + parent_name); + + //Set child keyword list recursively. + keyword_list_load(keyword_list, parent_name_list, ptr); + + //Find ending position of keyword block to update pointer. + block_end = strstr(*ptr, "}"); + if (!block_end) + { + free(keyword_name); + free(keyword_desc); + break; + } + *ptr = block_end + 1; //Move pointer position after "}". + + //Remove current keyword name from parent name list. + *parent_name_list = eina_list_remove(*parent_name_list, parent_name); + + //Create a new keyword data and Append it to keyword list. + keyword = calloc(1, sizeof(keyword_data)); + keyword->name = keyword_name; + keyword->desc = keyword_desc; + if (*parent_name_list) + { + Eina_List *new_parent_name_list = NULL; + Eina_List *l = NULL; + EINA_LIST_REVERSE_FOREACH(*parent_name_list, l, parent_name) + { + new_parent_name_list = eina_list_append(new_parent_name_list, + strdup(parent_name)); + } + keyword->parent_name_list = new_parent_name_list; + } + *keyword_list = eina_list_append(*keyword_list, keyword); + } +} + +static void +ref_load(ref_data *md) +{ + if (!md) return; + + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "%s/reference/reference.src", + eina_prefix_data_get(PREFIX)); + + //Open source file. + md->source_file = eina_file_open((const char *)buf, EINA_FALSE); + if (!md->source_file) return; + + //Load text from source file. + char *text = (char *)eina_file_map_all(md->source_file, EINA_FILE_POPULATE); + if (!text) goto end; + + if (md->keyword_list) + { + keyword_list_free(&(md->keyword_list)); + md->keyword_list = NULL; + } + + //Load keyword data list from text. + Eina_List *keyword_list = NULL; + Eina_List *parent_name_list = NULL; + char *ptr = text; + keyword_list_load(&keyword_list, &parent_name_list, &ptr); + md->keyword_list = keyword_list; + +end: + if (text) eina_file_map_free(md->source_file, text); + + eina_file_close(md->source_file); + md->source_file = NULL; +} + +static void +event_rect_mouse_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +entry_unfocused_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +entry_move_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +entry_changed_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +entry_changed_user_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +entry_cursor_changed_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +entry_cursor_changed_manual_cb(void *data EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ref_event_rect_delete(); + ref_layout_delete(); +} + +static void +ref_event_rect_delete(void) +{ + ref_data *md = g_md; + if (!md) return; + + if (!md->event_rect) return; + evas_object_del(md->event_rect); + md->event_rect = NULL; +} + +static void +ref_layout_delete(void) +{ + ref_data *md = g_md; + if (!md) return; + + if (!md->layout) return; + evas_object_del(md->layout); + md->layout = NULL; + + if (!md->ed) return; + //Delete entry callbacks to delete reference layout. + Evas_Object *edit_entry = edit_entry_get(md->ed); + evas_object_event_callback_del(edit_entry, EVAS_CALLBACK_MOVE, + entry_move_cb); + evas_object_smart_callback_del(edit_entry, "unfocused", entry_unfocused_cb); + evas_object_smart_callback_del(edit_entry, "changed", entry_changed_cb); + evas_object_smart_callback_del(edit_entry, "changed,user", + entry_changed_user_cb); + evas_object_smart_callback_del(edit_entry, "cursor,changed", + entry_cursor_changed_cb); + evas_object_smart_callback_del(edit_entry, "cursor,changed,manual", + entry_cursor_changed_manual_cb); +} + +static Evas_Object * +ref_event_rect_create(Evas_Object *edit_obj) +{ + if (!edit_obj) return NULL; + + //event_rect catches mouse down event, which delete reference layout. + Evas_Object *win = elm_object_top_widget_get(edit_obj); + 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, NULL); + evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, + EVAS_HINT_EXPAND); + elm_win_resize_object_add(win, rect); + evas_object_show(rect); + + return rect; +} + +static Evas_Object * +ref_layout_create(Evas_Object *edit_entry, const char *desc) +{ + if (!edit_entry) return NULL; + if (!desc) return NULL; + + Evas_Object *layout = elm_layout_add(edit_entry); + if (!layout) + { + EINA_LOG_ERR("Failed to allocate Memory!"); + return NULL; + } + elm_layout_file_set(layout, EDJE_PATH, "reference_layout"); + + //Do not apply edit entry's font scale to reference layout. + elm_object_scale_set(layout, 1.0); + + Evas_Object *entry = elm_entry_add(layout); + elm_object_style_set(entry, "enventor"); + elm_entry_editable_set(entry, EINA_FALSE); + elm_entry_scrollable_set(entry, EINA_TRUE); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_entry_entry_set(entry, desc); + + elm_object_part_content_set(layout, "elm.swallow.content", entry); + evas_object_show(layout); + + //Add entry callbacks to delete reference layout. + evas_object_event_callback_add(edit_entry, EVAS_CALLBACK_MOVE, entry_move_cb, + NULL); + evas_object_smart_callback_add(edit_entry, "unfocused", entry_unfocused_cb, + NULL); + evas_object_smart_callback_add(edit_entry, "changed", entry_changed_cb, + NULL); + evas_object_smart_callback_add(edit_entry, "changed,user", + entry_changed_user_cb, NULL); + evas_object_smart_callback_add(edit_entry, "cursor,changed", + entry_cursor_changed_cb, NULL); + evas_object_smart_callback_add(edit_entry, "cursor,changed,manual", + entry_cursor_changed_manual_cb, NULL); + + return layout; +} + +/*****************************************************************************/ +/* Externally accessible calls */ +/*****************************************************************************/ + +void +ref_show(edit_data *ed) +{ + ref_data *md = g_md; + Evas_Object *edit_obj = NULL; + Evas_Object *edit_entry = NULL; + + if (!md) return; + if (!ed) return; + md->ed = ed; + + //Return if reference layout is already shown. + if (md->event_rect || md->layout) return; + + edit_obj = edit_obj_get(ed); + edit_entry = edit_entry_get(ed); + + //Set keyword name. + if (md->keyword_name) free(md->keyword_name); + md->keyword_name = cursor_keyword_name_find(edit_entry); + if (!md->keyword_name) return; + + //Set keyword desc. + if (md->keyword_desc) + { + free(md->keyword_desc); + md->keyword_desc = NULL; + } + keyword_data *keyword = cursor_keyword_data_find(edit_entry, + md->keyword_list, + md->keyword_name); + if (keyword) + md->keyword_desc = strdup(keyword->desc); + if (!md->keyword_desc) return; + + //Create event rect which catches mouse down event. + md->event_rect = ref_event_rect_create(edit_obj); + + //Create reference layout. + md->layout = ref_layout_create(edit_entry, md->keyword_desc); + + //Calculate reference layout position. + Evas_Coord obj_x, obj_y, obj_w, obj_h; + evas_object_geometry_get(edit_obj, &obj_x, &obj_y, &obj_w, &obj_h); + + Evas_Coord entry_x; + evas_object_geometry_get(edit_entry, &entry_x, NULL, NULL, NULL); + + Evas_Coord cur_x, cur_y, cur_h; + elm_entry_cursor_geometry_get(edit_entry, &cur_x, &cur_y, NULL, &cur_h); + + char *layout_width = + (char *)edje_object_data_get(elm_layout_edje_get(md->layout), "width"); + char *layout_height = + (char *)edje_object_data_get(elm_layout_edje_get(md->layout), "height"); + if (!layout_width || !layout_height) return; + + const Evas_Coord ref_w = ELM_SCALE_SIZE(atoi(layout_width)); + const Evas_Coord ref_h = ELM_SCALE_SIZE(atoi(layout_height)); + Evas_Coord ref_x = 0; + Evas_Coord ref_y = 0; + + //The center of reference layout is the entry cursor position. + if (cur_x < (ref_w / 2)) + ref_x = obj_x; + else if ((obj_w - (entry_x - obj_x + cur_x)) < (ref_w / 2)) + ref_x = obj_x + obj_w - ref_w; + else + ref_x = entry_x + cur_x - (ref_w / 2); + + if ((obj_h - cur_y - cur_h) < ref_h) + ref_y = obj_y + cur_y - ref_h; + else + ref_y = obj_y + cur_y + cur_h; + + evas_object_move(md->layout, ref_x, ref_y); +} + +void +ref_init(void) +{ + ref_data *md = calloc(1, sizeof(ref_data)); + if (!md) + { + EINA_LOG_ERR("Failed to allocate Memory!"); + return; + } + + ref_load(md); + + g_md = md; +} + +void +ref_term(void) +{ + ref_data *md = g_md; + + keyword_list_free(&(md->keyword_list)); + + if (md->keyword_name) free(md->keyword_name); + if (md->keyword_desc) free(md->keyword_desc); + + ref_event_rect_delete(); + ref_layout_delete(); + + free(md); + g_md = NULL; +} --
