edje_cc: support script inheritance

Summary:
When inherit_script is set to 1, script of current group contains
variables and funtions from script of parent groups. If there is same
name variable or function, newly defined one will replace that of
parents.

Reviewers: cedric, jpeg

Subscribers: taxi2se

Differential Revision: https://phab.enlightenment.org/D5062

Signed-off-by: Cedric Bail <cedric@osg.samsung.com>
This commit is contained in:
Jeeyong Um 2017-10-27 11:49:02 -07:00 committed by Cedric Bail
parent a191a052b8
commit 64fb807b63
4 changed files with 566 additions and 12 deletions

View File

@ -176,7 +176,9 @@ bin/edje/edje_cc_parse.c \
bin/edje/edje_cc_mem.c \
bin/edje/edje_cc_handlers.c \
bin/edje/edje_cc_sources.c \
bin/edje/edje_cc_script.c \
bin/edje/edje_multisense_convert.c
bin_edje_edje_cc_CPPFLAGS = -I$(top_builddir)/src/lib/efl $(EDJE_COMMON_CPPFLAGS) @EDJE_LUA_CFLAGS@
bin_edje_edje_cc_LDADD = $(USE_EDJE_BIN_LIBS) @EDJE_LUA_LIBS@
bin_edje_edje_cc_DEPENDENCIES = \

View File

@ -91,7 +91,10 @@ struct _Code
char *shared;
char *original;
Eina_List *programs;
int is_lua;
Eina_List *vars;
Eina_List *func;
Eina_Bool parsed : 1;
Eina_Bool is_lua : 1;
};
struct _Code_Program
@ -160,8 +163,10 @@ struct _Edje_Part_Collection_Parser
Eina_List *target_groups;
Eina_List *links;
Eina_Hash *link_hash;
Eina_List *base_codes;
Eina_Bool default_mouse_events;
Eina_Bool inherit_only;
Eina_Bool inherit_script : 1;
};
typedef enum
@ -297,6 +302,8 @@ int get_param_index(char *str);
void color_tree_root_free(void);
void convert_color_code(char *str, int *r, int *g, int *b, int *a);
void script_rewrite(Code *code);
/* global vars */
extern Eina_List *ext_dirs;
extern Eina_List *img_dirs;

View File

