forked from enlightenment/edi
[editor] begin re-adding the undo feature.
This is provided completely by elm_code so can be reused in other editors. For now this is just text/newline insertion... Still have to add undo for deletion and selection
This commit is contained in:
parent
89cbb9daa0
commit
acd7b54473
|
@ -1,6 +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)
|
||||
|
||||
2016-02-11 ajwillia.ms (Andy Williams)
|
||||
|
||||
|
|
1
NEWS
1
NEWS
|
@ -8,6 +8,7 @@ Features:
|
|||
* Update to EFL 1.17 release for better toolbar handling
|
||||
* Add file filtering for hunting down files in the list
|
||||
* Double click to select word, triple click to select line
|
||||
* Return of the undo feature!
|
||||
|
||||
Bug fixes:
|
||||
|
||||
|
|
|
@ -987,11 +987,41 @@ _elm_code_widget_delete_selection(Elm_Code_Widget *widget)
|
|||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
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_Info *info;
|
||||
|
||||
info = calloc(1, sizeof(*info));
|
||||
info->insert = EINA_TRUE;
|
||||
|
||||
info->start_col = start_col;
|
||||
info->start_line = start_line;
|
||||
info->end_col = end_col;
|
||||
info->end_line = end_line;
|
||||
|
||||
info->content = malloc((length + 1) * sizeof(char));
|
||||
strncpy((char *)info->content, text, length + 1);
|
||||
info->length = length;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
_elm_code_widget_change_free(Elm_Code_Widget_Change_Info *info)
|
||||
{
|
||||
free((char *)info->content);
|
||||
free(info);
|
||||
}
|
||||
|
||||
void
|
||||
_elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char *text, int length)
|
||||
{
|
||||
Elm_Code *code;
|
||||
Elm_Code_Line *line;
|
||||
Elm_Code_Widget_Change_Info *change;
|
||||
unsigned int row, col, position, col_width;
|
||||
|
||||
_elm_code_widget_delete_selection(widget);
|
||||
|
@ -1013,15 +1043,19 @@ _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char *text
|
|||
|
||||
eo_do(widget,
|
||||
elm_obj_code_widget_cursor_position_set(col + col_width, 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_insert(col, row, col + col_width - 1, row, text, length);
|
||||
_elm_code_widget_undo_change_add(widget, change);
|
||||
_elm_code_widget_change_free(change);
|
||||
}
|
||||
|
||||
static void
|
||||
_elm_code_widget_tab_at_cursor_insert(Elm_Code_Widget *widget)
|
||||
{
|
||||
Elm_Code_Widget_Data *pd;
|
||||
unsigned int col, row;
|
||||
Elm_Code_Widget_Change_Info *change;
|
||||
unsigned int col, row, rem;
|
||||
|
||||
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
|
||||
if (!pd->tab_inserts_spaces)
|
||||
|
@ -1032,21 +1066,26 @@ _elm_code_widget_tab_at_cursor_insert(Elm_Code_Widget *widget)
|
|||
|
||||
eo_do(widget,
|
||||
elm_obj_code_widget_cursor_position_get(&col, &row));
|
||||
col = (col - 1) % pd->tabstop;
|
||||
rem = (col - 1) % pd->tabstop;
|
||||
|
||||
while (col < pd->tabstop)
|
||||
while (rem < pd->tabstop)
|
||||
{
|
||||
_elm_code_widget_text_at_cursor_insert(widget, " ", 1);
|
||||
col++;
|
||||
rem++;
|
||||
}
|
||||
|
||||
change = _elm_code_widget_change_create_insert(col, row, (col - 1) % pd ->tabstop, row, "\t", 1);
|
||||
_elm_code_widget_undo_change_add(widget, change);
|
||||
_elm_code_widget_change_free(change);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
_elm_code_widget_newline(Elm_Code_Widget *widget)
|
||||
{
|
||||
Elm_Code *code;
|
||||
Elm_Code_Line *line;
|
||||
unsigned int row, col, position, oldlen, leading;
|
||||
Elm_Code_Widget_Change_Info *change;
|
||||
unsigned int row, col, position, oldlen, leading, width, indent;
|
||||
char *oldtext;
|
||||
|
||||
_elm_code_widget_delete_selection(widget);
|
||||
|
@ -1065,6 +1104,7 @@ _elm_code_widget_newline(Elm_Code_Widget *widget)
|
|||
|
||||
position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
|
||||
elm_code_line_split_at(line, position);
|
||||
width = elm_code_widget_line_text_column_width_get(widget, line);
|
||||
|
||||
line = elm_code_file_line_get(code->file, row + 1);
|
||||
leading = elm_code_text_leading_whitespace_length(oldtext, oldlen);
|
||||
|
@ -1072,11 +1112,14 @@ _elm_code_widget_newline(Elm_Code_Widget *widget)
|
|||
elm_code_line_text_insert(line, 0, oldtext, leading);
|
||||
free(oldtext);
|
||||
|
||||
indent = elm_obj_code_widget_line_text_column_width_to_position(line, leading);
|
||||
eo_do(widget,
|
||||
elm_obj_code_widget_cursor_position_set(
|
||||
elm_obj_code_widget_line_text_column_width_to_position(line, leading), row + 1),
|
||||
// TODO construct and pass a change object
|
||||
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);
|
||||
_elm_code_widget_undo_change_add(widget, change);
|
||||
_elm_code_widget_change_free(change);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1198,9 +1241,11 @@ _elm_code_widget_control_key_down_cb(Elm_Code_Widget *widget, const char *key)
|
|||
elm_code_widget_selection_paste(widget);
|
||||
else if (!strcmp("x", key))
|
||||
elm_code_widget_selection_cut(widget);
|
||||
else if (!strcmp("z", key))
|
||||
elm_code_widget_undo(widget);
|
||||
|
||||
eo_do(widget,
|
||||
// TODO construct and pass a change object
|
||||
// TODO construct and pass a change object for cut and paste
|
||||
eo_event_callback_call(ELM_CODE_WIDGET_EVENT_CHANGED_USER, NULL));
|
||||
}
|
||||
|
||||
|
@ -1638,4 +1683,5 @@ _elm_code_widget_evas_object_smart_add(Eo *obj, Elm_Code_Widget_Data *pd)
|
|||
}
|
||||
|
||||
#include "elm_code_widget_text.c"
|
||||
#include "elm_code_widget_undo.c"
|
||||
#include "elm_code_widget.eo.c"
|
||||
|
|
|
@ -202,6 +202,8 @@ class Elm.Code_Widget (Elm.Layout, Elm.Interface_Atspi_Text)
|
|||
}
|
||||
return: uint;
|
||||
}
|
||||
undo {
|
||||
}
|
||||
}
|
||||
implements {
|
||||
class.constructor;
|
||||
|
|
|
@ -27,14 +27,33 @@ typedef struct
|
|||
|
||||
Elm_Code_Widget_Selection_Data *selection;
|
||||
Evas_Object *tooltip;
|
||||
|
||||
/* Undo stack */
|
||||
Eina_List *undo_stack;
|
||||
Eina_List *undo_stack_ptr;
|
||||
} Elm_Code_Widget_Data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *content;
|
||||
unsigned int length;
|
||||
unsigned int start_line, start_col, end_line, end_col;
|
||||
|
||||
Eina_Bool insert : 1; /**< True if the change is an insertion */
|
||||
} Elm_Code_Widget_Change_Info;
|
||||
|
||||
/* Private widget methods */
|
||||
|
||||
void _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, const char *text, int length);
|
||||
|
||||
void _elm_code_widget_newline(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);
|
||||
|
||||
EAPI Elm_Code_Widget_Selection_Data *elm_code_widget_selection_normalized_get(Evas_Object *widget);
|
||||
|
||||
void _elm_code_widget_undo_change_add(Evas_Object *widget, Elm_Code_Widget_Change_Info *info);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#ifdef HAVE_CONFIG
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "Elm_Code.h"
|
||||
|
||||
#include "elm_code_widget_private.h"
|
||||
|
||||
void
|
||||
_elm_code_widget_undo_change_add(Evas_Object *widget,
|
||||
Elm_Code_Widget_Change_Info *info)
|
||||
{
|
||||
Elm_Code_Widget_Data *pd;
|
||||
Elm_Code_Widget_Change_Info *info_copy;
|
||||
|
||||
info_copy = calloc(1, sizeof(*info));
|
||||
memcpy(info_copy, info, sizeof(*info));
|
||||
|
||||
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
|
||||
|
||||
pd->undo_stack_ptr = eina_list_prepend(pd->undo_stack_ptr, info_copy);
|
||||
pd->undo_stack = pd->undo_stack_ptr;
|
||||
}
|
||||
|
||||
static void
|
||||
_elm_code_widget_undo_change(Evas_Object *widget,
|
||||
Elm_Code_Widget_Change_Info *info)
|
||||
{
|
||||
if (info->insert)
|
||||
{
|
||||
elm_code_widget_selection_start(widget, info->start_line, info->start_col);
|
||||
elm_code_widget_selection_end(widget, info->end_line, info->end_col);
|
||||
elm_code_widget_selection_delete(widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_elm_code_widget_undo(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
|
||||
{
|
||||
Elm_Code_Widget_Change_Info *info;
|
||||
|
||||
if (!pd->undo_stack_ptr)
|
||||
return;
|
||||
|
||||
info = eina_list_data_get(pd->undo_stack_ptr);
|
||||
_elm_code_widget_undo_change(obj, info);
|
||||
|
||||
if (eina_list_next(pd->undo_stack_ptr))
|
||||
pd->undo_stack_ptr = eina_list_next(pd->undo_stack_ptr);
|
||||
else
|
||||
pd->undo_stack_ptr = NULL;
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ elm_code_test_text.c \
|
|||
widget/elm_code_test_widget.c \
|
||||
widget/elm_code_test_widget_text.c \
|
||||
widget/elm_code_test_widget_selection.c \
|
||||
widget/elm_code_test_widget_undo.c \
|
||||
elm_code_suite.c
|
||||
|
||||
elm_code_suite_CPPFLAGS = \
|
||||
|
|
|
@ -21,6 +21,7 @@ static const struct {
|
|||
{ "widget", elm_code_test_widget },
|
||||
{ "widget_text", elm_code_test_widget_text },
|
||||
{ "widget_selection", elm_code_test_widget_selection },
|
||||
{ "widget_undo", elm_code_test_widget_undo },
|
||||
};
|
||||
|
||||
START_TEST(elm_code_initialization)
|
||||
|
|
|
@ -26,5 +26,6 @@ void elm_code_test_text(TCase *tc);
|
|||
void elm_code_test_widget(TCase *tc);
|
||||
void elm_code_test_widget_text(TCase *tc);
|
||||
void elm_code_test_widget_selection(TCase *tc);
|
||||
void elm_code_test_widget_undo(TCase *tc);
|
||||
|
||||
#endif /* _EDLM_CODE_SUITE_H */
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "elm_code_suite.h"
|
||||
|
||||
#include "elm_code_widget_private.h"
|
||||
|
||||
START_TEST (elm_code_test_widget_undo_text_insert)
|
||||
{
|
||||
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_text_at_cursor_insert(widget, "a", 1);
|
||||
line = elm_code_file_line_get(file, 1);
|
||||
content = elm_code_line_text_get(line, &length);
|
||||
ck_assert_strn_eq("atest", 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, 3, 1);
|
||||
_elm_code_widget_text_at_cursor_insert(widget, "r", 1);
|
||||
content = elm_code_line_text_get(line, &length);
|
||||
ck_assert_strn_eq("terst", 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, 4, 1);
|
||||
_elm_code_widget_text_at_cursor_insert(widget, "\t", 1);
|
||||
content = elm_code_line_text_get(line, &length);
|
||||
ck_assert_strn_eq("tes\tt", 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
|
||||
|
||||
START_TEST (elm_code_test_widget_undo_newline)
|
||||
{
|
||||
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, 5, 1);
|
||||
_elm_code_widget_newline(widget);
|
||||
ck_assert_int_eq(2, elm_code_file_lines_get(file));
|
||||
line = elm_code_file_line_get(file, 1);
|
||||
content = elm_code_line_text_get(line, &length);
|
||||
ck_assert_strn_eq("test", content, 1);
|
||||
|
||||
elm_code_widget_undo(widget);
|
||||
|
||||
ck_assert_int_eq(1, elm_code_file_lines_get(file));
|
||||
line = elm_code_file_line_get(file, 1);
|
||||
content = elm_code_line_text_get(line, &length);
|
||||
ck_assert_strn_eq("test", content, 4);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -52,86 +52,13 @@ _edi_editor_autosave_cb(void *data)
|
|||
return ECORE_CALLBACK_CANCEL;
|
||||
}
|
||||
|
||||
static void
|
||||
_undo_do(Edi_Editor *editor, Elm_Entry_Change_Info *inf)
|
||||
{
|
||||
if (inf->insert)
|
||||
{
|
||||
const Evas_Object *tb = elm_entry_textblock_get(editor->entry);
|
||||
Evas_Textblock_Cursor *mcur, *end;
|
||||
mcur = (Evas_Textblock_Cursor *) evas_object_textblock_cursor_get(tb);
|
||||
end = evas_object_textblock_cursor_new(tb);
|
||||
|
||||
if (inf->insert)
|
||||
{
|
||||
elm_entry_cursor_pos_set(editor->entry, inf->change.insert.pos);
|
||||
evas_textblock_cursor_pos_set(end, inf->change.insert.pos +
|
||||
inf->change.insert.plain_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
elm_entry_cursor_pos_set(editor->entry, inf->change.del.start);
|
||||
evas_textblock_cursor_pos_set(end, inf->change.del.end);
|
||||
}
|
||||
|
||||
evas_textblock_cursor_range_delete(mcur, end);
|
||||
evas_textblock_cursor_free(end);
|
||||
elm_entry_calc_force(editor->entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inf->insert)
|
||||
{
|
||||
elm_entry_cursor_pos_set(editor->entry, inf->change.insert.pos);
|
||||
elm_entry_entry_insert(editor->entry, inf->change.insert.content);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t start;
|
||||
start = (inf->change.del.start < inf->change.del.end) ?
|
||||
inf->change.del.start : inf->change.del.end;
|
||||
|
||||
elm_entry_cursor_pos_set(editor->entry, start);
|
||||
elm_entry_entry_insert(editor->entry, inf->change.insert.content);
|
||||
elm_entry_cursor_pos_set(editor->entry, inf->change.del.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_undo_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
Elm_Entry_Change_Info *change;
|
||||
Edi_Editor *editor = data;
|
||||
|
||||
if (!eina_list_next(editor->undo_stack))
|
||||
return;
|
||||
|
||||
change = eina_list_data_get(editor->undo_stack);
|
||||
_undo_do(editor, change);
|
||||
editor->undo_stack = eina_list_next(editor->undo_stack);
|
||||
}
|
||||
|
||||
static void
|
||||
_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
|
||||
{
|
||||
Elm_Entry_Change_Info *change;
|
||||
Edi_Editor *editor = data;
|
||||
/*
|
||||
TODO move this code into elm_code for undo/redo
|
||||
change = calloc(1, sizeof(*change));
|
||||
memcpy(change, event_info, sizeof(*change));
|
||||
if (change->insert)
|
||||
{
|
||||
eina_stringshare_ref(change->change.insert.content);
|
||||
}
|
||||
else
|
||||
{
|
||||
eina_stringshare_ref(change->change.del.content);
|
||||
}
|
||||
|
||||
editor->undo_stack = eina_list_prepend(editor->undo_stack, change);
|
||||
*/
|
||||
editor->modified = EINA_TRUE;
|
||||
|
||||
if (editor->save_timer)
|
||||
|
@ -184,10 +111,6 @@ _smart_cb_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
|
|||
{
|
||||
edi_mainview_goto_popup_show();
|
||||
}
|
||||
else if (!strcmp(ev->key, "z"))
|
||||
{
|
||||
_undo_cb(editor, obj, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue