[editor] add delete and backspace undo hooks

This commit is contained in:
Andy Williams 2016-02-21 23:46:10 +00:00
parent 5dc607ecea
commit 5b1a45cf45
8 changed files with 202 additions and 94 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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);
}
}

View File

@ -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);
}