@ -163,7 +163,7 @@ static Edje_Part_Description_Common *parent_desc = NULL;
static Edje_Program *current_program = NULL;
static Eina_List *current_program_lookups = NULL;
Eina_Bool current_group_inherit = EINA_FALSE;
Eina_Bool script_override = EINA_FALSE;
Eina_Bool script_is_replaceable = EINA_FALSE;
static Edje_Program *sequencing = NULL;
static Eina_List *sequencing_lookups = NULL;
static int *anonymous_delete = NULL;
@ -268,6 +268,7 @@ static void st_collections_group_data_item(void);
static void st_collections_group_orientation(void);
static void st_collections_group_mouse_events(void);
static void st_collections_group_use_custom_seat_names(void);
static void st_collections_group_inherit_script(void);
static void st_collections_group_limits_vertical(void);
static void st_collections_group_limits_horizontal(void);
@ -550,6 +551,7 @@ static void st_collections_group_mouse(void);
static void st_collections_group_nomouse(void);
static void st_collections_group_broadcast(void);
static void st_collections_group_nobroadcast(void);
static void st_collections_group_noinherit_script(void);
static void st_images_vector(void);
static void _handle_vector_image(void);
@ -746,6 +748,7 @@ New_Statement_Handler statement_handlers[] =
{"collections.group.broadcast_signal", st_collections_group_broadcast_signal},
{"collections.group.orientation", st_collections_group_orientation},
{"collections.group.mouse_events", st_collections_group_mouse_events},
{"collections.group.inherit_script", st_collections_group_inherit_script},
{"collections.group.data.item", st_collections_group_data_item},
{"collections.group.limits.horizontal", st_collections_group_limits_horizontal},
{"collections.group.limits.vertical", st_collections_group_limits_vertical},
@ -1143,6 +1146,8 @@ New_Statement_Handler statement_handlers_short[] =
nobroadcast; -> broadcast_signal: 0;
mouse; -> mouse_events: 1;
nomouse; -> mouse_events: 0;
inherit_script; -> inherit_script: 1;
noinherit_script; -> inherit_script: 0;
parts {
part {
mouse; -> mouse_events: 1;
@ -1191,6 +1196,8 @@ New_Statement_Handler statement_handlers_short_single[] =
{"collections.group.nomouse", st_collections_group_nomouse},
{"collections.group.broadcast", st_collections_group_broadcast},
{"collections.group.nobroadcast", st_collections_group_nobroadcast},
{"collections.group.inherit_script", st_collections_group_inherit_script},
{"collections.group.noinherit_script", st_collections_group_noinherit_script},
{"collections.group.parts.part.description.inherit", st_collections_group_parts_part_description_inherit},
};
@ -4238,7 +4245,7 @@ ob_collections_group(void)
current_desc = NULL;
current_group_inherit = EINA_FALSE;
script_override = EINA_FALSE;
script_is_replaceable = EINA_FALSE;
current_de = mem_alloc(SZ(Edje_Part_Collection_Directory_Entry));
current_de->id = eina_list_count(edje_collections);
@ -4257,6 +4264,7 @@ ob_collections_group(void)
pcp = (Edje_Part_Collection_Parser *)pc;
pcp->default_mouse_events = 1;
pcp->inherit_script = EINA_FALSE;
pc->scene_size.width = 0;
pc->scene_size.height = 0;
@ -4835,6 +4843,7 @@ st_collections_group_inherit(void)
pcp = (Edje_Part_Collection_Parser *)pc;
pcp2 = (Edje_Part_Collection_Parser *)pc2;
pcp->default_mouse_events = pcp2->default_mouse_events;
pcp->inherit_script = pcp2->inherit_script;
/* as of 7 April 2014, target groups cannot be modified and are not freed.
* this code will break if that ever changes.
@ -4961,6 +4970,9 @@ st_collections_group_inherit(void)
cd = eina_list_data_get(eina_list_last(codes));
cd->is_lua = cd2->is_lua;
if (!cd2->is_lua)
pcp->base_codes = eina_list_append(pcp->base_codes, cd2);
if (cd2->shared)
{
if (cd->shared)
@ -4975,7 +4987,7 @@ st_collections_group_inherit(void)
cd->shared = STRDUP(cd2->shared);
cd->original = STRDUP(cd2->original);
script_override = EINA_TRUE;
script_is_replaceable = EINA_TRUE;
}
EINA_LIST_FOREACH(cd2->programs, l, cp2)
@ -5268,6 +5280,76 @@ st_collections_group_nomouse(void)
pcp->default_mouse_events = 0;
}
/**
@page edcref
@property
inherit_script
@parameters
[1 or 0]
@effect
Determine whether to inherit script block from parent group.
If it is set to 0, script from parent group will be replaced with
new script block.
Defaults to 0 if not set, to maintain compatibility.
@endproperty
*/
static void
st_collections_group_inherit_script(void)
{
Edje_Part_Collection_Parser *pcp;
pcp = eina_list_last_data_get(edje_collections);
if (get_arg_count() == 1)
pcp->inherit_script = parse_bool(0);
else
pcp->inherit_script = EINA_TRUE;
}
static void
st_collections_group_noinherit_script(void)
{
Edje_Part_Collection_Parser *pcp;
check_arg_count(0);
pcp = eina_list_last_data_get(edje_collections);
pcp->inherit_script = EINA_FALSE;
}
static void
_script_flush(void)
{
Edje_Part_Collection_Parser *pcp;
Code *code;
pcp = eina_list_last_data_get(edje_collections);
code = eina_list_last_data_get(codes);
if (!pcp->inherit_script || code->is_lua) return;
// If script is replaceable and overridable, code->shared will be inherited
// script. Free it to avoid duplication.
if (script_is_replaceable)
{
if (code->shared)
{
free(code->shared);
code->shared = NULL;
}
if (code->original)
{
free(code->original);
code->original = NULL;
}
}
script_rewrite(code);
eina_list_free(pcp->base_codes);
}
/**
@page edcref
@property
@ -5327,10 +5409,8 @@ st_collections_group_program_source(void)
static void
ob_collections_group_script(void)
{
Edje_Part_Collection *pc;
Code *cd;
pc = eina_list_last_data_get(edje_collections);
cd = eina_list_data_get(eina_list_last(codes));
if (!is_verbatim()) track_verbatim(1);
@ -5345,14 +5425,11 @@ ob_collections_group_script(void)
cd->l2 = get_verbatim_line2();
if (cd->shared)
{
if (script_override)
if (script_is_replaceable)
{
free(cd->shared);
free(cd->original);
script_override = EINA_FALSE;
WRN("%s:%i. Inherited script block in group \"%s\" is redefined. "
"This can break inherited edje programs.", file_in, line - 1, pc->part);
script_is_replaceable = EINA_FALSE;
}
else
{
@ -15825,7 +15902,10 @@ edje_cc_handlers_pop_notify(const char *token)
else if (current_program && (!strcmp(token, "link")))
current_program = NULL;
else if (current_de && (!strcmp(token, "group")))
_link_combine();
{
_link_combine();
_script_flush();
}
else if (current_desc && (!strcmp(token, "description")))
free_anchors();
}

View File

@ -0,0 +1,465 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "edje_cc.h"
#define MESSAGE_OVERRIDE
typedef struct _Code_Symbol
{
const char *name;
const char *tag;
Eina_List *args;
char *body;
Eina_Bool is_public : 1;
} Code_Symbol;
typedef enum
{
TOKEN_TYPE_INVALID = -1,
TOKEN_TYPE_EOF,
TOKEN_TYPE_COLON = (1 << 0),
TOKEN_TYPE_SEMICOLON = (1 << 1),
TOKEN_TYPE_COMMA = (1 << 2),
TOKEN_TYPE_PARENS = (1 << 3),
TOKEN_TYPE_BRACES = (1 << 4),
TOKEN_TYPE_EQUAL_MARK = (1 << 5),
TOKEN_TYPE_PUBLIC = (1 << 6),
TOKEN_TYPE_IDENTIFIER = (1 << 7)
} Token_Type;
typedef struct _Token
{
char *str;
Token_Type type;
} Token;
static void code_parse_internal(Code *code);
static Token *next_token(char **begin, char *end);
static void
code_parse(Code *code)
{
Edje_Part_Collection_Parser *pcp;
Code *base;
Eina_List *l;
int id;
if (code->is_lua) return;
id = eina_list_data_idx(codes, code);
pcp = eina_list_nth(edje_collections, id);
EINA_LIST_FOREACH(pcp->base_codes, l, base)
{
if (!base->parsed)
code_parse(base);
}
if (code->shared)
code_parse_internal(code);
code->parsed = EINA_TRUE;
}
static void
code_parse_internal(Code *code)
{
Code_Symbol *sym = NULL, *func;
Token *token, *tmp;
char *begin = code->shared;
char *end = begin + strlen(begin);
char *body;
Eina_Array *stack;
Eina_Bool is_args = EINA_FALSE;
Eina_Bool is_public = EINA_FALSE;
int depth = 0;
stack = eina_array_new(4);
while ((token = next_token(&begin, end)))
{
if (token->type == TOKEN_TYPE_EOF)
break;
// Variables in script cannot be initialized by assignment.
// Skip until value assignment expression ends.
if (token->type == TOKEN_TYPE_EQUAL_MARK)
{
while ((tmp = next_token(&begin, end)))
{
if ((tmp->type == TOKEN_TYPE_COMMA) ||
(tmp->type == TOKEN_TYPE_SEMICOLON))
{
token = tmp;
break;
}
}
}
switch (token->type)
{
case TOKEN_TYPE_COLON:
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->tag = eina_array_pop(stack);
break;
case TOKEN_TYPE_SEMICOLON:
if (eina_array_count(stack))
{
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->name = eina_array_pop(stack);
sym->is_public = is_public;
code->vars = eina_list_append(code->vars, sym);
sym = NULL;
}
is_public = EINA_FALSE;
break;
case TOKEN_TYPE_COMMA:
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->name = eina_array_pop(stack);
if (is_args)
func->args = eina_list_append(func->args, sym);
else
{
sym->is_public = is_public;
code->vars = eina_list_append(code->vars, sym);
}
sym = NULL;
break;
case TOKEN_TYPE_PARENS:
is_args = !is_args;
if (is_args)
{
if (!sym)
func = mem_alloc(SZ(Code_Symbol));
else
{
func = sym;
sym = NULL;
}
func->name = eina_array_pop(stack);
}
else
{
if (eina_array_count(stack))
{
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->name = eina_array_pop(stack);
func->args = eina_list_append(func->args, sym);
}
sym = func;
}
break;
case TOKEN_TYPE_BRACES:
depth = 1;
body = begin;
while ((tmp = next_token(&begin, end)))
{
if (tmp->type == TOKEN_TYPE_BRACES)
{
switch (tmp->str[0])
{
case '{':
depth++;
break;
case '}':
depth--;
break;
}
}
if (!depth)
break;
}
if ((begin - 1) > body)
{
sym->body = mem_alloc(sizeof(char) * (begin - body - 1));
strncpy(sym->body, body, (begin - body - 2));
}
sym->is_public = is_public;
code->func = eina_list_append(code->func, sym);
sym = NULL;
is_public = EINA_FALSE;
break;
case TOKEN_TYPE_PUBLIC:
is_public = EINA_TRUE;
break;
case TOKEN_TYPE_IDENTIFIER:
eina_array_push(stack, token->str);
token->str = NULL;
break;
default:
break;
}
if (token->str)
free(token->str);
free(token);
}
eina_array_free(stack);
}
static Token *
next_token(char **begin, char *end)
{
char buf[PATH_MAX] = { 0, };
char *src;
int index;
Token *token;
Eina_Bool parsed = EINA_FALSE;
if (!begin || (*begin >= end))
return NULL;
token = mem_alloc(SZ(Token));
token->type = TOKEN_TYPE_INVALID;
src = *begin - 1;
index = 0;
while (++src < end)
{
char ch = *src;
switch (ch)
{
case ' ':
case '\t':
case '\n':
if (index > 0)
parsed = EINA_TRUE;
break;
case ':':
case ';':
case ',':
case '(':
case ')':
case '{':
case '}':
case '=':
if (!index)
{
buf[index++] = ch;
src++;
}
goto exit;
default:
if (parsed)
goto exit;
buf[index++] = ch;
break;
}
}
exit:
switch (buf[0])
{
case ':':
token->type = TOKEN_TYPE_COLON;
break;
case ';':
token->type = TOKEN_TYPE_SEMICOLON;
break;
case ',':
token->type = TOKEN_TYPE_COMMA;
break;
case '(':
case ')':
token->type = TOKEN_TYPE_PARENS;
break;
case '{':
case '}':
token->type = TOKEN_TYPE_BRACES;
break;
case '=':
token->type = TOKEN_TYPE_EQUAL_MARK;
break;
case '\0':
token->type = TOKEN_TYPE_EOF;
break;
}
if (token->type < 0)
{
if (!strcmp(buf, "public"))
token->type = TOKEN_TYPE_PUBLIC;
else
token->type = TOKEN_TYPE_IDENTIFIER;
}
*begin = src;
token->str = strdup(buf);
return token;
}
static void
_push_symbol(Eina_List **total, Code_Symbol *sym, Edje_Part_Collection *pc)
{
Eina_List *list, *l;
Code_Symbol *sym2;
list = *total;
EINA_LIST_FOREACH(list, l, sym2)
{
if (!strcmp(sym2->name, sym->name))
{
WRN("Symbols in group \"%s\" have same name \"%s\". Latter defined "
"will shadow former one.", pc->part, sym->name);
list = eina_list_remove(list, sym2);
break;
}
}
list = eina_list_append(list, sym);
*total = list;
}
void
script_rewrite(Code *code)
{
Edje_Part_Collection *pc;
Edje_Part_Collection_Parser *pcp;
Code *base;
Eina_List *l, *ll;
int id, count;
Eina_Strbuf *buf;
Eina_List *vars = NULL;
Eina_List *func = NULL;
#ifdef MESSAGE_OVERRIDE
Eina_List *message = NULL;
#endif
Code_Symbol *sym, *arg;
code_parse(code);
id = eina_list_data_idx(codes, code);
pc = eina_list_nth(edje_collections, id);
pcp = (Edje_Part_Collection_Parser *)pc;
EINA_LIST_FOREACH(pcp->base_codes, l, base)
{
EINA_LIST_FOREACH(base->vars, ll, sym)
_push_symbol(&vars, sym, pc);
EINA_LIST_FOREACH(base->func, ll, sym)
{
#ifndef MESSAGE_OVERRIDE
_push_symbol(&func, sym, pc);
#else
if (strcmp(sym->name, "message"))
_push_symbol(&func, sym, pc);
else
message = eina_list_append(message, sym);
#endif
}
}
EINA_LIST_FOREACH(code->vars, l, sym)
_push_symbol(&vars, sym, pc);
EINA_LIST_FOREACH(code->func, l, sym)
{
#ifndef MESSAGE_OVERRIDE
_push_symbol(&func, sym, pc);
#else
if (strcmp(sym->name, "message"))
_push_symbol(&func, sym, pc);
else
message = eina_list_append(message, sym);
#endif
}
buf = eina_strbuf_new();
if (vars)
{
count = 0;
EINA_LIST_FOREACH(vars, l, sym)
{
if (!sym->is_public) continue;
if (count++)
eina_strbuf_append(buf, ", ");
else
eina_strbuf_append(buf, "public ");
if (sym->tag)
eina_strbuf_append_printf(buf, "%s:", sym->tag);
eina_strbuf_append(buf, sym->name);
}
if (count)
eina_strbuf_append(buf, ";\n");
count = 0;
EINA_LIST_FOREACH(vars, l, sym)
{
if (sym->is_public) continue;
if (count++)
eina_strbuf_append(buf, ", ");
if (sym->tag)
eina_strbuf_append_printf(buf, "%s:", sym->tag);
eina_strbuf_append(buf, sym->name);
}
if (count)
eina_strbuf_append(buf, ";\n");
}
if (func)
{
EINA_LIST_FOREACH(func, l, sym)
{
eina_strbuf_append(buf, "\n");
if (sym->is_public)
eina_strbuf_append(buf, "public ");
if (sym->tag)
eina_strbuf_append_printf(buf, "%s:", sym->tag);
eina_strbuf_append_printf(buf, "%s(", sym->name);
count = 0;
EINA_LIST_FOREACH(sym->args, ll, arg)
{
if (count++)
eina_strbuf_append(buf, ", ");
if (arg->tag)
eina_strbuf_append_printf(buf, "%s:", arg->tag);
eina_strbuf_append(buf, arg->name);
}
eina_strbuf_append(buf, ") {");
if (sym->body)
{
eina_strbuf_append(buf, sym->body);
eina_strbuf_rtrim(buf);
}
eina_strbuf_append(buf, "\n}\n");
}
}
#ifdef MESSAGE_OVERRIDE
if (message)
{
eina_strbuf_append(buf, "\npublic message(Msg_Type:type, id, ...) {");
EINA_LIST_FOREACH(message, l, sym)
{
eina_strbuf_append(buf, sym->body);
eina_strbuf_rtrim(buf);
eina_strbuf_append(buf, "\n");
}
eina_strbuf_append(buf, "}\n");
}
#endif
code->shared = eina_strbuf_string_steal(buf);
code->original = strdup(code->shared);
eina_strbuf_free(buf);
eina_list_free(vars);
eina_list_free(func);
}