enventor/src/bin/edc_editor.c

1253 lines
34 KiB
C

#include <Elementary.h>
#include "common.h"
#include "template_code.h"
//FIXME: Make flexible
const int MAX_LINE_DIGIT_CNT = 10;
const int SYNTAX_COLOR_SPARE_LINES = 42;
const double SYNTAX_COLOR_DEFAULT_TIME = 0.25;
const double SYNTAX_COLOR_SHORT_TIME = 0.025;
typedef struct syntax_color_thread_data_s
{
edit_data *ed;
char *text;
const char *translated;
} syntax_color_td;
struct editor_s
{
Evas_Object *en_edit;
Evas_Object *en_line;
Evas_Object *scroller;
Evas_Object *layout;
Evas_Object *parent;
syntax_helper *sh;
parser_data *pd;
int cur_line;
int line_max;
int syntax_color_lock;
Evas_Coord scroller_h;
Ecore_Timer *syntax_color_timer;
Ecore_Thread *syntax_color_thread;
void (*view_sync_cb)(void *data, Eina_Stringshare *part_name,
Eina_Stringshare *group_name);
void *view_sync_cb_data;
int select_pos;
Eina_Bool edit_changed : 1;
Eina_Bool linenumber : 1;
Eina_Bool ctrl_pressed : 1;
Eina_Bool on_select_recover : 1;
};
static Eina_Bool
image_preview_show(edit_data *ed, char *cur, Evas_Coord x, Evas_Coord y);
static void
line_init(edit_data *ed)
{
char buf[MAX_LINE_DIGIT_CNT];
ed->line_max = 1;
snprintf(buf, sizeof(buf), "%d", ed->line_max);
elm_entry_entry_set(ed->en_line, buf);
}
static void
visible_text_region_get(edit_data *ed, int *from_line, int *to_line)
{
Evas_Coord region_y, region_h;
Evas_Coord cursor_h;
elm_scroller_region_get(ed->scroller, NULL, &region_y, NULL, &region_h);
elm_entry_cursor_geometry_get(ed->en_edit, NULL, NULL, NULL, &cursor_h);
int from = (region_y / cursor_h);
int to = from + (region_h / cursor_h);
from -= SYNTAX_COLOR_SPARE_LINES;
to += SYNTAX_COLOR_SPARE_LINES;
if (from < 1) from = 1;
if (to > ed->line_max) to = ed->line_max;
*from_line = from;
*to_line = to;
}
static void
entry_recover(edit_data *ed, int cursor_pos)
{
elm_entry_calc_force(ed->en_edit);
//recover cursor position??
elm_entry_cursor_pos_set(ed->en_edit, 0);
elm_entry_cursor_pos_set(ed->en_edit, cursor_pos);
//not on selection mode
if (ed->select_pos == -1) return;
//recover selection region
const char *select = elm_entry_selection_get(ed->en_edit);
if (!select) return;
char *utf8 = evas_textblock_text_markup_to_utf8(NULL, select);
ed->on_select_recover = EINA_TRUE;
elm_entry_select_none(ed->en_edit);
elm_entry_select_region_set(ed->en_edit, ed->select_pos, cursor_pos);
ed->on_select_recover = EINA_FALSE;
free(utf8);
}
static void
syntax_color_apply(edit_data *ed, Eina_Bool partial)
{
Evas_Object *tb = elm_entry_textblock_get(ed->en_edit);
char *text = (char *) evas_object_textblock_text_markup_get(tb);
int pos = elm_entry_cursor_pos_get(ed->en_edit);
int from_line = 1;
int to_line = -1;
if (partial) visible_text_region_get(ed, &from_line, &to_line);
char *from = NULL;
char *to = NULL;
char *utf8 = (char *) color_cancel(syntax_color_data_get(ed->sh), text,
strlen(text), from_line, to_line, &from,
&to);
if (!utf8) return;
const char *translated = color_apply(syntax_color_data_get(ed->sh), utf8,
strlen(utf8), from, to);
if (!translated) return;
/* I'm not sure this will be problem.
But it can avoid entry_object_text_escaped_set() in Edje.
Logically that's unnecessary in this case. */
evas_object_textblock_text_markup_set(tb, translated);
entry_recover(ed, pos);
}
static Eina_Bool
syntax_color_timer_cb(void *data)
{
edit_data *ed = data;
if (!color_ready(syntax_color_data_get(ed->sh))) return ECORE_CALLBACK_RENEW;
syntax_color_apply(ed, EINA_TRUE);
ed->syntax_color_timer = NULL;
return ECORE_CALLBACK_CANCEL;
}
static void
syntax_color_partial_update(edit_data *ed, double time)
{
/* If the syntax_color_full_update is requested forcely, lock would be -1
in this case, it should avoid partial updation by entry changed. */
if (ed->syntax_color_lock != 0) return;
ecore_thread_cancel(ed->syntax_color_thread);
ed->syntax_color_thread = NULL;
ecore_timer_del(ed->syntax_color_timer);
ed->syntax_color_timer = ecore_timer_add(time, syntax_color_timer_cb, ed);
}
static void
syntax_color_thread_cb(void *data, Ecore_Thread *thread EINA_UNUSED)
{
syntax_color_td *td = data;
char *utf8 = (char *) color_cancel(syntax_color_data_get(td->ed->sh),
td->text, strlen(td->text), -1, -1, NULL,
NULL);
if (!utf8) return;
td->translated = color_apply(syntax_color_data_get(td->ed->sh), utf8,
strlen(utf8), NULL, NULL);
}
static void
syntax_color_thread_end_cb(void *data, Ecore_Thread *thread EINA_UNUSED)
{
syntax_color_td *td = data;
if (!td->translated) return;
int pos = elm_entry_cursor_pos_get(td->ed->en_edit);
Evas_Object *tb = elm_entry_textblock_get(td->ed->en_edit);
/* I'm not sure this will be problem.
But it can avoid entry_object_text_escaped_set() in Edje.
Logically that's unnecessary in this case. */
evas_object_textblock_text_markup_set(tb, td->translated);
entry_recover(td->ed, pos);
td->ed->syntax_color_thread = NULL;
free(td);
}
static void
syntax_color_thread_cancel_cb(void *data, Ecore_Thread *thread EINA_UNUSED)
{
syntax_color_td *td = data;
td->ed->syntax_color_thread = NULL;
free(td);
}
void
syntax_color_full_update(edit_data *ed, Eina_Bool thread)
{
if (ed->syntax_color_lock > 0) return;
ecore_timer_del(ed->syntax_color_timer);
ed->syntax_color_timer = NULL;
if (thread)
{
syntax_color_td *td = malloc(sizeof(syntax_color_td));
td->ed = ed;
Evas_Object *tb = elm_entry_textblock_get(ed->en_edit);
td->text = (char *) evas_object_textblock_text_markup_get(tb);
ed->syntax_color_thread =
ecore_thread_run(syntax_color_thread_cb,
syntax_color_thread_end_cb,
syntax_color_thread_cancel_cb,
td);
}
else
{
syntax_color_apply(ed, EINA_FALSE);
}
}
static void
edit_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
Elm_Entry_Change_Info *info = event_info;
edit_data *ed = data;
edit_changed_set(ed, EINA_TRUE);
Eina_Bool syntax_color = EINA_TRUE;
if (info->insert)
{
if ((info->change.insert.plain_length == 1)&&
(info->change.insert.content[0] == ' ')) return;
if (!strcmp(info->change.insert.content, EOL))
{
edit_line_increase(ed, 1);
syntax_color = EINA_FALSE;
}
else
{
int increase =
parser_line_cnt_get(ed->pd, info->change.insert.content);
edit_line_increase(ed, increase);
}
if (config_auto_indent_get())
indent_insert_apply(syntax_indent_data_get(ed->sh), ed->en_edit,
info->change.insert.content, ed->cur_line);
}
else
{
int decrease = parser_line_cnt_get(ed->pd, info->change.del.content);
if (config_auto_indent_get())
{
if (indent_delete_apply(syntax_indent_data_get(ed->sh),
ed->en_edit, info->change.del.content,
ed->cur_line))
decrease++;
}
edit_line_decrease(ed, decrease);
if (info->change.del.content[0] == ' ') return;
}
if (!syntax_color) return;
syntax_color_partial_update(ed, SYNTAX_COLOR_DEFAULT_TIME);
}
static void
save_msg_show(edit_data *ed)
{
if (!config_stats_bar_get()) return;
char buf[PATH_MAX];
if (ed->edit_changed)
snprintf(buf, sizeof(buf), "File saved. \"%s\"", config_edc_path_get());
else
snprintf(buf, sizeof(buf), "Already saved. \"%s\"", config_edc_path_get());
stats_info_msg_update(buf);
}
Eina_Bool
edit_save(edit_data *ed)
{
if (!ed->edit_changed)
{
save_msg_show(ed);
return EINA_TRUE;
}
const char *text = elm_entry_entry_get(ed->en_edit);
char *utf8 = elm_entry_markup_to_utf8(text);
FILE *fp = fopen(config_edc_path_get(), "w");
if (!fp) return EINA_FALSE;
fputs(utf8, fp);
fclose(fp);
free(utf8);
save_msg_show(ed);
//FIXME: If compile edc here? we can edit_changed FALSE;
//edit_changed_set(ed, EINA_FALSE);
edit_view_sync(ed);
return EINA_TRUE;
}
static void
ctxpopup_candidate_dismiss_cb(void *data, Evas_Object *obj,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
evas_object_del(obj);
elm_object_disabled_set(ed->layout, EINA_FALSE);
elm_object_focus_set(ed->en_edit, EINA_TRUE);
}
static void
ctxpopup_candidate_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
edit_data *ed = data;
const char *text = event_info;
elm_entry_entry_insert(ed->en_edit, text);
elm_ctxpopup_dismiss(obj);
edit_changed_set(ed, EINA_TRUE);
edit_save(ed);
}
static void
ctxpopup_preview_dismiss_cb(void *data, Evas_Object *obj,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
int skip_focus = (int) evas_object_data_get(obj, "continue");
evas_object_del(obj);
//Since the ctxpopup will be shown again, Don't revert the focus.
if (skip_focus) return;
elm_object_disabled_set(ed->layout, EINA_FALSE);
elm_object_focus_set(ed->en_edit, EINA_TRUE);
}
void
edit_syntax_color_full_apply(edit_data *ed, Eina_Bool force)
{
int lock;
if (force)
{
lock = ed->syntax_color_lock;
ed->syntax_color_lock = -1;
}
syntax_color_full_update(ed, EINA_FALSE);
if (force) ed->syntax_color_lock = lock;
else ed->syntax_color_lock++;
}
void
edit_syntax_color_partial_apply(edit_data *ed)
{
if (ed->syntax_color_lock > 0) ed->syntax_color_lock = 0;
syntax_color_partial_update(ed, SYNTAX_COLOR_DEFAULT_TIME);
}
void
edit_template_insert(edit_data *ed)
{
const char *paragh = parser_paragh_name_get(ed->pd, ed->en_edit);
if (!paragh) return;
if (!strcmp(paragh, "parts"))
{
edit_template_part_insert(ed, EDJE_PART_TYPE_IMAGE);
return;
}
int line_cnt;
char **t = NULL;
char buf[64];
char buf2[12];
if (!strcmp(paragh, "part"))
{
line_cnt = TEMPLATE_DESC_LINE_CNT;
t = (char **) &TEMPLATE_DESC;
strcpy(buf2, "Description");
}
else if (!strcmp(paragh, "programs"))
{
line_cnt = TEMPLATE_PROG_LINE_CNT;
t = (char **) &TEMPLATE_PROG;
strcpy(buf2, "Program");
}
else if (!strcmp(paragh, "images"))
{
line_cnt = TEMPLATE_IMG_LINE_CNT;
t = (char **) &TEMPLATE_IMG;
strcpy(buf2, "Image File");
}
else if (!strcmp(paragh, "collections"))
{
line_cnt = TEMPLATE_GROUP_LINE_CNT;
t = (char **) &TEMPLATE_GROUP;
strcpy(buf2, "Group");
}
if (!t)
{
stats_info_msg_update("Can't insert template code here. Move the cursor inside the \"Collections,Images,Parts,Part,Programs\" scope.");
return;
}
int cursor_pos = elm_entry_cursor_pos_get(ed->en_edit);
elm_entry_cursor_line_begin_set(ed->en_edit);
int space = indent_space_get(syntax_indent_data_get(ed->sh), ed->en_edit);
//Alloc Empty spaces
char *p = alloca(space + 1);
memset(p, ' ', space);
p[space] = '\0';
int i;
for (i = 0; i < (line_cnt - 1); i++)
{
elm_entry_entry_insert(ed->en_edit, p);
elm_entry_entry_insert(ed->en_edit, t[i]);
}
edit_line_increase(ed, (line_cnt -1));
elm_entry_entry_insert(ed->en_edit, p);
elm_entry_entry_insert(ed->en_edit, t[i]);
elm_entry_cursor_pos_set(ed->en_edit, cursor_pos);
syntax_color_partial_update(ed, 0);
snprintf(buf, sizeof(buf), "Template code inserted. (%s)", buf2);
stats_info_msg_update(buf);
}
int
edit_cur_indent_depth_get(edit_data *ed)
{
return indent_space_get(syntax_indent_data_get(ed->sh), ed->en_edit);
}
void
edit_template_part_insert(edit_data *ed, Edje_Part_Type type)
{
if (type == EDJE_PART_TYPE_NONE) return;
int cursor_pos = elm_entry_cursor_pos_get(ed->en_edit);
elm_entry_cursor_line_begin_set(ed->en_edit);
int space = indent_space_get(syntax_indent_data_get(ed->sh), ed->en_edit);
//Alloc Empty spaces
char *p = alloca(space + 1);
memset(p, ' ', space);
p[space] = '\0';
int line_cnt;
char **t;
char buf[64];
char part[20];
switch(type)
{
case EDJE_PART_TYPE_RECTANGLE:
line_cnt = TEMPLATE_PART_RECT_LINE_CNT;
t = (char **) &TEMPLATE_PART_RECT;
strcpy(part, "Rect");
break;
case EDJE_PART_TYPE_TEXT:
line_cnt = TEMPLATE_PART_TEXT_LINE_CNT;
t = (char **) &TEMPLATE_PART_TEXT;
strcpy(part, "Text");
break;
case EDJE_PART_TYPE_SWALLOW:
line_cnt = TEMPLATE_PART_SWALLOW_LINE_CNT;
t = (char **) &TEMPLATE_PART_SWALLOW;
strcpy(part, "Swallow");
break;
case EDJE_PART_TYPE_TEXTBLOCK:
line_cnt = TEMPLATE_PART_TEXTBLOCK_LINE_CNT;
t = (char **) &TEMPLATE_PART_TEXTBLOCK;
strcpy(part, "Textblock");
break;
case EDJE_PART_TYPE_SPACER:
line_cnt = TEMPLATE_PART_SPACER_LINE_CNT;
t = (char **) &TEMPLATE_PART_SPACER;
strcpy(part, "Spacer");
break;
case EDJE_PART_TYPE_IMAGE:
case EDJE_PART_TYPE_NONE:
case EDJE_PART_TYPE_GRADIENT:
case EDJE_PART_TYPE_GROUP:
case EDJE_PART_TYPE_BOX:
case EDJE_PART_TYPE_TABLE:
case EDJE_PART_TYPE_EXTERNAL:
case EDJE_PART_TYPE_PROXY:
case EDJE_PART_TYPE_LAST:
line_cnt = TEMPLATE_PART_IMAGE_LINE_CNT;
t = (char **) &TEMPLATE_PART_IMAGE;
strcpy(part, "Image");
break;
}
int i;
for (i = 0; i < (line_cnt - 1); i++)
{
elm_entry_entry_insert(ed->en_edit, p);
elm_entry_entry_insert(ed->en_edit, t[i]);
//Incease line by (line count - 1)
edit_line_increase(ed, 1);
}
elm_entry_entry_insert(ed->en_edit, p);
elm_entry_entry_insert(ed->en_edit, t[i]);
elm_entry_cursor_pos_set(ed->en_edit, cursor_pos);
syntax_color_partial_update(ed, 0);
snprintf(buf, sizeof(buf), "Template code inserted. (%s Part)", part);
stats_info_msg_update(buf);
}
static void
cur_line_pos_set(edit_data *ed, Eina_Bool force)
{
Evas_Coord y, h;
elm_entry_cursor_geometry_get(ed->en_edit, NULL, &y, NULL, &h);
int line = (y / h) + 1;
if (line < 0) line = 1;
if (!force && (ed->cur_line == line)) return;
ed->cur_line = line;
if (!config_stats_bar_get()) return;
stats_line_num_update(ed->cur_line, ed->line_max);
}
static void
program_run(edit_data *ed, char *cur)
{
char *program = parser_name_get(ed->pd, cur);
if (program)
{
view_data *vd = edj_mgr_view_get(NULL);
view_program_run(vd, program);
free(program);
}
}
//This function is called when user press up/down key or mouse wheel up/down
static void
preview_img_relay_show(edit_data *ed, Evas_Object *ctxpopup, Eina_Bool next)
{
if (next) elm_entry_cursor_down(ed->en_edit);
else elm_entry_cursor_up(ed->en_edit);
Evas_Object *textblock = elm_entry_textblock_get(ed->en_edit);
Evas_Textblock_Cursor *cursor = evas_object_textblock_cursor_get(textblock);
const char *str = evas_textblock_cursor_paragraph_text_get(cursor);
char *text = elm_entry_markup_to_utf8(str);
//Compute current ctxpopup position.
Evas_Coord x, y, h;
evas_object_geometry_get(ctxpopup, &x, &y, NULL, NULL);
elm_entry_cursor_geometry_get(ed->en_edit, NULL, NULL, NULL, &h);
if (next) y += h;
else y -= h;
//Limit the ctxpopup position in the scroller vertical zone.
Evas_Coord scrl_y, scrl_h;
evas_object_geometry_get(ed->scroller, NULL, &scrl_y, NULL, &scrl_h);
if (y > (scrl_y + scrl_h)) y = (scrl_y + scrl_h);
else if (y < scrl_y) y = scrl_y;
if (image_preview_show(ed, text, x, y))
{
/* Since the ctxpopup will be shown again,
Don't revert the focus in the dismiss cb. */
evas_object_data_set(ctxpopup, "continue", (void *) 1);
}
menu_ctxpopup_unregister(ctxpopup);
elm_ctxpopup_dismiss(ctxpopup);
}
static void
ctxpopup_preview_relay_cb(void *data, Evas_Object *obj, void *event_info)
{
edit_data *ed = data;
int next = (int) event_info;
preview_img_relay_show(ed, obj, (Eina_Bool) next);
}
static Eina_Bool
image_preview_show(edit_data *ed, char *cur, Evas_Coord x, Evas_Coord y)
{
char *filename = parser_name_get(ed->pd, cur);
if (!filename) return EINA_FALSE;
char fullpath[PATH_MAX];
//1.Find the image path.
Eina_List *list = config_edc_img_path_list_get();
Eina_List *l;
char *path;
Eina_Bool found = EINA_FALSE;
EINA_LIST_FOREACH(list, l, path)
{
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, filename);
if (!ecore_file_exists(fullpath)) continue;
found = EINA_TRUE;
break;
}
//2. Find in the default fault.
if (!found)
{
snprintf(fullpath, sizeof(fullpath), "%s/images/%s",
elm_app_data_dir_get(), filename);
if (ecore_file_exists(fullpath)) found = EINA_TRUE;
}
Eina_Bool succeed;
//Create Ctxpopup with the image pathes.
if (found)
{
Evas_Object *ctxpopup =
ctxpopup_img_preview_create(ed, fullpath,
ctxpopup_preview_dismiss_cb,
ctxpopup_preview_relay_cb);
if (!ctxpopup) return EINA_FALSE;
evas_object_move(ctxpopup, x, y);
evas_object_show(ctxpopup);
menu_ctxpopup_register(ctxpopup);
elm_object_disabled_set(ed->layout, EINA_TRUE);
succeed = EINA_TRUE;
}
else
{
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "Failed to load the image. \"%s\"",
filename);
stats_info_msg_update(buf);
succeed = EINA_FALSE;
}
free(filename);
return succeed;
}
static void
candidate_list_show(edit_data *ed, char *text, char *cur, char *selected)
{
attr_value * attr = parser_attribute_get(ed->pd, text, cur);
if (!attr) return;
//Show up the list of the types
Evas_Object *ctxpopup =
ctxpopup_candidate_list_create(ed, attr, atof(selected),
ctxpopup_candidate_dismiss_cb,
ctxpopup_candidate_selected_cb);
if (!ctxpopup) return;
int x, y;
evas_pointer_output_xy_get(evas_object_evas_get(ed->en_edit), &x, &y);
evas_object_move(ctxpopup, x, y);
evas_object_show(ctxpopup);
menu_ctxpopup_register(ctxpopup);
elm_object_disabled_set(ed->layout, EINA_TRUE);
}
static void
edit_cursor_double_clicked_cb(void *data, Evas_Object *obj,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
if (ed->ctrl_pressed) return;
char *selected = (char *) elm_entry_selection_get(obj);
if (!selected) return;
selected = elm_entry_markup_to_utf8(selected);
if (selected[0] == '\"')
{
free(selected);
return;
}
Evas_Object *textblock = elm_entry_textblock_get(obj);
Evas_Textblock_Cursor *cursor = evas_object_textblock_cursor_get(textblock);
const char *str = evas_textblock_cursor_paragraph_text_get(cursor);
char *text = elm_entry_markup_to_utf8(str);
char *cur = strstr(text, selected);
if (!strcmp(selected, "program"))
{
program_run(ed, cur);
}
else if ((!strncmp(selected, "image", 5)) || //5: sizeof("image")
(!strcmp(selected, "normal")) ||
(!strcmp(selected, "tween")))
{
int x, y;
evas_pointer_output_xy_get(evas_object_evas_get(ed->en_edit), &x, &y);
image_preview_show(ed, cur, x, y);
}
else
candidate_list_show(ed, text, cur, selected);
free(selected);
free(text);
}
static void
cur_name_get_cb(void *data, Eina_Stringshare *part_name,
Eina_Stringshare *group_name)
{
edit_data *ed = data;
if (ed->view_sync_cb)
ed->view_sync_cb(ed->view_sync_cb_data, part_name, group_name);
}
void
edit_view_sync(edit_data *ed)
{
if (!config_part_highlight_get())
parser_cur_group_name_get(ed->pd, ed->en_edit, cur_name_get_cb, ed);
else
parser_cur_name_get(ed->pd, ed->en_edit, cur_name_get_cb, ed);
}
static void
edit_selection_cleared_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
if (ed->on_select_recover) return;
cur_line_pos_set(ed, EINA_TRUE);
ed->select_pos = -1;
}
static void
edit_selection_start_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
if (ed->select_pos != -1) return;
ed->select_pos = elm_entry_cursor_pos_get(ed->en_edit);
}
static void
edit_cursor_changed_manual_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
edit_view_sync(ed);
}
static void
edit_cursor_changed_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
cur_line_pos_set(ed, EINA_FALSE);
}
void
edit_view_sync_cb_set(edit_data *ed,
void (*cb)(void *data, Eina_Stringshare *part_name,
Eina_Stringshare *group_name), void *data)
{
ed->view_sync_cb = cb;
ed->view_sync_cb_data = data;
}
void
edit_line_delete(edit_data *ed)
{
if (!elm_object_focus_get(ed->en_edit)) return;
Evas_Object *textblock = elm_entry_textblock_get(ed->en_edit);
int line1 = ed->cur_line - 1;
int line2 = ed->cur_line;
//min position case
if (line1 < 0)
{
line1 = 0;
line2 = 1;
}
//Max position case
Eina_Bool max = EINA_FALSE;
if (line2 >= ed->line_max)
{
line1 = (ed->line_max - 2);
line2 = (ed->line_max - 1);
max = EINA_TRUE;
}
//only one line remain. clear it.
if (ed->line_max == 1)
{
elm_entry_entry_set(ed->en_edit, "");
line_init(ed);
return;
}
Evas_Textblock_Cursor *cur1 = evas_object_textblock_cursor_new(textblock);
evas_textblock_cursor_line_set(cur1, line1);
if (max) evas_textblock_cursor_line_char_last(cur1);
Evas_Textblock_Cursor *cur2 = evas_object_textblock_cursor_new(textblock);
evas_textblock_cursor_line_set(cur2, line2);
if (max) evas_textblock_cursor_line_char_last(cur2);
evas_textblock_cursor_range_delete(cur1, cur2);
evas_textblock_cursor_free(cur1);
evas_textblock_cursor_free(cur2);
elm_entry_calc_force(ed->en_edit);
edit_line_decrease(ed, 1);
cur_line_pos_set(ed, EINA_TRUE);
edit_changed_set(ed, EINA_TRUE);
}
static Eina_Bool
key_down_cb(void *data, int type EINA_UNUSED, void *ev)
{
Ecore_Event_Key *event = ev;
edit_data *ed = data;
//Control Key
if (!strcmp("Control_L", event->key))
ed->ctrl_pressed = EINA_TRUE;
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
key_up_cb(void *data, int type EINA_UNUSED, void *ev)
{
Ecore_Event_Key *event = ev;
edit_data *ed = data;
//Control Key
if (!strcmp("Control_L", event->key))
ed->ctrl_pressed = EINA_FALSE;
return ECORE_CALLBACK_PASS_ON;
}
static void
scroller_scroll_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
syntax_color_partial_update(ed, SYNTAX_COLOR_SHORT_TIME);
}
static void
scroller_resize_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
edit_data *ed = data;
Evas_Coord h;
evas_object_geometry_get(obj, NULL, NULL, NULL, &h);
if (h == ed->scroller_h) return;
syntax_color_partial_update(ed, SYNTAX_COLOR_DEFAULT_TIME);
ed->scroller_h = h;
}
static void
scroller_vbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
syntax_color_full_update(ed, EINA_TRUE);
ed->syntax_color_lock++;
}
static void
scroller_vbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
edit_data *ed = data;
ed->syntax_color_lock--;
syntax_color_partial_update(ed, SYNTAX_COLOR_SHORT_TIME);
}
edit_data *
edit_init(Evas_Object *parent)
{
parser_data *pd = parser_init();
syntax_helper *sh = syntax_init();
edit_data *ed = calloc(1, sizeof(edit_data));
ed->pd = pd;
ed->sh = sh;
ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, key_down_cb, ed);
ecore_event_handler_add(ECORE_EVENT_KEY_UP, key_up_cb, ed);
//Scroller
Evas_Object *scroller = elm_scroller_add(parent);
elm_scroller_policy_set(scroller, ELM_SCROLLER_POLICY_AUTO,
ELM_SCROLLER_POLICY_AUTO);
elm_object_focus_allow_set(scroller, EINA_FALSE);
evas_object_smart_callback_add(scroller, "scroll,up", scroller_scroll_cb,
ed);
evas_object_smart_callback_add(scroller, "scroll,down", scroller_scroll_cb,
ed);
evas_object_smart_callback_add(scroller, "vbar,press",
scroller_vbar_press_cb, ed);
evas_object_smart_callback_add(scroller, "vbar,unpress",
scroller_vbar_unpress_cb, ed);
evas_object_event_callback_add(scroller, EVAS_CALLBACK_RESIZE,
scroller_resize_cb, ed);
evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
//Layout
Evas_Object *layout = elm_layout_add(scroller);
elm_layout_file_set(layout, EDJE_PATH, "edit_layout");
evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_content_set(scroller, layout);
//Line Number Entry
Evas_Object *en_line = elm_entry_add(layout);
evas_object_color_set(en_line, 101, 101, 101, 255);
elm_entry_editable_set(en_line, EINA_FALSE);
elm_entry_line_wrap_set(en_line, ELM_WRAP_NONE);
evas_object_size_hint_weight_set(en_line, 0, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(en_line, 0, EVAS_HINT_FILL);
elm_object_part_content_set(layout, "elm.swallow.linenumber", en_line);
//EDC Editor Entry
Evas_Object *en_edit = elm_entry_add(layout);
elm_entry_context_menu_disabled_set(en_edit, EINA_TRUE);
elm_entry_line_wrap_set(en_edit, ELM_WRAP_NONE);
evas_object_smart_callback_add(en_edit, "changed,user", edit_changed_cb, ed);
evas_object_smart_callback_add(en_edit, "cursor,changed,manual",
edit_cursor_changed_manual_cb, ed);
evas_object_smart_callback_add(en_edit, "cursor,changed",
edit_cursor_changed_cb, ed);
evas_object_smart_callback_add(en_edit, "clicked,double",
edit_cursor_double_clicked_cb, ed);
evas_object_smart_callback_add(en_edit, "selection,cleared",
edit_selection_cleared_cb, ed);
evas_object_smart_callback_add(en_edit, "selection,start",
edit_selection_start_cb, ed);
evas_object_size_hint_weight_set(en_edit, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(en_edit, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_focus_set(en_edit, EINA_TRUE);
elm_object_part_content_set(layout, "elm.swallow.edit", en_edit);
ed->scroller = scroller;
ed->en_line = en_line;
ed->en_edit = en_edit;
ed->layout = layout;
ed->parent = parent;
ed->linenumber = EINA_TRUE;
ed->cur_line = -1;
ed->select_pos = -1;
edit_line_number_toggle(ed);
edit_font_size_update(ed, EINA_FALSE, EINA_FALSE);
return ed;
}
void
edit_editable_set(edit_data *ed, Eina_Bool editable)
{
elm_entry_editable_set(ed->en_edit, editable);
}
Evas_Object *
edit_obj_get(edit_data *ed)
{
return ed->scroller;
}
void
edit_term(edit_data *ed)
{
if (!ed) return;
syntax_helper *sh = ed->sh;
parser_data *pd = ed->pd;
ecore_thread_cancel(ed->syntax_color_thread);
ecore_timer_del(ed->syntax_color_timer);
free(ed);
syntax_term(sh);
parser_term(pd);
}
void
edit_edc_read(edit_data *ed, const char *file_path)
{
char buf[MAX_LINE_DIGIT_CNT];
Eina_File *file = NULL;
Eina_Strbuf *strbuf_line = NULL;
char *utf8_edit = NULL;
char *markup_edit = NULL;
char *markup_line = NULL;
int line_num = 1;
ed->line_max = 0;
autocomp_target_set(NULL);
file = eina_file_open(file_path, EINA_FALSE);
if (!file) goto err;
strbuf_line = eina_strbuf_new();
if (!strbuf_line) goto err;
utf8_edit = eina_file_map_all(file, EINA_FILE_REMOVE);
if (!utf8_edit) goto err;
//Append line numbers
if (!eina_strbuf_append_char(strbuf_line, '1')) goto err;
char *p = utf8_edit;
int len = strlen(p);
while ((p = strchr(p, '\n')) && p < (utf8_edit + len))
{
line_num++;
++p;
sprintf(buf, "\n%d", line_num);
if (!eina_strbuf_append(strbuf_line, buf)) goto err;
}
markup_line = elm_entry_utf8_to_markup(eina_strbuf_string_get(strbuf_line));
if (!markup_line) goto err;
elm_entry_entry_append(ed->en_line, markup_line);
free(markup_line);
markup_edit = elm_entry_utf8_to_markup(utf8_edit);
if (!markup_edit) goto err;
elm_entry_entry_set(ed->en_edit, markup_edit);
free(markup_edit);
ed->line_max = line_num;
Eina_Stringshare *group_name =
parser_first_group_name_get(ed->pd, ed->en_edit);
stats_edc_group_update(group_name);
stats_line_num_update(0, ed->line_max);
base_title_set(config_edc_path_get());
ecore_animator_add(syntax_color_timer_cb, ed);
err:
//Even any text is not inserted, line number should start with 1
if (ed->line_max == 0) line_init(ed);
if (strbuf_line) eina_strbuf_free(strbuf_line);
if (utf8_edit) eina_file_map_free(file, utf8_edit);
if (file) eina_file_close(file);
autocomp_target_set(ed);
}
void
edit_focus_set(edit_data *ed)
{
elm_object_focus_set(ed->en_edit, EINA_TRUE);
}
Eina_Bool
edit_changed_get(edit_data *ed)
{
return ed->edit_changed;
}
void
edit_changed_set(edit_data *ed, Eina_Bool changed)
{
ed->edit_changed = changed;
}
void
edit_line_number_toggle(edit_data *ed)
{
//FIXME: edit & config toggle should be handled in one place.
Eina_Bool linenumber = config_linenumber_get();
if (ed->linenumber == linenumber) return;
ed->linenumber = linenumber;
if (linenumber)
elm_object_signal_emit(ed->layout, "elm,state,linenumber,show", "");
else
elm_object_signal_emit(ed->layout, "elm,state,linenumber,hide", "");
}
void
edit_new(edit_data *ed)
{
parser_cancel(ed->pd);
elm_entry_entry_set(ed->en_edit, "");
elm_entry_entry_set(ed->en_line, "");
edit_edc_read(ed, config_edc_path_get());
edit_changed_set(ed, EINA_TRUE);
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "File Path: \"%s\"", config_edc_path_get());
stats_info_msg_update(buf);
}
void
edit_font_size_update(edit_data *ed, Eina_Bool msg, Eina_Bool update)
{
elm_object_scale_set(ed->layout, config_font_size_get());
if (!msg) return;
char buf[128];
snprintf(buf, sizeof(buf), "Font Size: %1.1fx", config_font_size_get());
stats_info_msg_update(buf);
if (update) syntax_color_partial_update(ed, 0);
}
void
edit_part_highlight_toggle(edit_data *ed, Eina_Bool msg)
{
Eina_Bool highlight = config_part_highlight_get();
if (highlight) edit_view_sync(ed);
else view_part_highlight_set(VIEW_DATA, NULL);
if (!msg) return;
if (highlight)
stats_info_msg_update("Part Highlighting Enabled.");
else
stats_info_msg_update("Part Highlighting Disabled.");
}
void
edit_edc_reload(edit_data *ed, const char *edc_path)
{
config_edc_path_set(edc_path);
edit_new(ed);
edj_mgr_reload_need_set(EINA_TRUE);
config_apply();
}
Eina_Stringshare *
edit_cur_prog_name_get(edit_data *ed)
{
return parser_cur_name_fast_get(ed->en_edit, "program");
}
Eina_Stringshare *
edit_cur_part_name_get(edit_data *ed)
{
return parser_cur_name_fast_get(ed->en_edit, "part");
}
int
edit_max_line_get(edit_data *ed)
{
return ed->line_max;
}
void
edit_goto(edit_data *ed, int line)
{
elm_entry_select_none(ed->en_edit);
Evas_Object *tb = elm_entry_textblock_get(ed->en_edit);
Evas_Textblock_Cursor *cur = evas_object_textblock_cursor_get(tb);
evas_textblock_cursor_line_set(cur, (line - 1));
elm_entry_calc_force(ed->en_edit);
elm_object_focus_set(ed->en_edit, EINA_TRUE);
}
Evas_Object *
edit_entry_get(edit_data *ed)
{
return ed->en_edit;
}
void
edit_line_increase(edit_data *ed, int cnt)
{
char buf[MAX_LINE_DIGIT_CNT];
int i;
for (i = 0; i < cnt; i++)
{
ed->line_max++;
snprintf(buf, sizeof(buf), "<br/>%d", ed->line_max);
elm_entry_entry_append(ed->en_line, buf);
}
elm_entry_calc_force(ed->en_line);
}
void
edit_line_decrease(edit_data *ed, int cnt)
{
if (cnt < 1) return;
Evas_Object *textblock = elm_entry_textblock_get(ed->en_line);
Evas_Textblock_Cursor *cur1 = evas_object_textblock_cursor_new(textblock);
Evas_Textblock_Cursor *cur2 = evas_object_textblock_cursor_new(textblock);
int i = cnt;
while (i)
{
evas_textblock_cursor_paragraph_last(cur1);
evas_textblock_cursor_paragraph_prev(cur1);
evas_textblock_cursor_paragraph_last(cur2);
evas_textblock_cursor_range_delete(cur1, cur2);
i--;
}
evas_textblock_cursor_free(cur1);
evas_textblock_cursor_free(cur2);
elm_entry_calc_force(ed->en_line);
ed->line_max -= cnt;
if (ed->line_max < 1) line_init(ed);
}