forked from enlightenment/edi
[editor] add delete and backspace undo hooks
This commit is contained in:
parent
5dc607ecea
commit
5b1a45cf45
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue