/* * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 */ #include "e.h" #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; int cursor_pos; int cursor_visible; int selection_pos; int selection_visible; int password_mode; char *text; int char_length; int unicode_length; int allocated_length; 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_char_geometry_get_from_pos(Evas_Object *editable, int utf_pos, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch); 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; } 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 text object */ obj_group = malloc(strlen(group) + strlen("/text") + 1); sprintf(obj_group, "%s/text", group); e_theme_edje_object_set(sd->text_object, category, obj_group); free(obj_group); sd->average_char_w = -1; sd->average_char_h = -1; /* 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); } _e_editable_text_update(editable); _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; _e_editable_text_update(editable); } 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); edje_object_signal_emit(sd->cursor_object, "e,action,show,cursor", "e"); } } /** * 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; evas_object_hide(sd->cursor_object); } /** * 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 * * @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->selection_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 * * @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 ((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); } /** * 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 */ EAPI int e_editable_pos_get_from_coords(Evas_Object *editable, Evas_Coord x, Evas_Coord y) { E_Editable_Smart_Data *sd; Evas_Object *text_obj; Evas_Coord ox, oy; Evas_Coord tx, ty, tw, th; Evas_Coord cx, cw; Evas_Coord canvas_x, canvas_y; int index, pos, i, j; const char *text; if ((!editable) || (!(sd = evas_object_smart_data_get(editable)))) return 0; if (!(text_obj = edje_object_part_object_get(sd->text_object, "e.text.text"))) return 0; evas_object_geometry_get(editable, &ox, &oy, NULL, NULL); evas_object_geometry_get(text_obj, &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 { index = evas_object_text_char_coords_get(text_obj, canvas_x - tx, canvas_y - ty, &cx, NULL, &cw, NULL); text = evas_object_text_text_get(text_obj); if ((index >= 0) && (text)) { if ((canvas_x - tx) > (cx + (cw / 2))) index++; i = 0; j = -1; pos = 0; while ((i < index) && (j != i)) { pos++; j = i; i = evas_string_char_next_get(text, i, NULL); } if (pos > sd->unicode_length) pos = sd->unicode_length; } else pos = 0; } 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; Evas_Object *text_obj; E_Editable_Smart_Data *sd; char *text = "Tout est bon dans l'abricot sauf le noyau!" "Wakey wakey! Eggs and Bakey!"; const char *font, *font_source; Evas_Text_Style_Type style; int font_size; 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))) return; if (!(text_obj = edje_object_part_object_get(sd->text_object, "e.text.text"))) return; if ((sd->average_char_w <= 0) || (sd->average_char_h <= 0)) { font_source = evas_object_text_font_source_get(text_obj); evas_object_text_font_get(text_obj, &font, &font_size); style = evas_object_text_style_get(text_obj); text_obj = evas_object_text_add(evas); evas_object_text_font_source_set(text_obj, font_source); evas_object_text_font_set(text_obj, font, font_size); evas_object_text_style_set(text_obj, style); evas_object_text_text_set(text_obj, text); evas_object_geometry_get(text_obj, NULL, NULL, &tw, &th); evas_object_del(text_obj); 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)) 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_Object *text_obj; Evas_Coord tx, ty; Evas_Coord cx, cy, ch; if ((!editable) || (!(sd = evas_object_smart_data_get(editable)))) return; if (!(text_obj = edje_object_part_object_get(sd->text_object, "e.text.text"))) return; evas_object_geometry_get(text_obj, &tx, &ty, NULL, NULL); _e_editable_char_geometry_get_from_pos(editable, sd->cursor_pos, &cx, &cy, NULL, &ch); evas_object_move(sd->cursor_object, tx + cx, ty + cy); evas_object_resize(sd->cursor_object, sd->cursor_width, ch); if (sd->cursor_visible && evas_object_visible_get(editable)) { evas_object_show(sd->cursor_object); edje_object_signal_emit(sd->cursor_object, "e,action,show,cursor", "e"); } _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_Object *text_obj; Evas_Coord tx, ty; Evas_Coord cx, cy; Evas_Coord sx, sy, sw, sh; int start_pos, end_pos; if ((!editable) || (!(sd = evas_object_smart_data_get(editable)))) return; if (!(text_obj = edje_object_part_object_get(sd->text_object, "e.text.text"))) return; if ((sd->cursor_pos == sd->selection_pos) || (!sd->selection_visible)) evas_object_hide(sd->selection_object); else { evas_object_geometry_get(text_obj, &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; _e_editable_char_geometry_get_from_pos(editable, start_pos, &cx, &cy, NULL, NULL); sx = tx + cx; sy = ty + cy; _e_editable_char_geometry_get_from_pos(editable, end_pos, &cx, NULL, NULL, &sh); sw = tx + cx - sx; 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; Evas_Coord minw, minh; 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'; edje_object_part_text_set(sd->text_object, "e.text.text", text); free(text); } else { edje_object_part_text_set(sd->text_object, "e.text.text", sd->text); } edje_object_size_min_calc(sd->text_object, &minw, &minh); evas_object_resize(sd->text_object, minw, minh); } /* 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); } } /* Gets the geometry of the char according to its utf-8 pos */ static int _e_editable_char_geometry_get_from_pos(Evas_Object *editable, int utf_pos, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch) { E_Editable_Smart_Data *sd; Evas_Object *text_obj; const char *text; Evas_Coord x, w; int index, i; int last_pos; int ret; if (cx) *cx = 0; if (cy) *cy = 0; if (cw) *cw = 0; if (ch) *ch = 0; if ((!editable) || (!(sd = evas_object_smart_data_get(editable)))) return 0; if (!(text_obj = edje_object_part_object_get(sd->text_object, "e.text.text"))) return 0; text = evas_object_text_text_get(text_obj); if ((!text) || (sd->unicode_length <= 0) || (utf_pos <= 0)) { if (cx) *cx = 0; if (cy) *cy = 0; e_editable_char_size_get(editable, cw, ch); return 1; } else { if (utf_pos >= sd->unicode_length) { utf_pos = sd->unicode_length - 1; last_pos = 1; } else last_pos = 0; index = 0; for (i = 0; i < utf_pos; i++) index = evas_string_char_next_get(text, index, NULL); ret = evas_object_text_char_pos_get(text_obj, index, &x, cy, &w, ch); if (cx) *cx = x - 1 + (last_pos ? w : 0); if (cw) *cw = last_pos ? 1 : w; return ret; } } /* Editable object's smart methods */ static void _e_editable_smart_add(Evas_Object *object) { Evas *evas; E_Editable_Smart_Data *sd; Evas_Coord ox, oy; if ((!object) || !(evas = evas_object_evas_get(object))) return; sd = malloc(sizeof(E_Editable_Smart_Data)); if (!sd) return; _e_editable_smart_use++; evas_object_smart_data_set(object, sd); evas_object_geometry_get(object, &ox, &oy, NULL, NULL); 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_width = 1; sd->selection_on_fg = 0; sd->average_char_w = -1; sd->average_char_h = -1; sd->cursor_pos = 0; sd->cursor_visible = 1; sd->selection_pos = 0; sd->selection_visible = 1; sd->password_mode = 0; sd->clip_object = evas_object_rectangle_add(evas); evas_object_move(sd->clip_object, ox, oy); evas_object_smart_member_add(sd->clip_object, object); sd->event_object = evas_object_rectangle_add(evas); evas_object_color_set(sd->event_object, 0, 0, 0, 0); evas_object_clip_set(sd->event_object, sd->clip_object); evas_object_move(sd->event_object, ox, oy); evas_object_smart_member_add(sd->event_object, object); sd->text_object = edje_object_add(evas); evas_object_pass_events_set(sd->text_object, 1); evas_object_clip_set(sd->text_object, sd->clip_object); evas_object_move(sd->text_object, ox, oy); evas_object_smart_member_add(sd->text_object, object); sd->selection_object = edje_object_add(evas); evas_object_pass_events_set(sd->selection_object, 1); evas_object_clip_set(sd->selection_object, sd->clip_object); evas_object_move(sd->selection_object, ox, oy); evas_object_smart_member_add(sd->selection_object, object); sd->cursor_object = edje_object_add(evas); evas_object_pass_events_set(sd->cursor_object, 1); evas_object_clip_set(sd->cursor_object, sd->clip_object); evas_object_move(sd->cursor_object, ox, oy); 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; 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); free(sd->text); 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); edje_object_signal_emit(sd->cursor_object, "e,action,show,cursor", "e"); } if ((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); } /* 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); }