enlightenment/src/bin/e_editable.c

1354 lines
38 KiB
C

/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
#include "e.h"
#define E_EDITABLE_CURSOR_SHOW_DELAY 1.25
#define E_EDITABLE_CURSOR_HIDE_DELAY 0.25
#define E_EDITABLE_CURSOR_MARGIN 5
#define E_EDITABLE_BLOCK_SIZE 128
#define E_EDITABLE_SIZE_TO_ALLOC(length) \
(((length) + (E_EDITABLE_BLOCK_SIZE - 1)) / E_EDITABLE_BLOCK_SIZE) * E_EDITABLE_BLOCK_SIZE
typedef struct _E_Editable_Smart_Data E_Editable_Smart_Data;
struct _E_Editable_Smart_Data
{
Evas_Object *clip_object;
Evas_Object *event_object;
Evas_Object *text_object;
Evas_Object *cursor_object;
Evas_Object *selection_object;
Ecore_Timer *cursor_timer;
int cursor_pos;
int cursor_visible;
int selection_pos;
int selection_visible;
int selectable;
int password_mode;
char *text;
int char_length;
int unicode_length;
int allocated_length;
char *font;
int font_size;
Evas_Text_Style_Type font_style;
int cursor_width;
int selection_on_fg;
int average_char_w;
int average_char_h;
};
/* local subsystem functions */
static int _e_editable_text_insert(Evas_Object *editable, int pos, const char *text);
static int _e_editable_text_delete(Evas_Object *editable, int start, int end);
static void _e_editable_cursor_update(Evas_Object *editable);
static void _e_editable_selection_update(Evas_Object *editable);
static void _e_editable_text_update(Evas_Object *editable);
static void _e_editable_text_position_update(Evas_Object *editable, Evas_Coord real_w);
static int _e_editable_cursor_timer_cb(void *data);
static void _e_editable_smart_add(Evas_Object *object);
static void _e_editable_smart_del(Evas_Object *object);
static void _e_editable_smart_move(Evas_Object *object, Evas_Coord x, Evas_Coord y);
static void _e_editable_smart_resize(Evas_Object *object, Evas_Coord w, Evas_Coord h);
static void _e_editable_smart_show(Evas_Object *object);
static void _e_editable_smart_hide(Evas_Object *object);
static void _e_editable_color_set(Evas_Object *object, int r, int g, int b, int a);
static void _e_editable_clip_set(Evas_Object *object, Evas_Object *clip);
static void _e_editable_clip_unset(Evas_Object *object);
/* local subsystem globals */
static Evas_Smart *_e_editable_smart = NULL;
static int _e_editable_smart_use = 0;
/* externally accessible functions */
/**
* Creates a new editable object. An editable object is an evas smart object in
* which the user can type some single-line text, select it and delete it.
*
* @param evas The evas where to add the editable object
* @return Returns the new editable object
*/
EAPI Evas_Object *
e_editable_add(Evas *evas)
{
if (!_e_editable_smart)
{
_e_editable_smart = evas_smart_new("e_editable",
_e_editable_smart_add, /* add */
_e_editable_smart_del, /* del */
NULL, NULL, NULL, NULL, NULL, /* stacking */
_e_editable_smart_move, /* move */
_e_editable_smart_resize, /* resize */
_e_editable_smart_show, /* show */
_e_editable_smart_hide, /* hide */
_e_editable_color_set, /* color_set */
_e_editable_clip_set, /* clip_set */
_e_editable_clip_unset, /* clip_unset */
NULL); /* data*/
_e_editable_smart_use = 0;
}
_e_editable_smart_use++;
return evas_object_smart_add(evas, _e_editable_smart);
}
/**
* Sets the theme group to be used by the editable object.
* This function has to be called, or the cursor and the selection won't be
* visible.
*
* @param editable an editable object
* @param category the theme category to use for the editable object
* @param group the theme group to use for the editable object
*/
EAPI void
e_editable_theme_set(Evas_Object *editable, const char *category, const char *group)
{
E_Editable_Smart_Data *sd;
char *obj_group;
const char *data;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if ((!category) || (!group))
return;
/* Gets the theme for the cursor */
obj_group = malloc(strlen(group) + strlen("/cursor") + 1);
sprintf(obj_group, "%s/cursor", group);
e_theme_edje_object_set(sd->cursor_object, category, obj_group);
free(obj_group);
edje_object_size_min_get(sd->cursor_object, &sd->cursor_width, NULL);
if (sd->cursor_width < 1)
sd->cursor_width = 1;
/* Gets the theme for the selection */
obj_group = malloc(strlen(group) + strlen("/selection") + 1);
sprintf(obj_group, "%s/selection", group);
e_theme_edje_object_set(sd->selection_object, category, obj_group);
free(obj_group);
data = edje_object_data_get(sd->selection_object, "on_foreground");
if ((data) && (strcmp(data, "1") == 0))
{
sd->selection_on_fg = 1;
evas_object_stack_above(sd->selection_object, sd->text_object);
}
else
{
sd->selection_on_fg = 0;
evas_object_stack_below(sd->selection_object, sd->text_object);
}
/* TODO: font */
_e_editable_cursor_update(editable);
}
/**
* Sets whether or not the editable object is in password mode. In password
* mode, the editable object displays '*' instead of the characters
*
* @param editable an editable object
* @param password_mode 1 to turn on the password mode of the editable object,
* 0 to turn it off
*/
EAPI void
e_editable_password_set(Evas_Object *editable, int password_mode)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (sd->password_mode == password_mode)
return;
sd->password_mode = password_mode;
_e_editable_text_update(editable);
_e_editable_cursor_update(editable);
}
/**
* Gets whether or not the editable is in password mode
*
* @param editable an editable object
* @return Returns 1 if the editable object is in the password mode, 0 otherwise
*/
EAPI int
e_editable_password_get(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
return sd->password_mode;
}
/**
* Sets the text of the editable object
*
* @param editable an editable object
* @param text the text to set
*/
EAPI void
e_editable_text_set(Evas_Object *editable, const char *text)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
free(sd->text);
sd->text = NULL;
sd->char_length = 0;
sd->unicode_length = 0;
sd->allocated_length = -1;
if (_e_editable_text_insert(editable, 0, text) <= 0)
{
sd->text = malloc((E_EDITABLE_BLOCK_SIZE + 1) * sizeof(char));
sd->text[0] = '\0';
sd->char_length = 0;
sd->unicode_length = 0;
sd->allocated_length = E_EDITABLE_BLOCK_SIZE;
}
sd->cursor_pos = sd->unicode_length;
sd->selection_pos = sd->unicode_length;
_e_editable_cursor_update(editable);
}
/**
* Gets the entire text of the editable object
*
* @param editable an editable object
* @return Returns the entire text of the editable object
*/
EAPI const char *
e_editable_text_get(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return NULL;
return sd->text;
}
/**
* Gets a range of the text of the editable object, from position @a start to
* position @a end
*
* @param editable an editable object
* @param start the start position of the text range to get
* @param end the end position of the text range to get
* @return Returns the range of text. The returned string will have to be freed
*/
EAPI char *
e_editable_text_range_get(Evas_Object *editable, int start, int end)
{
E_Editable_Smart_Data *sd;
char *range;
int start_id, end_id;
int i;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return NULL;
start = E_CLAMP(start, 0, sd->unicode_length);
end = E_CLAMP(end, 0, sd->unicode_length);
if (end <= start)
return NULL;
start_id = 0;
end_id = 0;
for (i = 0; i < end; i++)
{
end_id = evas_string_char_next_get(sd->text, end_id, NULL);
if (i < start)
start_id = end_id;
}
if (end_id <= start_id)
return NULL;
range = malloc((end_id - start_id + 1) * sizeof(char));
strncpy(range, &sd->text[start_id], end_id - start_id);
range[end_id - start_id] = '\0';
return range;
}
/**
* Gets the unicode length of the text of the editable object. The unicode
* length is not always the length returned by strlen() since a UTF-8 char can
* take several bytes
*
* @param editable an editable object
* @return Returns the unicode length of the text of the editable object
*/
EAPI int
e_editable_text_length_get(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
return sd->unicode_length;
}
/**
* Inserts some text at the given position in the editable object
*
* @param editable the editable object in which the text should be inserted
* @param pos the position where to insert the text
* @param text the text to insert
* @return Returns 1 if the text has been modified, 0 otherwise
*/
EAPI int
e_editable_insert(Evas_Object *editable, int pos, const char *text)
{
E_Editable_Smart_Data *sd;
int unicode_length;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
unicode_length = _e_editable_text_insert(editable, pos, text);
if (unicode_length <= 0)
return 0;
if (sd->cursor_pos >= pos)
e_editable_cursor_pos_set(editable, sd->cursor_pos + unicode_length);
if (sd->selection_pos >= pos)
e_editable_selection_pos_set(editable, sd->selection_pos + unicode_length);
_e_editable_text_position_update(editable, -1);
return 1;
}
/**
* Deletes the text of the editable object, between position "start" and
* position "end"
*
* @param editable the editable object in which the text should be deleted
* @param start the position of the first char to delete
* @param end the position of the last char to delete
* @return Returns 1 if the text has been modified, 0 otherwise
*/
EAPI int
e_editable_delete(Evas_Object *editable, int start, int end)
{
E_Editable_Smart_Data *sd;
int unicode_length;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
unicode_length = _e_editable_text_delete(editable, start, end);
if (unicode_length <= 0)
return 0;
if (sd->cursor_pos > end)
e_editable_cursor_pos_set(editable, sd->cursor_pos - unicode_length);
else if (sd->cursor_pos > start)
e_editable_cursor_pos_set(editable, start);
if (sd->selection_pos > end)
e_editable_selection_pos_set(editable, sd->selection_pos - unicode_length);
else if (sd->selection_pos > start)
e_editable_selection_pos_set(editable, start);
_e_editable_text_position_update(editable, -1);
return 1;
}
/**
* Moves the cursor of the editable object to the given position
*
* @param editable an editable object
* @param pos the position where to move the cursor
*/
EAPI void
e_editable_cursor_pos_set(Evas_Object *editable, int pos)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
pos = E_CLAMP(pos, 0, sd->unicode_length);
if ((sd->cursor_pos == pos))
return;
sd->cursor_pos = pos;
_e_editable_cursor_update(editable);
}
/**
* Gets the position of the cursor of the editable object
*
* @param editable an editable object
* @return Returns the position of the cursor of the editable object
*/
EAPI int
e_editable_cursor_pos_get(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
return sd->cursor_pos;
}
/**
* Moves the cursor to the start of the editable object
*
* @param editable an editable object
*/
EAPI void
e_editable_cursor_move_to_start(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_cursor_pos_set(editable, 0);
}
/**
* Moves the cursor to the end of the editable object
*
* @param editable an editable object
*/
EAPI void
e_editable_cursor_move_to_end(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_cursor_pos_set(editable, sd->unicode_length);
}
/**
* Moves the cursor backward by one character offset
*
* @param editable an editable object
*/
EAPI void
e_editable_cursor_move_left(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_cursor_pos_set(editable, sd->cursor_pos - 1);
}
/**
* Moves the cursor forward by one character offset
*
* @param editable an editable object
*/
EAPI void
e_editable_cursor_move_right(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_cursor_pos_set(editable, sd->cursor_pos + 1);
}
/**
* Shows the cursor of the editable object
*
* @param editable the editable object whose cursor should be shown
*/
EAPI void
e_editable_cursor_show(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (sd->cursor_visible)
return;
sd->cursor_visible = 1;
if (evas_object_visible_get(editable))
{
evas_object_show(sd->cursor_object);
sd->cursor_timer = ecore_timer_add(E_EDITABLE_CURSOR_SHOW_DELAY,
_e_editable_cursor_timer_cb,
editable);
}
}
/**
* Hides the cursor of the editable object
*
* @param editable the editable object whose cursor should be hidden
*/
EAPI void
e_editable_cursor_hide(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (!sd->cursor_visible)
return;
sd->cursor_visible = 0;
if (evas_object_visible_get(editable))
{
evas_object_hide(sd->cursor_object);
ecore_timer_del(sd->cursor_timer);
sd->cursor_timer = NULL;
}
}
/**
* Moves the selection bound of the editable object to the given position
*
* @param editable an editable object
* @param pos the position where to move the selection bound
*/
EAPI void
e_editable_selection_pos_set(Evas_Object *editable, int pos)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
pos = E_CLAMP(pos, 0, sd->unicode_length);
if ((sd->selection_pos == pos))
return;
sd->selection_pos = pos;
_e_editable_selection_update(editable);
}
/**
* Gets the position of the selection bound of the editable object. If the
* editable object is not selectable, this function returns the position of the
* cursor instead.
*
* @param editable an editable object
* @return Returns the position of the selection bound of the editable object
*/
EAPI int
e_editable_selection_pos_get(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
return sd->selectable ? sd->selection_pos : sd->cursor_pos;
}
/**
* Moves the selection bound to the start of the editable object
*
* @param editable an editable object
*/
EAPI void
e_editable_selection_move_to_start(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_selection_pos_set(editable, 0);
}
/**
* Moves the selection bound to the end of the editable object
*
* @param editable an editable object
*/
EAPI void
e_editable_selection_move_to_end(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_selection_pos_set(editable, sd->unicode_length);
}
/**
* Moves the selection bound backward by one character offset
*
* @param editable an editable object
*/
EAPI void
e_editable_selection_move_left(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_selection_pos_set(editable, sd->selection_pos - 1);
}
/**
* Moves the selection bound forward by one character offset
*
* @param editable an editable object
*/
EAPI void
e_editable_selection_move_right(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_selection_pos_set(editable, sd->selection_pos + 1);
}
/**
* Selects all the text of the editable object. The selection bound will be
* moved to the start of the editable object and the cursor will be moved to
* the end
*
* @param editable an editable object
*/
EAPI void
e_editable_select_all(Evas_Object *editable)
{
if (!editable)
return;
e_editable_selection_move_to_start(editable);
e_editable_cursor_move_to_end(editable);
}
/**
* Unselects all the text of the editable object. The selection bound will be
* moved to the cursor position
*
* @param editable an editable object
*/
EAPI void
e_editable_unselect_all(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
e_editable_selection_pos_set(editable, sd->cursor_pos);
}
/**
* Shows the selection of the editable object. The editable object need to be
* selectable (see e_editable_selectable_set())
*
* @param editable an editable object
*/
EAPI void
e_editable_selection_show(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (sd->selection_visible)
return;
sd->selection_visible = 1;
if ((sd->selectable) && (evas_object_visible_get(editable)) &&
(sd->cursor_pos != sd->selection_pos))
evas_object_show(sd->selection_object);
}
/**
* Hides the selection of the editable object
*
* @param editable an editable object
*/
EAPI void
e_editable_selection_hide(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (!sd->selection_visible)
return;
sd->selection_visible = 0;
evas_object_hide(sd->selection_object);
}
/**
* Sets whether or not the editable object is selectable. If the editable object
* is not selectable, the selection rectangle won't be shown, and
* e_editable_selection_pos_get() will then return the position of the cursor
*
* @param editable an editable object
* @param selection 1 to make the editable object selectable, 0 otherwise
*/
EAPI void
e_editable_selectable_set(Evas_Object *editable, int selectable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (sd->selectable == selectable)
return;
sd->selectable = selectable;
if (sd->selectable)
e_editable_unselect_all(editable);
if ((sd->selectable) && (sd->selection_visible) &&
(evas_object_visible_get(editable)) &&
(sd->cursor_pos != sd->selection_pos))
evas_object_show(sd->selection_object);
else
evas_object_hide(sd->selection_object);
}
/**
* Gets whether or not the editable object is selectable
*
* @param editable an editable object
* @return Returns 1 if the editable object is selectable, 0 otherwise
*/
EAPI int
e_editable_selectable_get(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
return sd->selectable;
}
/**
* Gets the cursor position at the coords ( @a x, @a y ). It's used to know
* where to place the cursor or the selection bound on mouse evevents.
*
* @param editable an editable object
* @param x the x coord, relative to the editable object
* @param y the y coord, relative to the editable object
* @return Returns the position where to place the cursor according to the
* given coords, or -1 on failure
*/
EAPI int
e_editable_pos_get_from_coords(Evas_Object *editable, Evas_Coord x, Evas_Coord y)
{
E_Editable_Smart_Data *sd;
Evas_Coord ox, oy;
Evas_Coord tx, ty, tw, th;
Evas_Coord cx, cw;
Evas_Coord canvas_x, canvas_y;
int pos;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return -1;
evas_object_geometry_get(editable, &ox, &oy, NULL, NULL);
evas_object_geometry_get(sd->text_object, &tx, &ty, &tw, &th);
canvas_x = ox + x;
canvas_y = oy + y;
if ((canvas_y < ty) || (canvas_x < tx))
pos = 0;
else if ((canvas_y > (ty + th)) || (canvas_x > (tx + tw)))
pos = sd->unicode_length;
else
{
pos = evas_object_text_char_coords_get(sd->text_object,
canvas_x - tx, canvas_y - ty,
&cx, NULL, &cw, NULL);
if (pos >= 0)
{
if ((canvas_x - tx) > (cx + (cw / 2)))
pos++;
if (pos > sd->unicode_length)
pos = sd->unicode_length;
}
else
pos = -1;
}
return pos;
}
/**
* A utility function to get the average size of a character written inside
* the editable object
*
* @param editable an editable object
* @param w the location where to store the average width of a character
* @param h the location where to store the average height of a character
*/
EAPI void
e_editable_char_size_get(Evas_Object *editable, int *w, int *h)
{
int tw = 0, th = 0;
Evas *evas;
E_Editable_Smart_Data *sd;
Evas_Object *text_object;
char *text = "Tout est bon dans l'abricot sauf le noyau!"
"Wakey wakey! Eggs and Bakey!";
if (w) *w = 0;
if (h) *h = 0;
if ((!editable) || (!(evas = evas_object_evas_get(editable))))
return;
if ((!(sd = evas_object_smart_data_get(editable))) || (!sd->font))
return;
if ((sd->average_char_w <= 0) || (sd->average_char_h <= 0))
{
text_object = evas_object_text_add(evas);
evas_object_text_font_set(text_object, sd->font, sd->font_size);
evas_object_text_style_set(text_object, sd->font_style);
evas_object_text_text_set(text_object, text);
evas_object_geometry_get(text_object, NULL, NULL, &tw, &th);
evas_object_del(text_object);
sd->average_char_w = tw / strlen(text);
sd->average_char_h = th;
}
if (w) *w = sd->average_char_w;
if (h) *h = sd->average_char_h;
}
/* Private functions */
/* A utility function to insert some text inside the editable object.
* It doesn't update the position of the cursor, nor the selection... */
static int
_e_editable_text_insert(Evas_Object *editable, int pos, const char *text)
{
E_Editable_Smart_Data *sd;
int char_length, unicode_length, prev_length;
int index;
int i;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
if ((!text) || (*text == '\0'))
return 0;
if (pos < 0)
pos = 0;
else if (pos > sd->unicode_length)
pos = sd->unicode_length;
char_length = -1;
unicode_length = -1;
for (i = 0; i != char_length; i = evas_string_char_next_get(text, i, NULL))
{
char_length = i;
unicode_length++;
}
index = 0;
for (i = 0; i < pos; i++)
index = evas_string_char_next_get(sd->text, index, NULL);
if ((unicode_length <= 0) || (char_length <= 0))
if ((unicode_length <= 0) || (char_length <= 0))
return 0;
prev_length = sd->char_length;
sd->char_length += char_length;
sd->unicode_length += unicode_length;
if (sd->char_length > sd->allocated_length)
{
sd->text = realloc(sd->text,
E_EDITABLE_SIZE_TO_ALLOC(sd->char_length) + 1);
sd->allocated_length = E_EDITABLE_SIZE_TO_ALLOC(sd->char_length);
}
if (prev_length > index)
memmove(&sd->text[index + char_length], &sd->text[index], prev_length - index);
strncpy(&sd->text[index], text, char_length);
sd->text[sd->char_length] = '\0';
_e_editable_text_update(editable);
return unicode_length;
}
/* A utility function to delete a range of text from the editable object.
* It doesn't update the position of the cursor, nor the selection... */
static int
_e_editable_text_delete(Evas_Object *editable, int start, int end)
{
E_Editable_Smart_Data *sd;
int start_id, end_id;
int i;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return 0;
start = E_CLAMP(start, 0, sd->unicode_length);
end = E_CLAMP(end, 0, sd->unicode_length);
if (end <= start)
return 0;
start_id = 0;
end_id = 0;
for (i = 0; i < end; i++)
{
end_id = evas_string_char_next_get(sd->text, end_id, NULL);
if (i < start)
start_id = end_id;
}
if (end_id <= start_id)
return 0;
memmove(&sd->text[start_id], &sd->text[end_id], sd->char_length - end_id);
sd->char_length -= (end_id - start_id);
sd->unicode_length -= (end - start);
sd->text[sd->char_length] = '\0';
_e_editable_text_update(editable);
return end - start;
}
/* Updates the position of the cursor
* It also updates automatically the text position and the selection */
static void
_e_editable_cursor_update(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
Evas_Coord tx, ty;
Evas_Coord cx, cy, cw, ch;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
evas_object_geometry_get(sd->text_object, &tx, &ty, NULL, NULL);
if ((sd->unicode_length <= 0) || (sd->cursor_pos <= 0))
{
e_editable_char_size_get(editable, &cw, &ch);
evas_object_move(sd->cursor_object, tx, ty);
evas_object_resize(sd->cursor_object, 1, ch);
}
else
{
if (sd->cursor_pos >= sd->unicode_length)
{
evas_object_text_char_pos_get(sd->text_object, sd->unicode_length - 1,
&cx, &cy, &cw, &ch);
evas_object_move(sd->cursor_object, tx + cx + cw - 1, ty + cy);
evas_object_resize(sd->cursor_object, 1, ch);
}
else
{
evas_object_text_char_pos_get(sd->text_object, sd->cursor_pos,
&cx, &cy, &cw, &ch);
evas_object_move(sd->cursor_object, tx + cx - 1, ty + cy);
evas_object_resize(sd->cursor_object, 1, ch);
}
}
if (sd->cursor_timer)
{
evas_object_show(sd->cursor_object);
ecore_timer_interval_set(sd->cursor_timer, E_EDITABLE_CURSOR_SHOW_DELAY);
}
_e_editable_selection_update(editable);
_e_editable_text_position_update(editable, -1);
}
/* Updates the selection of the editable object */
static void
_e_editable_selection_update(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
Evas_Coord tx, ty;
Evas_Coord cx, cy, cw, ch;
Evas_Coord sx, sy, sw, sh;
int start_pos, end_pos;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if ((sd->cursor_pos == sd->selection_pos) ||
(!sd->selection_visible) || (!sd->selectable))
evas_object_hide(sd->selection_object);
else
{
evas_object_geometry_get(sd->text_object, &tx, &ty, NULL, NULL);
start_pos = (sd->cursor_pos <= sd->selection_pos) ?
sd->cursor_pos : sd->selection_pos;
end_pos = (sd->cursor_pos >= sd->selection_pos) ?
sd->cursor_pos : sd->selection_pos;
/* Position of the start cursor (note, the start cursor can not be at
* the end of the editable object, and the editable object can not be
* empty, or it would have returned before)*/
evas_object_text_char_pos_get(sd->text_object, start_pos,
&cx, &cy, &cw, &ch);
sx = tx + cx - 1;
sy = ty + cy;
/* Position of the end cursor (note, the editable object can not be
* empty, or it would have returned before)*/
if (end_pos >= sd->unicode_length)
{
evas_object_text_char_pos_get(sd->text_object, sd->unicode_length - 1,
&cx, &cy, &cw, &ch);
sw = (tx + cx + cw - 1) - sx;
sh = ch;
}
else
{
evas_object_text_char_pos_get(sd->text_object, end_pos,
&cx, &cy, &cw, &ch);
sw = (tx + cx - 1) - sx;
sh = ch;
}
evas_object_move(sd->selection_object, sx, sy);
evas_object_resize(sd->selection_object, sw, sh);
evas_object_show(sd->selection_object);
}
}
/* Updates the text of the text object of the editable object
* (it fills it with '*' if the editable is in password mode)
* It does not update the position of the text */
static void
_e_editable_text_update(Evas_Object *editable)
{
E_Editable_Smart_Data *sd;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
if (sd->password_mode)
{
char *text;
text = malloc((sd->unicode_length + 1) * sizeof(char));
memset(text, '*', sd->unicode_length * sizeof(char));
text[sd->unicode_length] = '\0';
evas_object_text_text_set(sd->text_object, text);
free(text);
}
else
evas_object_text_text_set(sd->text_object, sd->text ? sd->text : "");
}
/* Updates the position of the text object according to the position of the
* cursor (we make sure the cursor is visible) */
static void
_e_editable_text_position_update(Evas_Object *editable, Evas_Coord real_w)
{
E_Editable_Smart_Data *sd;
Evas_Coord ox, oy, ow;
Evas_Coord tx, ty, tw;
Evas_Coord cx, cy, cw;
Evas_Coord sx, sy;
Evas_Coord offset_x = 0;
if ((!editable) || (!(sd = evas_object_smart_data_get(editable))))
return;
evas_object_geometry_get(editable, &ox, &oy, &ow, NULL);
if (real_w >= 0)
ow = real_w;
evas_object_geometry_get(sd->text_object, &tx, &ty, &tw, NULL);
evas_object_geometry_get(sd->cursor_object, &cx, &cy, &cw, NULL);
evas_object_geometry_get(sd->selection_object, &sx, &sy, NULL, NULL);
if (tw <= ow)
offset_x = ox - tx;
else if (cx < (ox + E_EDITABLE_CURSOR_MARGIN))
offset_x = ox + E_EDITABLE_CURSOR_MARGIN - cx;
else if ((cx + cw + E_EDITABLE_CURSOR_MARGIN) > (ox + ow))
offset_x = (ox + ow) - (cx + cw + E_EDITABLE_CURSOR_MARGIN);
if (tw > ow)
{
if ((tx + offset_x) > ox)
offset_x = ox - tx;
else if ((tx + tw + offset_x) < (ox + ow))
offset_x = (ox + ow) - (tx + tw);
}
if (offset_x != 0)
{
evas_object_move(sd->text_object, tx + offset_x, ty);
evas_object_move(sd->cursor_object, cx + offset_x, cy);
evas_object_move(sd->selection_object, sx + offset_x, sy);
}
}
/* Shows/hides the cursor on regular interval */
static int
_e_editable_cursor_timer_cb(void *data)
{
Evas_Object *editable;
E_Editable_Smart_Data *sd;
if ((!(editable = data)) || (!(sd = evas_object_smart_data_get(editable))))
return 1;
if (evas_object_visible_get(sd->cursor_object))
{
evas_object_hide(sd->cursor_object);
ecore_timer_interval_set(sd->cursor_timer, E_EDITABLE_CURSOR_HIDE_DELAY);
}
else
{
evas_object_show(sd->cursor_object);
ecore_timer_interval_set(sd->cursor_timer, E_EDITABLE_CURSOR_SHOW_DELAY);
}
return 1;
}
/* Editable object's smart methods */
static void
_e_editable_smart_add(Evas_Object *object)
{
Evas *evas;
E_Editable_Smart_Data *sd;
if ((!object) || !(evas = evas_object_evas_get(object)))
return;
sd = malloc(sizeof(E_Editable_Smart_Data));
if (!sd)
return;
evas_object_smart_data_set(object, sd);
sd->text = malloc((E_EDITABLE_BLOCK_SIZE + 1) * sizeof(char));
sd->text[0] = '\0';
sd->char_length = 0;
sd->unicode_length = 0;
sd->allocated_length = E_EDITABLE_BLOCK_SIZE;
/* TODO: themability! */
sd->font = strdup("Vera");
sd->font_size = 10;
sd->font_style = EVAS_TEXT_STYLE_PLAIN;
sd->cursor_width = 1;
sd->selection_on_fg = 0;
sd->average_char_w = 0;
sd->average_char_h = 0;
sd->cursor_timer = NULL;
sd->cursor_pos = 0;
sd->cursor_visible = 1;
sd->selection_pos = 0;
sd->selection_visible = 1;
sd->selectable = 1;
sd->password_mode = 0;
sd->clip_object = evas_object_rectangle_add(evas);
evas_object_smart_member_add(sd->clip_object, object);
sd->event_object = evas_object_rectangle_add(evas);
evas_object_color_set(sd->event_object, 255, 255, 255, 0);
evas_object_clip_set(sd->event_object, sd->clip_object);
evas_object_smart_member_add(sd->event_object, object);
sd->text_object = evas_object_text_add(evas);
evas_object_text_font_set(sd->text_object, sd->font, sd->font_size);
evas_object_text_style_set(sd->text_object, sd->font_style);
evas_object_color_set(sd->text_object, 0, 0, 0, 255);
evas_object_clip_set(sd->text_object, sd->clip_object);
evas_object_smart_member_add(sd->text_object, object);
sd->selection_object = edje_object_add(evas);
evas_object_clip_set(sd->selection_object, sd->clip_object);
evas_object_smart_member_add(sd->selection_object, object);
sd->cursor_object = edje_object_add(evas);
evas_object_clip_set(sd->cursor_object, sd->clip_object);
evas_object_smart_member_add(sd->cursor_object, object);
_e_editable_cursor_update(object);
}
/* Deletes the editable */
static void
_e_editable_smart_del(Evas_Object *object)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
free(sd->font);
evas_object_del(sd->clip_object);
evas_object_del(sd->event_object);
evas_object_del(sd->text_object);
evas_object_del(sd->cursor_object);
evas_object_del(sd->selection_object);
if (sd->cursor_timer)
ecore_timer_del(sd->cursor_timer);
free(sd);
_e_editable_smart_use--;
if (_e_editable_smart_use <= 0)
{
evas_smart_free(_e_editable_smart);
_e_editable_smart = NULL;
}
}
/* Moves the editable object */
static void
_e_editable_smart_move(Evas_Object *object, Evas_Coord x, Evas_Coord y)
{
E_Editable_Smart_Data *sd;
Evas_Coord prev_x, prev_y;
Evas_Coord ox, oy;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_geometry_get(object, &prev_x, &prev_y, NULL, NULL);
evas_object_move(sd->clip_object, x, y);
evas_object_move(sd->event_object, x, y);
evas_object_geometry_get(sd->text_object, &ox, &oy, NULL, NULL);
evas_object_move(sd->text_object, ox + (x - prev_x), oy + (y - prev_y));
evas_object_geometry_get(sd->cursor_object, &ox, &oy, NULL, NULL);
evas_object_move(sd->cursor_object, ox + (x - prev_x), oy + (y - prev_y));
evas_object_geometry_get(sd->selection_object, &ox, &oy, NULL, NULL);
evas_object_move(sd->selection_object, ox + (x - prev_x), oy + (y - prev_y));
}
/* Resizes the editable object */
static void
_e_editable_smart_resize(Evas_Object *object, Evas_Coord w, Evas_Coord h)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_resize(sd->clip_object, w, h);
evas_object_resize(sd->event_object, w, h);
_e_editable_text_position_update(object, w);
}
/* Shows the editable object */
static void
_e_editable_smart_show(Evas_Object *object)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_show(sd->clip_object);
evas_object_show(sd->event_object);
evas_object_show(sd->text_object);
if (sd->cursor_visible)
{
evas_object_show(sd->cursor_object);
if (sd->cursor_timer)
ecore_timer_interval_set(sd->cursor_timer, E_EDITABLE_CURSOR_SHOW_DELAY);
else
{
sd->cursor_timer = ecore_timer_add(E_EDITABLE_CURSOR_SHOW_DELAY,
_e_editable_cursor_timer_cb,
object);
}
}
if ((sd->selectable) && (sd->selection_visible) &&
(sd->cursor_pos != sd->selection_pos))
evas_object_show(sd->selection_object);
}
/* Hides the editable object */
static void
_e_editable_smart_hide(Evas_Object *object)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_hide(sd->clip_object);
evas_object_hide(sd->event_object);
evas_object_hide(sd->text_object);
evas_object_hide(sd->cursor_object);
evas_object_hide(sd->selection_object);
if (sd->cursor_timer)
{
ecore_timer_del(sd->cursor_timer);
sd->cursor_timer = NULL;
}
}
/* Changes the color of the editable object */
static void
_e_editable_color_set(Evas_Object *object, int r, int g, int b, int a)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_color_set(sd->clip_object, r, g, b, a);
}
/* Clips the editable object against "clip" */
static void
_e_editable_clip_set(Evas_Object *object, Evas_Object *clip)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_clip_set(sd->clip_object, clip);
}
/* Unclips the editable object */
static void
_e_editable_clip_unset(Evas_Object *object)
{
E_Editable_Smart_Data *sd;
if ((!object) || (!(sd = evas_object_smart_data_get(object))))
return;
evas_object_clip_unset(sd->clip_object);
}