efl/src/lib/elementary/elm_code_widget_selection.c

519 lines
14 KiB
C

#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include "Elementary.h"
#include "elm_code_widget_private.h"
static char _breaking_chars[] = " \t,.?!;:*&()[]{}'\"";
static Elm_Code_Widget_Selection_Data *
_elm_code_widget_selection_new()
{
Elm_Code_Widget_Selection_Data *data;
data = calloc(1, sizeof(Elm_Code_Widget_Selection_Data));
return data;
}
static void
_elm_code_widget_selection_limit(Evas_Object *widget EINA_UNUSED, Elm_Code_Widget_Data *pd,
unsigned int *row, unsigned int *col)
{
Elm_Code_Line *line;
Elm_Code_File *file;
unsigned int width;
file = pd->code->file;
if (*row > elm_code_file_lines_get(file))
*row = elm_code_file_lines_get(file);
line = elm_code_file_line_get(file, *row);
width = elm_code_widget_line_text_column_width_get(widget, line);
if (*col > width + 1)
*col = width + 1;
}
EAPI void
elm_code_widget_selection_start(Evas_Object *widget,
unsigned int line, unsigned int col)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Widget_Selection_Data *selection;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
_elm_code_widget_selection_limit(widget, pd, &line, &col);
if (!pd->selection)
{
selection = _elm_code_widget_selection_new();
selection->end_line = line;
selection->end_col = col;
pd->selection = selection;
}
_elm_code_widget_selection_in_progress_set(widget, EINA_TRUE);
pd->selection->start_line = line;
pd->selection->start_col = col;
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_START, widget);
}
EAPI void
elm_code_widget_selection_end(Evas_Object *widget,
unsigned int line, unsigned int col)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Widget_Selection_Data *selection;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (pd->selection && (pd->selection->end_line == line) &&
(pd->selection->end_col == col)) return;
_elm_code_widget_selection_limit(widget, pd, &line, &col);
if (!pd->selection)
{
selection = _elm_code_widget_selection_new();
selection->start_line = line;
selection->start_col = col;
pd->selection = selection;
}
pd->selection->end_line = line;
pd->selection->end_col = col;
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CHANGED, widget);
}
EAPI void
elm_code_widget_selection_select_all(Evas_Object *widget)
{
Elm_Code_Line *last_line;
unsigned int last_length, last_col;
Elm_Code_Widget_Data *pd;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
elm_code_widget_selection_start(widget, 1, 1);
int maxrow = elm_code_file_lines_get(pd->code->file);
last_line = elm_code_file_line_get(pd->code->file, elm_code_file_lines_get(pd->code->file));
elm_code_line_text_get(last_line, &last_length);
last_col = elm_code_widget_line_text_column_width_to_position(widget, last_line, last_length);
elm_code_widget_selection_end(widget, maxrow, last_col);
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CHANGED, widget);
}
EAPI Elm_Code_Widget_Selection_Data *
elm_code_widget_selection_normalized_get(Evas_Object *widget)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Widget_Selection_Data *selection;
Eina_Bool reverse;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
selection = _elm_code_widget_selection_new();
if (!pd->selection)
{
selection->start_line = selection->end_line = 1;
selection->start_col = selection->end_col = 1;
return selection;
}
if (pd->selection->start_line == pd->selection->end_line)
reverse = pd->selection->start_col > pd->selection->end_col;
else
reverse = pd->selection->start_line > pd->selection->end_line;
if (reverse)
{
selection->start_line = pd->selection->end_line;
selection->start_col = pd->selection->end_col;
selection->end_line = pd->selection->start_line;
selection->end_col = pd->selection->start_col;
}
else
{
selection->start_line = pd->selection->start_line;
selection->start_col = pd->selection->start_col;
selection->end_line = pd->selection->end_line;
selection->end_col = pd->selection->end_col;
}
return selection;
}
EAPI void
elm_code_widget_selection_clear(Evas_Object *widget)
{
Elm_Code_Widget_Data *pd;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (!pd->selection)
return;
free(pd->selection);
pd->selection = NULL;
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CLEARED, widget);
}
static void
_elm_code_widget_selection_delete_single(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
{
Elm_Code_Line *line;
const char *old;
unsigned int old_length, start, end, length;
char *content;
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);
old = elm_code_line_text_get(line, &old_length);
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);
if (end == line->length)
{
length = line->length - (end - start);
content = malloc(sizeof(char) * length);
strncpy(content, old, start);
}
else
{
length = line->length - (end - start + 1);
content = malloc(sizeof(char) * length);
strncpy(content, old, start);
strncpy(content + start, old + end + 1,
old_length - (end + 1));
}
elm_code_line_text_set(line, content, length);
free(content);
free(selection);
}
static void
my_string_copy_truncate(char *dest, const char *src, size_t len)
{
char *p;
for (p = dest; len > 0; p++, src++, len--)
{
*p = *src;
if (*src == 0) break;
}
}
static void
_elm_code_widget_selection_delete_multi(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
{
Elm_Code_Line *line;
const char *first, *last;
unsigned int last_length, start, length, end, i;
char *content;
Elm_Code_Widget_Selection_Data *selection;
if (pd->selection->end_line == pd->selection->start_line)
return;
selection = elm_code_widget_selection_normalized_get(widget);
line = elm_code_file_line_get(pd->code->file, selection->start_line);
first = elm_code_line_text_get(line, NULL);
start = elm_code_widget_line_text_position_for_column_get(widget, line, selection->start_col);
line = elm_code_file_line_get(pd->code->file, selection->end_line);
last = elm_code_line_text_get(line, &last_length);
end = (int)elm_code_widget_line_text_position_for_column_get(widget, line, selection->end_col);
if (end == line->length)
length = start + last_length - end;
else
length = start + last_length - (end + 1);
content = malloc(sizeof(char) * length);
strncpy(content, first, start);
if (last_length > 0)
{
if (end == last_length)
my_string_copy_truncate(content + start, last + end,
last_length - end);
else
my_string_copy_truncate(content + start, last + end + 1,
last_length - (end + 1));
}
for (i = line->number; i > selection->start_line; i--)
elm_code_file_line_remove(pd->code->file, i);
line = elm_code_file_line_get(pd->code->file, selection->start_line);
elm_code_line_text_set(line, content, length);
free(content);
free(selection);
}
void
_elm_code_widget_selection_delete_do(Evas_Object *widget, Eina_Bool undo)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Widget_Selection_Data *selection;
unsigned int row, col;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (!pd->selection)
return;
if (undo)
_elm_code_widget_change_selection_add(widget);
selection = elm_code_widget_selection_normalized_get(widget);
row = selection->start_line;
col = selection->start_col;
if (selection->start_line == selection->end_line)
_elm_code_widget_selection_delete_single(widget, pd);
else
_elm_code_widget_selection_delete_multi(widget, pd);
free(pd->selection);
pd->selection = NULL;
free(selection);
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CLEARED, widget);
elm_code_widget_cursor_position_set(widget, row, col);
}
EAPI void
elm_code_widget_selection_delete(Evas_Object *widget)
{
_elm_code_widget_selection_delete_do(widget, EINA_TRUE);
}
void
_elm_code_widget_selection_delete_no_undo(Evas_Object *widget)
{
_elm_code_widget_selection_delete_do(widget, EINA_FALSE);
}
EAPI void
elm_code_widget_selection_select_line(Evas_Object *widget, unsigned int line)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Line *lineobj;
unsigned int col;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
lineobj = elm_code_file_line_get(pd->code->file, line);
if (!lineobj)
return;
elm_code_widget_selection_start(widget, line, 1);
col = elm_code_widget_line_text_column_width_to_position(widget, lineobj, lineobj->length);
elm_code_widget_selection_end(widget, line, col);
}
static Eina_Bool
_elm_code_widget_selection_char_breaks(char chr)
{
unsigned int i;
if (chr == 0)
return EINA_TRUE;
for (i = 0; i < sizeof(_breaking_chars); i++)
if (chr == _breaking_chars[i])
return EINA_TRUE;
return EINA_FALSE;
}
EAPI void
elm_code_widget_selection_select_word(Evas_Object *widget, unsigned int line, unsigned int col)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Line *lineobj;
unsigned int colpos, length, pos;
const char *content;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
lineobj = elm_code_file_line_get(pd->code->file, line);
content = elm_code_line_text_get(lineobj, &length);
_elm_code_widget_selection_limit(widget, pd, &line, &col);
colpos = elm_code_widget_line_text_position_for_column_get(widget, lineobj, col);
pos = colpos;
while (pos > 0)
{
if (_elm_code_widget_selection_char_breaks(content[pos - 1]))
break;
pos--;
}
elm_code_widget_selection_start(widget, line,
elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos));
pos = colpos;
while (pos < length - 1)
{
if (_elm_code_widget_selection_char_breaks(content[pos + 1]))
break;
pos++;
}
elm_code_widget_selection_end(widget, line,
elm_code_widget_line_text_column_width_to_position(widget, lineobj, pos));
}
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 = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (!pd->selection)
return strdup("");
selection = elm_code_widget_selection_normalized_get(widget);
text = elm_code_widget_text_between_positions_get(widget,
selection->start_line, selection->start_col,
selection->end_line, selection->end_col);
free(selection);
return text;
}
static void
_selection_loss_cb(void *data EINA_UNUSED, Elm_Sel_Type selection EINA_UNUSED)
{
/* Elm_Code_Widget *widget; */
/* widget = (Elm_Code_Widget *)data; */
// TODO we need to know whih selection we are clearing!
// elm_code_widget_selection_clear(widget);
}
EAPI void
elm_code_widget_selection_cut(Evas_Object *widget)
{
char *text;
if (elm_code_widget_selection_is_empty(widget))
return;
text = elm_code_widget_selection_text_get(widget);
elm_cnp_selection_set(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, text, strlen(text));
elm_cnp_selection_loss_callback_set(widget, ELM_SEL_TYPE_CLIPBOARD, _selection_loss_cb, widget);
free(text);
elm_code_widget_selection_delete(widget);
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CUT, widget);
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
}
EAPI void
elm_code_widget_selection_copy(Evas_Object *widget)
{
char *text;
if (elm_code_widget_selection_is_empty(widget))
return;
text = elm_code_widget_selection_text_get(widget);
elm_cnp_selection_set(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, text, strlen(text));
elm_cnp_selection_loss_callback_set(widget, ELM_SEL_TYPE_CLIPBOARD, _selection_loss_cb, widget);
free(text);
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_COPY, widget);
}
static Eina_Bool
_selection_paste_cb(void *data, Evas_Object *obj EINA_UNUSED, Elm_Selection_Data *ev)
{
Elm_Code_Widget *widget;
widget = (Elm_Code_Widget *)data;
elm_code_widget_text_at_cursor_insert(widget, ev->data);
efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_PASTE, widget);
return EINA_TRUE;
}
EAPI void
elm_code_widget_selection_paste(Evas_Object *widget)
{
elm_code_widget_selection_delete(widget);
elm_cnp_selection_get(widget, ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, _selection_paste_cb, widget);
}
EAPI Eina_Bool
elm_code_widget_selection_is_empty(Evas_Object *widget)
{
Elm_Code_Widget_Data *pd;
Elm_Code_Widget_Selection_Data *selection;
Eina_Bool ret = EINA_FALSE;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (!pd->selection)
return EINA_TRUE;
selection = elm_code_widget_selection_normalized_get(widget);
// check there is no space between start and end
if (selection->start_col == selection->end_col + 1 &&
selection->start_line == selection->end_line)
ret = EINA_TRUE;
free(selection);
return ret;
}
void
_elm_code_widget_selection_in_progress_set(Evas_Object *widget, Eina_Bool state)
{
Elm_Code_Widget_Data *pd;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (!pd || !pd->selection)
return;
pd->selection->in_progress = state;
}
void
_elm_code_widget_selection_type_set(Evas_Object *widget, Elm_Code_Widget_Selection_Type type)
{
Elm_Code_Widget_Data *pd;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (!pd || !pd->selection)
return;
pd->selection->type = type;
}