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;
+}

-- 


Reply via email to