diff --git a/ChangeLog b/ChangeLog index aac0318..0130c03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 2016-02-21 ajwillia.ms (Andy Williams) * Fix crash when deleting a selection ending in newline - * Begin the re-add of the undo feature (just text insertion now) + * Begin the re-add of the undo feature (just keyboard input so far) 2016-02-11 ajwillia.ms (Andy Williams) diff --git a/elm_code/src/lib/widget/elm_code_widget.c b/elm_code/src/lib/widget/elm_code_widget.c index fe78a82..203c1d1 100644 --- a/elm_code/src/lib/widget/elm_code_widget.c +++ b/elm_code/src/lib/widget/elm_code_widget.c @@ -988,14 +988,14 @@ _elm_code_widget_delete_selection(Elm_Code_Widget *widget) } static Elm_Code_Widget_Change_Info * -_elm_code_widget_change_create_insert(unsigned int start_col, unsigned int start_line, - unsigned int end_col, unsigned int end_line, - const char *text, unsigned int length) +_elm_code_widget_change_create(unsigned int start_col, unsigned int start_line, + unsigned int end_col, unsigned int end_line, + const char *text, unsigned int length, Eina_Bool insert) { Elm_Code_Widget_Change_Info *info; info = calloc(1, sizeof(*info)); - info->insert = EINA_TRUE; + info->insert = insert; info->start_col = start_col; info->start_line = start_line; @@ -1003,7 +1003,8 @@ _elm_code_widget_change_create_insert(unsigned int start_col, unsigned int start info->end_line = end_line; info->content = malloc((length + 1) * sizeof(char)); - strncpy((char *)info->content, text, length + 1); + strncpy(info->content, text, length); + info->content[length] = '\0'; info->length = length; return info; @@ -1045,7 +1046,7 @@ _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char *text elm_obj_code_widget_cursor_position_set(col + col_width, row), eo_event_callback_call(ELM_CODE_WIDGET_EVENT_CHANGED_USER, NULL)); - change = _elm_code_widget_change_create_insert(col, row, col + col_width - 1, row, text, length); + change = _elm_code_widget_change_create(col, row, col + col_width - 1, row, text, length, EINA_TRUE); _elm_code_widget_undo_change_add(widget, change); _elm_code_widget_change_free(change); } @@ -1054,7 +1055,6 @@ static void _elm_code_widget_tab_at_cursor_insert(Elm_Code_Widget *widget) { Elm_Code_Widget_Data *pd; - Elm_Code_Widget_Change_Info *change; unsigned int col, row, rem; pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS); @@ -1113,7 +1113,7 @@ _elm_code_widget_newline(Elm_Code_Widget *widget) elm_obj_code_widget_cursor_position_set(indent, row + 1), eo_event_callback_call(ELM_CODE_WIDGET_EVENT_CHANGED_USER, NULL)); - change = _elm_code_widget_change_create_insert(width + 1, row, indent - 1, row + 1, "\n", 1); + change = _elm_code_widget_change_create(width + 1, row, indent - 1, row + 1, "\n", 1, EINA_TRUE); _elm_code_widget_undo_change_add(widget, change); _elm_code_widget_change_free(change); } @@ -1149,15 +1149,17 @@ _elm_code_widget_backspaceline(Elm_Code_Widget *widget, Eina_Bool nextline) eo_do(widget, eo_event_callback_call(ELM_CODE_WIDGET_EVENT_CHANGED_USER, NULL)); } -static void +void _elm_code_widget_backspace(Elm_Code_Widget *widget) { Elm_Code *code; Elm_Code_Line *line; + Elm_Code_Widget_Change_Info *change; unsigned int row, col, position, start_col, char_width; + const char *text; if (_elm_code_widget_delete_selection(widget)) - return; + return; // TODO fire the change and log it eo_do(widget, code = elm_obj_code_widget_code_get(), @@ -1179,23 +1181,29 @@ _elm_code_widget_backspace(Elm_Code_Widget *widget) elm_code_widget_line_text_position_for_column_get(widget, line, col - 1)); char_width = position - elm_code_widget_line_text_position_for_column_get(widget, line, start_col); + text = elm_code_widget_text_between_positions_get(widget, start_col, row, start_col, row); elm_code_line_text_remove(line, position - char_width, char_width); eo_do(widget, elm_obj_code_widget_cursor_position_set(start_col, row)); -// TODO construct and pass a change object eo_do(widget, eo_event_callback_call(ELM_CODE_WIDGET_EVENT_CHANGED_USER, NULL)); + + change = _elm_code_widget_change_create(start_col, row, col, row, text, char_width, EINA_FALSE); + _elm_code_widget_undo_change_add(widget, change); + _elm_code_widget_change_free(change); } -static void +void _elm_code_widget_delete(Elm_Code_Widget *widget) { Elm_Code *code; Elm_Code_Line *line; + Elm_Code_Widget_Change_Info *change; unsigned int row, col, position, char_width, start_col; + const char *text; if (_elm_code_widget_delete_selection(widget)) - return; + return; // TODO fire the change and log it eo_do(widget, code = elm_obj_code_widget_code_get(), @@ -1214,11 +1222,15 @@ _elm_code_widget_delete(Elm_Code_Widget *widget) char_width = elm_code_widget_line_text_position_for_column_get(widget, line, col + 1) - position; start_col = elm_code_widget_line_text_column_width_to_position(widget, line, position); + text = elm_code_widget_text_between_positions_get(widget, start_col, row, start_col, row); elm_code_line_text_remove(line, position, char_width?char_width:1); eo_do(widget, elm_obj_code_widget_cursor_position_set(start_col, row), -// TODO construct and pass a change object eo_event_callback_call(ELM_CODE_WIDGET_EVENT_CHANGED_USER, NULL)); + + change = _elm_code_widget_change_create(start_col, row, col, row, text, char_width, EINA_FALSE); + _elm_code_widget_undo_change_add(widget, change); + _elm_code_widget_change_free(change); } static void diff --git a/elm_code/src/lib/widget/elm_code_widget.eo b/elm_code/src/lib/widget/elm_code_widget.eo index e07459a..ace043a 100644 --- a/elm_code/src/lib/widget/elm_code_widget.eo +++ b/elm_code/src/lib/widget/elm_code_widget.eo @@ -175,6 +175,15 @@ class Elm.Code_Widget (Elm.Layout, Elm.Interface_Atspi_Text) text_line_number_width_get { return: int; [[the column width required to represent the number of lines in the widget.]] } + text_between_positions_get { + params { + start_col: uint; [[the widget column of the first character to get]] + start_line: uint; [[the line of the first character to get]] + end_col: uint; [[the widget column of the last character to get]] + end_line: uint; [[the line of the last character to get]] + } + return: char *; [[the text content between start and end positions]] + } line_text_column_width_to_position { params { diff --git a/elm_code/src/lib/widget/elm_code_widget_private.h b/elm_code/src/lib/widget/elm_code_widget_private.h index fab43c2..b4e1900 100644 --- a/elm_code/src/lib/widget/elm_code_widget_private.h +++ b/elm_code/src/lib/widget/elm_code_widget_private.h @@ -35,7 +35,7 @@ typedef struct typedef struct { - const char *content; + char *content; unsigned int length; unsigned int start_line, start_col, end_line, end_col; @@ -48,6 +48,10 @@ void _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char void _elm_code_widget_newline(Elm_Code_Widget *widget); +void _elm_code_widget_backspace(Elm_Code_Widget *widget); + +void _elm_code_widget_delete(Elm_Code_Widget *widget); + void _elm_code_widget_tooltip_text_set(Evas_Object *widget, const char *text); void _elm_code_widget_tooltip_add(Evas_Object *widget); diff --git a/elm_code/src/lib/widget/elm_code_widget_selection.c b/elm_code/src/lib/widget/elm_code_widget_selection.c index 9a558ec..d4d2142 100644 --- a/elm_code/src/lib/widget/elm_code_widget_selection.c +++ b/elm_code/src/lib/widget/elm_code_widget_selection.c @@ -317,91 +317,26 @@ elm_code_widget_selection_select_word(Evas_Object *widget, unsigned int line, un elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos)); } -static char * -_elm_code_widget_selection_text_single_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd) -{ - Elm_Code_Line *line; - unsigned int start, end; - Elm_Code_Widget_Selection_Data *selection; - - selection = elm_code_widget_selection_normalized_get(widget); - line = elm_code_file_line_get(pd->code->file, selection->start_line); - start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col); - end = elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col + 1); - free(selection); - - return elm_code_line_text_substr(line, start, end - start); -} - -static char * -_elm_code_widget_selection_text_multi_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd) -{ - Elm_Code_Line *line; - char *first, *last, *ret, *ptr; - const char *newline; - short newline_len; - int ret_len; - unsigned int row, start, end; - Elm_Code_Widget_Selection_Data *selection; - - selection = elm_code_widget_selection_normalized_get(widget); - newline = elm_code_file_line_ending_chars_get(pd->code->file, &newline_len); - - line = elm_code_file_line_get(pd->code->file, selection->start_line); - start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col); - first = elm_code_line_text_substr(line, start, line->length - start + 1); - - line = elm_code_file_line_get(pd->code->file, selection->end_line); - end = elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col + 1); - last = elm_code_line_text_substr(line, 0, end); - - ret_len = strlen(first) + strlen(last) + newline_len; - - for (row = pd->selection->start_line + 1; row < selection->end_line; row++) - { - line = elm_code_file_line_get(pd->code->file, row); - ret_len += line->length + newline_len; - } - - ret = malloc(sizeof(char) * (ret_len + 1)); - snprintf(ret, strlen(first) + newline_len + 1, "%s%s", first, newline); - - ptr = ret; - ptr += strlen(first) + newline_len; - - for (row = selection->start_line + 1; row < selection->end_line; row++) - { - line = elm_code_file_line_get(pd->code->file, row); - if (line->modified) - snprintf(ptr, line->length + 1, "%s", line->modified); - else - snprintf(ptr, line->length + 1, "%s", line->content); - - snprintf(ptr + line->length, newline_len + 1, "%s", newline); - ptr += line->length + newline_len; - } - snprintf(ptr, strlen(last) + 1, "%s", last); - - free(selection); - free(first); - free(last); - return ret; -} - EAPI char * elm_code_widget_selection_text_get(Evas_Object *widget) { Elm_Code_Widget_Data *pd; + Elm_Code_Widget_Selection_Data *selection; + char *text; pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS); if (!pd->selection) return strdup(""); - if (pd->selection->start_line == pd->selection->end_line) - return _elm_code_widget_selection_text_single_get(widget, pd); - else - return _elm_code_widget_selection_text_multi_get(widget, pd); + selection = elm_code_widget_selection_normalized_get(widget); + + text = elm_code_widget_text_between_positions_get(widget, + selection->start_col, selection->start_line, + selection->end_col, selection->end_line); + + free(selection); + return text; } static void diff --git a/elm_code/src/lib/widget/elm_code_widget_text.c b/elm_code/src/lib/widget/elm_code_widget_text.c index 0ae2595..3689e82 100644 --- a/elm_code/src/lib/widget/elm_code_widget_text.c +++ b/elm_code/src/lib/widget/elm_code_widget_text.c @@ -34,6 +34,87 @@ _elm_code_widget_text_left_gutter_width_get(Eo *obj, Elm_Code_Widget_Data *pd) return width; } +static char * +_elm_code_widget_text_multi_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, + unsigned int start_col, unsigned int start_line, + unsigned int end_col, unsigned int end_line) +{ + Elm_Code_Line *line; + char *first, *last, *ret, *ptr; + const char *newline; + short newline_len; + int ret_len; + unsigned int row, start, end; + + newline = elm_code_file_line_ending_chars_get(pd->code->file, &newline_len); + + line = elm_code_file_line_get(pd->code->file, start_line); + start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col); + first = elm_code_line_text_substr(line, start, line->length - start + 1); + + line = elm_code_file_line_get(pd->code->file, end_line); + end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1); + last = elm_code_line_text_substr(line, 0, end); + + ret_len = strlen(first) + strlen(last) + newline_len; + + for (row = pd->selection->start_line + 1; row < end_line; row++) + { + line = elm_code_file_line_get(pd->code->file, row); + ret_len += line->length + newline_len; + } + + ret = malloc(sizeof(char) * (ret_len + 1)); + + snprintf(ret, strlen(first) + newline_len + 1, "%s%s", first, newline); + + ptr = ret; + ptr += strlen(first) + newline_len; + + for (row = start_line + 1; row < end_line; row++) + { + line = elm_code_file_line_get(pd->code->file, row); + if (line->modified) + snprintf(ptr, line->length + 1, "%s", line->modified); + else + snprintf(ptr, line->length + 1, "%s", line->content); + + snprintf(ptr + line->length, newline_len + 1, "%s", newline); + ptr += line->length + newline_len; + } + snprintf(ptr, strlen(last) + 1, "%s", last); + + free(first); + free(last); + return ret; +} + +static char * +_elm_code_widget_text_single_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, + unsigned int start_col, unsigned int start_line, + unsigned int end_col) +{ + Elm_Code_Line *line; + unsigned int start, end; + + line = elm_code_file_line_get(pd->code->file, start_line); + start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col); + end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1); + + return elm_code_line_text_substr(line, start, end - start); +} + +static char * +_elm_code_widget_text_between_positions_get(Eo *widget, Elm_Code_Widget_Data *pd, + unsigned int start_col, unsigned int start_line, + unsigned int end_col, unsigned int end_line) +{ + if (start_line == end_line) + return _elm_code_widget_text_single_get(widget, pd, start_col, start_line, end_col); + else + return _elm_code_widget_text_multi_get(widget, pd, start_col, start_line, end_col, end_line); +} + static unsigned int _elm_code_widget_line_text_column_width_to_position(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int position) { diff --git a/elm_code/src/lib/widget/elm_code_widget_undo.c b/elm_code/src/lib/widget/elm_code_widget_undo.c index d51cc5f..6511be7 100644 --- a/elm_code/src/lib/widget/elm_code_widget_undo.c +++ b/elm_code/src/lib/widget/elm_code_widget_undo.c @@ -6,6 +6,19 @@ #include "elm_code_widget_private.h" +Elm_Code_Widget_Change_Info * +_elm_code_widget_undo_info_copy(Elm_Code_Widget_Change_Info *info) +{ + Elm_Code_Widget_Change_Info *copy; + + copy = calloc(1, sizeof(*info)); + memcpy(copy, info, sizeof(*info)); + copy->content = malloc(sizeof(char) * (info->length + 1)); + strncpy(copy->content, info->content, info->length); + + return copy; +} + void _elm_code_widget_undo_change_add(Evas_Object *widget, Elm_Code_Widget_Change_Info *info) @@ -13,9 +26,7 @@ _elm_code_widget_undo_change_add(Evas_Object *widget, Elm_Code_Widget_Data *pd; Elm_Code_Widget_Change_Info *info_copy; - info_copy = calloc(1, sizeof(*info)); - memcpy(info_copy, info, sizeof(*info)); - + info_copy = _elm_code_widget_undo_info_copy(info); pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS); pd->undo_stack_ptr = eina_list_prepend(pd->undo_stack_ptr, info_copy); @@ -26,6 +37,10 @@ static void _elm_code_widget_undo_change(Evas_Object *widget, Elm_Code_Widget_Change_Info *info) { + Elm_Code_Line *line; + Elm_Code_Widget_Data *pd; + unsigned int position; + if (info->insert) { elm_code_widget_selection_start(widget, info->start_line, info->start_col); @@ -34,6 +49,12 @@ _elm_code_widget_undo_change(Evas_Object *widget, } else { + pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS); + line = elm_code_file_line_get(pd->code->file, info->start_line); + position = elm_code_widget_line_text_position_for_column_get(widget, line, info->start_col); + + elm_code_line_text_insert(line, position, info->content, info->length); + elm_code_widget_cursor_position_set(widget, info->end_col, info->end_line); } } diff --git a/elm_code/src/tests/widget/elm_code_test_widget_undo.c b/elm_code/src/tests/widget/elm_code_test_widget_undo.c index f6c13f0..6f20909 100644 --- a/elm_code/src/tests/widget/elm_code_test_widget_undo.c +++ b/elm_code/src/tests/widget/elm_code_test_widget_undo.c @@ -93,9 +93,55 @@ START_TEST (elm_code_test_widget_undo_newline) } END_TEST +START_TEST (elm_code_test_widget_undo_delete) +{ + Elm_Code *code; + Elm_Code_File *file; + Elm_Code_Line *line; + Elm_Code_Widget *widget; + Evas_Object *win; + unsigned int length; + const char *content; + + elm_init(1, NULL); + code = elm_code_create(); + file = elm_code_file_new(code); + elm_code_file_line_append(file, "test", 4, NULL); + + win = elm_win_add(NULL, "entry", ELM_WIN_BASIC); + widget = elm_code_widget_add(win, code); + + elm_code_widget_cursor_position_set(widget, 4, 1); + _elm_code_widget_backspace(widget); + + line = elm_code_file_line_get(file, 1); + content = elm_code_line_text_get(line, &length); + ck_assert_strn_eq("tet", content, length); + + elm_code_widget_undo(widget); + content = elm_code_line_text_get(line, &length); + ck_assert_strn_eq("test", content, length); + + elm_code_widget_cursor_position_set(widget, 2, 1); + _elm_code_widget_delete(widget); + + line = elm_code_file_line_get(file, 1); + content = elm_code_line_text_get(line, &length); + ck_assert_strn_eq("tst", content, length); + + elm_code_widget_undo(widget); + content = elm_code_line_text_get(line, &length); + ck_assert_strn_eq("test", content, length); + + elm_code_free(code); + elm_shutdown(); +} +END_TEST + void elm_code_test_widget_undo(TCase *tc) { tcase_add_test(tc, elm_code_test_widget_undo_text_insert); tcase_add_test(tc, elm_code_test_widget_undo_newline); + tcase_add_test(tc, elm_code_test_widget_undo_delete); }