Enlightenment CVS committal Author : moom Project : e17 Module : proto
Dir : e17/proto/etk/src/lib Modified Files: etk_text_view.c etk_textblock.c etk_textblock.h Log Message: * More work on the textblock =================================================================== RCS file: /cvs/e/e17/proto/etk/src/lib/etk_text_view.c,v retrieving revision 1.6 retrieving revision 1.7 diff -u -3 -r1.6 -r1.7 --- etk_text_view.c 2 Jun 2006 18:46:34 -0000 1.6 +++ etk_text_view.c 9 Jul 2006 00:15:57 -0000 1.7 @@ -156,14 +156,17 @@ static void _etk_text_view_key_down_cb(Etk_Object *object, Etk_Event_Key_Up_Down *event, void *data) { Etk_Text_View *text_view; - - if (!(text_view = ETK_TEXT_VIEW(object)) || !event) + Etk_Textblock_Iter *cursor; + + if (!(text_view = ETK_TEXT_VIEW(object)) || !event || !text_view->textblock_object) return; - /* + + cursor = etk_textblock_object_cursor_get(text_view->textblock_object); + if (strcmp(event->key, "Left") == 0) - etk_textblock_iter_goto_prev_char(text_view->textblock->cursor); + etk_textblock_iter_backward_char(cursor); else if (strcmp(event->key, "Right") == 0) - etk_textblock_iter_goto_next_char(text_view->textblock->cursor);*/ + etk_textblock_iter_forward_char(cursor); } /** @} */ =================================================================== RCS file: /cvs/e/e17/proto/etk/src/lib/etk_textblock.c,v retrieving revision 1.16 retrieving revision 1.17 diff -u -3 -r1.16 -r1.17 --- etk_textblock.c 4 Jul 2006 21:37:26 -0000 1.16 +++ etk_textblock.c 9 Jul 2006 00:15:57 -0000 1.17 @@ -12,41 +12,52 @@ * @{ */ +#define ETK_TB_OBJECT_HEIGHT 300 + #define ETK_TB_TAG_PARAM_IS(param, len) \ (param_len == (len) && strncasecmp(param_start, (param), (len)) == 0) #define ETK_TB_TAG_VALUE_IS(value, len) \ (value_len == (len) && strncasecmp(value_start, (value), (len)) == 0) -/* A line for a textblock object is text terminated by '\n', <br> or </p>. - * It can actually fill several "visual" lines because of wrapping */ -typedef struct Etk_Textblock_Object_Line -{ - Etk_Textblock_Node *node; - - Etk_Geometry geometry; - Evas_Object *object; - - Etk_Bool need_geometry_update; - Etk_Bool need_content_update; -} Etk_Textblock_Object_Line; +typedef struct Etk_Textblock_Object_SD Etk_Textblock_Object_SD; +typedef struct Etk_Textblock_Object_Line Etk_Textblock_Object_Line; /* The smart data of a textblock object */ -typedef struct Etk_Textblock_Object_SD +struct Etk_Textblock_Object_SD { Etk_Textblock *tb; Etk_Textblock_Wrap wrap; Evas_Textblock_Style *style; - Evas_List *lines; - Evas_List *first_visible_line; + Etk_Textblock_Object_Line *lines; + Etk_Textblock_Object_Line *last_line; + + Etk_Textblock_Iter *cursor; + Etk_Textblock_Iter *selection; Evas_Object *cursor_object; Evas_Object *clip; Ecore_Job *update_job; -} Etk_Textblock_Object_SD; +}; + +/* A line for a textblock object is a text terminated by '\n', <br> or </p>. + * It can actually fill several "visual" lines because of wrapping */ +struct Etk_Textblock_Object_Line +{ + Etk_Textblock_Object_Line *prev; + Etk_Textblock_Object_Line *next; + + Etk_Textblock_Node *node; + + Etk_Geometry geometry; + Evas_Object *object; + + Etk_Bool need_geometry_update; + Etk_Bool need_content_update; +}; static void _etk_tb_constructor(Etk_Textblock *tb); static void _etk_tb_destructor(Etk_Textblock *tb); @@ -58,7 +69,7 @@ static void _etk_textblock_node_attach(Etk_Textblock_Node *node, Etk_Textblock_Node *parent, Etk_Textblock_Node *prev); static void _etk_textblock_node_format_get(Etk_Textblock_Node *node, Etk_Textblock_Format *format); -static Etk_Textblock_Node *_etk_textblock_nodes_clean(Etk_Textblock *tb, Etk_Textblock_Node *nodes); +static void _etk_textblock_nodes_clean(Etk_Textblock *tb, Etk_Textblock_Node *nodes); static void _etk_textblock_tag_insert(Etk_Textblock *tb, Etk_Textblock_Iter *iter, const char *tag, int length); static Etk_Textblock_Node *_etk_textblock_paragraph_add(Etk_Textblock *tb, Etk_Textblock_Iter *iter); static Etk_Textblock_Node *_etk_textblock_line_add(Etk_Textblock *tb, Etk_Textblock_Iter *iter); @@ -78,12 +89,26 @@ static const char *_etk_textblock_escape_parse(const char *escape, int len); static int _etk_textblock_hex_string_get(char ch); +static Etk_Textblock_Node *_etk_textblock_prev_node_get(Etk_Textblock_Node *node); +static Etk_Textblock_Node *_etk_textblock_next_node_get(Etk_Textblock_Node *node); +static Etk_Textblock_Node *_etk_textblock_prev_text_node_get(Etk_Textblock_Node *node); +static Etk_Textblock_Node *_etk_textblock_next_text_node_get(Etk_Textblock_Node *node); +static Etk_Textblock_Node *_etk_textblock_prev_line_get(Etk_Textblock_Node *line_node); +static Etk_Textblock_Node *_etk_textblock_next_line_get(Etk_Textblock_Node *line_node); + static void _etk_textblock_object_line_add(Evas_Object *tbo, Etk_Textblock_Node *line_node); static void _etk_textblock_object_line_update_queue(Evas_Object *tbo, Etk_Textblock_Object_Line *line, Etk_Bool content_update, Etk_Bool geometry_update); static void _etk_textblock_object_line_update(Evas_Object *tbo, Etk_Textblock_Object_Line *line, int y); static void _etk_textblock_object_line_fill(Evas_Object *tbo, Evas_Textblock_Cursor *cur, Etk_Textblock_Node *node); static void _etk_textblock_object_update(Evas_Object *tbo); static void _etk_textblock_object_update_job(void *data); +static void _etk_textblock_object_cursor_update(Evas_Object *tbo); +static void _etk_textblock_iter_update(Etk_Textblock_Iter *iter); + +static Etk_Textblock_Object_Line *_etk_textblock_object_line_get_from_node(Evas_Object *tbo, Etk_Textblock_Node *node); +static Evas_Textblock_Cursor *_etk_textblock_object_cursor_get_from_iter(Evas_Object *tbo, Etk_Textblock_Iter *iter); +/* TODO: unref */ +static int _etk_textblock_text_nodes_count(Etk_Textblock_Node *line, Etk_Textblock_Node *node); static void _etk_tb_object_smart_add(Evas_Object *obj); static void _etk_tb_object_smart_del(Evas_Object *obj); @@ -201,7 +226,7 @@ Evas_List *l; Evas_Object *tbo; Etk_Textblock_Object_SD *tbo_sd; - Etk_Textblock_Object_Line *line; + Etk_Textblock_Object_Line *line, *next; Etk_Textblock_Node *node; if (!tb) @@ -209,7 +234,7 @@ /* Frees the nodes */ while (tb->root.children) - tb->root.children = _etk_textblock_node_free(tb->root.children); + _etk_textblock_node_free(tb->root.children); /* Adds an empty line */ node = _etk_textblock_node_new(&tb->root, NULL, ETK_TEXTBLOCK_NODE_PARAGRAPH, ETK_TEXTBLOCK_TAG_P); @@ -221,14 +246,12 @@ if (!(tbo = l->data) || !(tbo_sd = evas_object_smart_data_get(tbo))) continue; - while (tbo_sd->lines) + for (line = tbo_sd->lines; line; line = next) { - line = tbo_sd->lines->data; if (line->object) evas_object_del(line->object); + next = line->next; free(line); - - tbo_sd->lines = evas_list_remove_list(tbo_sd->lines, tbo_sd->lines); } _etk_textblock_object_line_add(tbo, node); } @@ -392,6 +415,8 @@ esc_end = - 1; } + /* TODO: update queue!! */ + new_line = ETK_FALSE; unicode_length = 0; } @@ -461,8 +486,8 @@ } } - /* TODO: optimize (only clean the modified nodes)? */ - tb->root.children = _etk_textblock_nodes_clean(tb, tb->root.children); + /* TODO: optimize: only clean the modified nodes!!! */ + _etk_textblock_nodes_clean(tb, tb->root.children); if (iter->gravity == ETK_TEXTBLOCK_GRAVITY_LEFT) etk_textblock_iter_free(it); } @@ -564,6 +589,8 @@ iter->index = 0; iter->pos = 0; + + _etk_textblock_iter_update(iter); } /** @@ -572,21 +599,83 @@ */ void etk_textblock_iter_forward_end(Etk_Textblock_Iter *iter) { - Etk_Textblock_Node *n; - if (!iter || !iter->tb) return; iter->node = &iter->tb->root; while (iter->node->children) - { - for (n = iter->node->children; n->next; ) - n = n->next; - iter->node = n; - } + iter->node = iter->node->last_child; iter->pos = iter->node->unicode_length; iter->index = etk_string_length_get(iter->node->text); + + _etk_textblock_iter_update(iter); +} + +/** + * @brief Moves the iterator backward by one character offset + * @param iter an iterator + * @return Returns ETK_FALSE if the movement was not possible (i.e. if the iterator already points + * on the first character of the textblock) + */ +Etk_Bool etk_textblock_iter_backward_char(Etk_Textblock_Iter *iter) +{ + if (!(iter || !_etk_textblock_iter_is_valid(NULL, iter))) + return ETK_FALSE; + + if (iter->pos > 0) + { + iter->pos--; + _etk_textblock_iter_update(iter); + return ETK_TRUE; + } + else + { + Etk_Textblock_Node *prev_text_node; + + if ((prev_text_node = _etk_textblock_prev_text_node_get(iter->node))) + { + iter->node = prev_text_node; + iter->pos = prev_text_node->unicode_length - 1; + _etk_textblock_iter_update(iter); + return ETK_TRUE; + } + else + return ETK_FALSE; + } +} + +/** + * @brief Moves the iterator forward by one character offset + * @param iter an iterator + * @return Returns ETK_FALSE if the movement was not possible (i.e. if the iterator already points + * after the last character of the textblock) + */ +Etk_Bool etk_textblock_iter_forward_char(Etk_Textblock_Iter *iter) +{ + if (!(iter || !_etk_textblock_iter_is_valid(NULL, iter))) + return ETK_FALSE; + + if (iter->pos < iter->node->unicode_length) + { + iter->pos++; + _etk_textblock_iter_update(iter); + return ETK_TRUE; + } + else + { + Etk_Textblock_Node *next_text_node; + + if ((next_text_node = _etk_textblock_next_text_node_get(iter->node))) + { + iter->node = next_text_node; + iter->pos = 1; + _etk_textblock_iter_update(iter); + return ETK_TRUE; + } + else + return ETK_FALSE; + } } /** @@ -682,20 +771,23 @@ tbo_sd = evas_object_smart_data_get(obj); tbo_sd->tb= tb; - /* We creates the lines */ + /* We create the lines */ for (paragraph = tb->root.children; paragraph; paragraph = paragraph->next) { for (line = paragraph->children; line; line = line->next) _etk_textblock_object_line_add(obj, line); } + tbo_sd->cursor = etk_textblock_iter_new(tb); + tbo_sd->selection = etk_textblock_iter_new(tb); + tb->evas_objects = evas_list_append(tb->evas_objects, obj); return obj; } /** - * @brief Sets how the text of the textblock object should be wrapped by default + * @brief Sets how the text of the textblock object should be wrapped by default (a paragraph can override this setting) * @param tbo a textblock object * @param wrap the wrap mode. Here, ETK_TEXTBLOCK_WRAP_DEFAULT is equivalent to ETK_TEXTBLOCK_WRAP_WORD */ @@ -703,7 +795,6 @@ { Etk_Textblock_Object_SD *tbo_sd; Etk_Textblock_Object_Line *line; - Evas_List *l; if (!tbo || !(tbo_sd = evas_object_smart_data_get(tbo))) return; @@ -715,12 +806,11 @@ /* Updates the object's lines */ - for (l = tbo_sd->lines; l; l = l->next) + for (line = tbo_sd->lines; line; line = line->next) { - line = l->data; if (line->object && line->node && line->node->parent && line->node->parent->type == ETK_TEXTBLOCK_NODE_PARAGRAPH && - line->node->parent->tag.params.p.wrap < 0) + line->node->parent->tag.params.p.wrap == ETK_TEXTBLOCK_WRAP_DEFAULT) { Evas_Textblock_Cursor *cur; const char *format; @@ -728,7 +818,7 @@ cur = evas_object_textblock_cursor_new(line->object); for (evas_textblock_cursor_node_first(cur); evas_textblock_cursor_node_next(cur); ) { - if ((format == evas_textblock_cursor_node_format_get(cur)) && strstr(format, "wrap")) + if ((format = evas_textblock_cursor_node_format_get(cur)) && strstr(format, "wrap")) { if (wrap == ETK_TEXTBLOCK_WRAP_WORD) evas_textblock_cursor_format_append(cur, "+ wrap=word"); @@ -746,6 +836,8 @@ _etk_textblock_object_line_update_queue(tbo, line, ETK_FALSE, ETK_TRUE); } } + + /* TODO: update cursor? */ } /** @@ -763,6 +855,41 @@ } /** + * @brief Gets the cursor's iterator of the textblock object + * @param tbo a textblock object + * @return Returns the cursor's iterator of the textblock object + * @warning You should not free the returned iterator + */ +Etk_Textblock_Iter *etk_textblock_object_cursor_get(Evas_Object *tbo) +{ + Etk_Textblock_Object_SD *tbo_sd; + + if (!tbo || !(tbo_sd = evas_object_smart_data_get(tbo))) + return NULL; + return tbo_sd->cursor; +} + +/** + * @brief Gets the selection bound's iterator of the textblock object + * @param tbo a textblock object + * @return Returns the selection bound's iterator of the textblock object + * @warning You should not free the returned iterator + */ +Etk_Textblock_Iter *etk_textblock_object_selection_bound_get(Evas_Object *tbo) +{ + Etk_Textblock_Object_SD *tbo_sd; + + if (!tbo || !(tbo_sd = evas_object_smart_data_get(tbo))) + return NULL; + return tbo_sd->selection; +} + + +/************************** + * Misc funcs + **************************/ + +/** * @brief Gets the length of a unicode-encoded string. * It's not always the number of bytes of the string since a character can be encoded with several bytes in unicode. * @param unicode_string a unicode-encoded string @@ -818,6 +945,7 @@ tb->root.prev = NULL; tb->root.next = NULL; tb->root.children = NULL; + tb->root.last_child = NULL; /* Adds an empty line */ node = _etk_textblock_node_new(&tb->root, NULL, ETK_TEXTBLOCK_NODE_PARAGRAPH, ETK_TEXTBLOCK_TAG_P); @@ -840,7 +968,7 @@ etk_textblock_iter_free(tb->iters->data); while (tb->root.children) - tb->root.children = _etk_textblock_node_free(tb->root.children); + _etk_textblock_node_free(tb->root.children); } /************************** @@ -885,6 +1013,7 @@ node->prev = NULL; node->next = NULL; node->children = NULL; + node->last_child = NULL; _etk_textblock_node_type_set(node, node_type, tag_type); _etk_textblock_node_attach(node, parent, prev); @@ -907,13 +1036,15 @@ if (node->parent && node->parent->children == node) node->parent->children = node->next; + if (node->parent && node->parent->last_child == node) + node->parent->last_child = node->prev; if (node->prev) node->prev->next = node->next; if (node->next) node->next->prev = node->prev; while (node->children) - node->children = _etk_textblock_node_free(node->children); + _etk_textblock_node_free(node->children); n = node->next ? node->next : node->prev; free(node); @@ -967,7 +1098,9 @@ /* If "prev" == NULL, the new node is prepended to the list of nodes of the "parent" */ static void _etk_textblock_node_attach(Etk_Textblock_Node *node, Etk_Textblock_Node *parent, Etk_Textblock_Node *prev) { - if (!node || (node->parent == parent && node->prev == prev)) + if (!node || !parent) + return; + if ((prev && prev->parent != parent) || (node->parent == parent && node->prev == prev)) return; /* First, we detach the node */ @@ -975,6 +1108,8 @@ { if (node->parent->children == node) node->parent->children = node->next; + if (node->parent->last_child == node) + node->parent->last_child = node->prev; if (node->prev) node->prev->next = node->next; if (node->next) @@ -982,27 +1117,16 @@ } /* Then we re-attach it */ - if (!prev || prev->parent != parent) - { - node->prev = NULL; - if (parent) - { - node->next = parent->children; - if (node->next) - node->next->prev = node; - parent->children = node; - } - else - node->next = NULL; - } - else - { - node->prev = prev; - node->next = prev->next; - if (node->next) - node->next->prev = node; + node->prev = prev; + node->next = prev ? prev->next : parent->children; + if (node->next) + node->next->prev = node; + if (prev) prev->next = node; - } + else + parent->children = node; + if (parent->last_child == prev) + parent->last_child = node; node->parent = parent; } @@ -1095,19 +1219,19 @@ } /* Removes the empty nodes in the list of 'nodes' */ -static Etk_Textblock_Node *_etk_textblock_nodes_clean(Etk_Textblock *tb, Etk_Textblock_Node *nodes) +static void _etk_textblock_nodes_clean(Etk_Textblock *tb, Etk_Textblock_Node *nodes) { - Etk_Textblock_Node *n, *res = NULL; + Etk_Textblock_Node *n; Etk_Textblock_Iter *it; Evas_List *l; Etk_Bool delete_node; if (!tb || !nodes) - return NULL; + return; for (n = nodes; n; ) { - n->children = _etk_textblock_nodes_clean(tb, n->children); + _etk_textblock_nodes_clean(tb, n->children); delete_node = ETK_FALSE; if (n->type == ETK_TEXTBLOCK_NODE_NORMAL && !n->children && etk_string_length_get(n->text) <= 0) @@ -1127,14 +1251,8 @@ if (delete_node) n = _etk_textblock_node_free(n); else - { - if (!res) - res = n; n = n->next; - } } - - return res; } /* Inserts a tag to the textblock (used by etk_textblock_text_markup_insert()) */ @@ -1765,7 +1883,7 @@ { Etk_Textblock_Node *node, *right_node; - if (!iter || !_etk_textblock_iter_is_valid(iter->tb, iter)) + if (!iter || !_etk_textblock_iter_is_valid(NULL, iter)) return ETK_FALSE; node = iter->node; @@ -2023,6 +2141,135 @@ return 0; } +/* Gets the node just before "node" */ +static Etk_Textblock_Node *_etk_textblock_prev_node_get(Etk_Textblock_Node *node) +{ + Etk_Textblock_Node *n; + + if (!node) + return NULL; + + for (n = node; n; n = n->parent) + { + if (n->prev) + { + n = n->prev; + break; + } + } + if (!n) + return NULL; + + while (n->last_child) + n = n->last_child; + /* + while (n->children) + for (n = n->children; n->next; n = n->next);*/ + + return n; +} + +/* Gets the node just after "node" */ +static Etk_Textblock_Node *_etk_textblock_next_node_get(Etk_Textblock_Node *node) +{ + Etk_Textblock_Node *n; + + if (!node) + return NULL; + + for (n = node; n; n = n->parent) + { + if (n->next) + { + n = n->next; + break; + } + } + if (!n) + return NULL; + + while (n->children) + n = n->children; + + return n; +} + +/* Gets the text node just before "node" */ +static Etk_Textblock_Node *_etk_textblock_prev_text_node_get(Etk_Textblock_Node *node) +{ + Etk_Textblock_Node *prev; + + if (!node) + return NULL; + + for (prev = _etk_textblock_prev_node_get(node); prev; prev = _etk_textblock_prev_node_get(prev)) + { + if (etk_string_length_get(prev->text) > 0) + return prev; + } + + return NULL; +} + +/* Gets the text node just after "node" */ +static Etk_Textblock_Node *_etk_textblock_next_text_node_get(Etk_Textblock_Node *node) +{ + Etk_Textblock_Node *next; + + if (!node) + return NULL; + + for (next = _etk_textblock_next_node_get(node); next; next = _etk_textblock_next_node_get(next)) + { + if (etk_string_length_get(next->text) > 0) + return next; + } + + return NULL; +} + +/* Gets the node of the line before "line_node" */ +static Etk_Textblock_Node *_etk_textblock_prev_line_get(Etk_Textblock_Node *line_node) +{ + if (!line_node || line_node->type != ETK_TEXTBLOCK_NODE_LINE) + return NULL; + + if (line_node->prev) + return line_node->prev; + else + { + Etk_Textblock_Node *paragraph; + + for (paragraph = line_node->parent->prev; paragraph; paragraph = paragraph->prev) + { + if (paragraph->last_child) + return paragraph->last_child; + } + return NULL; + } +} + +/* Gets the node of the line after "line_node" */ +static Etk_Textblock_Node *_etk_textblock_next_line_get(Etk_Textblock_Node *line_node) +{ + if (!line_node || line_node->type != ETK_TEXTBLOCK_NODE_LINE) + return NULL; + + if (line_node->next) + return line_node->next; + else + { + Etk_Textblock_Node *paragraph; + + for (paragraph = line_node->parent->next; paragraph; paragraph = paragraph->next) + { + if (paragraph->children) + return paragraph->children; + } + return NULL; + } +} + /************************** * * Textblock object's rendering @@ -2033,35 +2280,47 @@ static void _etk_textblock_object_line_add(Evas_Object *tbo, Etk_Textblock_Node *line_node) { Etk_Textblock_Object_SD *tbo_sd; - Etk_Textblock_Object_Line *line, *new_line; + Etk_Textblock_Object_Line *prev_line, *new_line; + Etk_Textblock_Node *prev; if (!tbo || !(tbo_sd = evas_object_smart_data_get(tbo))) return; new_line = malloc(sizeof(Etk_Textblock_Object_Line)); + new_line->prev = NULL; + new_line->next = NULL; new_line->node = line_node; - new_line->object = NULL; + new_line->geometry.x = 0; + new_line->geometry.y = 0; new_line->geometry.w = 0; new_line->geometry.h = 0; + new_line->object = NULL; - if (!tbo_sd->lines) - tbo_sd->lines = evas_list_append(tbo_sd->lines, new_line); + if (!(prev = _etk_textblock_prev_line_get(line_node)) || !tbo_sd->lines) + { + new_line->next = tbo_sd->lines; + if (tbo_sd->lines) + tbo_sd->lines->prev = new_line; + tbo_sd->lines = new_line; + if (!tbo_sd->last_line) + tbo_sd->last_line = new_line; + } + else if (prev == tbo_sd->last_line->node) + { + new_line->prev = tbo_sd->last_line; + tbo_sd->last_line->next = new_line; + tbo_sd->last_line = new_line; + } + else if ((prev_line = _etk_textblock_object_line_get_from_node(tbo, prev))) + { + new_line->prev = prev_line; + prev_line->next = new_line; + } else { - line = tbo_sd->lines->data; - if (_etk_textblock_node_compare(line_node, line->node) <= 0) - tbo_sd->lines = evas_list_prepend(tbo_sd->lines, new_line); - else - { - line = evas_list_last(tbo_sd->lines)->data; - if (_etk_textblock_node_compare(line_node, line->node) >= 0) - tbo_sd->lines = evas_list_append(tbo_sd->lines, new_line); - else - { - /* TODO: We need to optimize that (and to actually do it!!)! */ - tbo_sd->lines = evas_list_append(tbo_sd->lines, new_line); - } - } + ETK_WARNING("Could not add a line the textblock object"); + free(new_line); + return; } _etk_textblock_object_line_update_queue(tbo, new_line, ETK_TRUE, ETK_TRUE); @@ -2093,7 +2352,7 @@ return; if (!(evas = evas_object_evas_get(tbo))) return; - if (!line->need_content_update && !line->need_geometry_update && line->object) + if (!line->need_content_update && !line->need_geometry_update) return; evas_object_geometry_get(tbo, &ox, &oy, &ow, &oh); @@ -2108,7 +2367,7 @@ evas_object_show(line->object); evas_object_show(tbo_sd->clip); } - /* TODO: optimize!!! */ + line->need_content_update = ETK_TRUE; line->need_geometry_update = ETK_TRUE; } @@ -2131,11 +2390,12 @@ line->geometry.x = 0; line->geometry.y = y; line->geometry.w = ow; - evas_object_resize(line->object, ow, 300); + evas_object_resize(line->object, line->geometry.w, ETK_TB_OBJECT_HEIGHT); evas_object_textblock_size_formatted_get(line->object, NULL, &line->geometry.h); evas_object_move(line->object, ox + line->geometry.x, oy + line->geometry.y); - evas_object_resize(line->object, line->geometry.w, line->geometry.h); + if (line->geometry.h > ETK_TB_OBJECT_HEIGHT) + evas_object_resize(line->object, line->geometry.w, line->geometry.h); line->need_geometry_update = ETK_FALSE; } @@ -2174,16 +2434,15 @@ /* Alignment */ if (paragraph->tag.params.p.align == 0.5) - { evas_textblock_cursor_format_append(cur, "+ align=center"); - opened_nodes++; - } else if (paragraph->tag.params.p.align == 1.0) - { evas_textblock_cursor_format_append(cur, "+ align=right"); - opened_nodes++; + else + { + fmt_str = etk_string_set_printf(fmt_str, "+ align=%d%%", (int)paragraph->tag.params.p.align * 100); + evas_textblock_cursor_format_append(cur, etk_string_get(fmt_str)); } - /* TODO: Does Evas' textblock support float alignments? */ + opened_nodes++; /* Margins */ if (paragraph->tag.params.p.left_margin > 0) @@ -2424,43 +2683,30 @@ { Etk_Textblock_Object_SD *tbo_sd; Etk_Textblock_Object_Line *line; - Evas_List *l; - int y_offset = 0; int y, h; if (!tbo || !(tbo_sd = evas_object_smart_data_get(tbo))) return; + /* We update the lines */ y = 0; evas_object_geometry_get(tbo, NULL, NULL, NULL, &h); - tbo_sd->first_visible_line = NULL; - for (l = tbo_sd->lines; l; l = l->next) + for (line = tbo_sd->lines; line; line = line->next) { - line = l->data; - - if (y >= h) - { - if (line->object) - { - evas_object_del(line->object); - line->object = NULL; - continue; - } - else - break; - } - + /* Optimize!! */ _etk_textblock_object_line_update(tbo, line, y); - y = line->geometry.y + line->geometry.h; - if (y <= y_offset) + if ((y + line->geometry.h <= 0 || y >= h) && line->object) { evas_object_del(line->object); line->object = NULL; } - else if (!tbo_sd->first_visible_line) - tbo_sd->first_visible_line = l; + + y += line->geometry.h; } + + /* Then we update the cursor and the selection */ + _etk_textblock_object_cursor_update(tbo); } /* The job used to update the textblock object */ @@ -2476,6 +2722,156 @@ tbo_sd->update_job = NULL; } +/* Updates the cursor object and the selection of the textblock object */ +static void _etk_textblock_object_cursor_update(Evas_Object *tbo) +{ + Etk_Textblock_Object_SD *tbo_sd; + Evas_Textblock_Cursor *cur; + Etk_Textblock_Object_Line *line; + int cx, cy, cw, ch; + int ox, oy; + + if (!tbo || !(tbo_sd = evas_object_smart_data_get(tbo))) + return; + + line = _etk_textblock_object_line_get_from_node(tbo, tbo_sd->cursor->node); + /* TODO: optimize?! */ + cur = _etk_textblock_object_cursor_get_from_iter(tbo, tbo_sd->cursor); + evas_textblock_cursor_char_geometry_get(cur, &cx, &cy, &cw, &ch); + evas_object_geometry_get(tbo, &ox, &oy, NULL, NULL); + + if (tbo_sd->cursor->pos < tbo_sd->cursor->node->unicode_length) + { + evas_object_move(tbo_sd->cursor_object, cx + line->geometry.x + ox - 1, cy + line->geometry.y + oy); + } + else + { + evas_object_move(tbo_sd->cursor_object, cx + line->geometry.x + ox + cw - 1, cy + line->geometry.y + oy); + } + evas_object_resize(tbo_sd->cursor_object, 1, ch); + evas_object_show(tbo_sd->cursor_object); +} + +/* TODOC */ +static void _etk_textblock_iter_update(Etk_Textblock_Iter *iter) +{ + Evas_List *l; + Evas_Object *tbo; + Etk_Textblock_Object_SD *tbo_sd; + + if (!iter || !iter->tb) + return; + + for (l = iter->tb->evas_objects; l; l = l->next) + { + tbo = l->data; + if (!(tbo_sd = evas_object_smart_data_get(tbo))) + continue; + + if (iter == tbo_sd->cursor) + _etk_textblock_object_cursor_update(tbo); + /* TODO: selection, queue */ + } +} + +/* Gets the line of a textblock object that contains the given node */ +/* TODO: _etk_textblock_object_line_get_from_node(): we should optimize that, maybe with a btree */ +static Etk_Textblock_Object_Line *_etk_textblock_object_line_get_from_node(Evas_Object *tbo, Etk_Textblock_Node *node) +{ + Etk_Textblock_Object_SD *tbo_sd; + Etk_Textblock_Object_Line *line; + Etk_Textblock_Node *n; + + if (!tbo || !node || !(tbo_sd = evas_object_smart_data_get(tbo))) + return NULL; + + for (n = node; n; n = n->parent) + { + if (n->type == ETK_TEXTBLOCK_NODE_LINE) + break; + } + if (!n) + return NULL; + + for (line = tbo_sd->lines; line; line = line->next) + { + if (line->node == n) + return line; + } + + return NULL; +} + +/* Gets the evas cursor corresponding to the iterator. + * The returned cursor should be freed with evas_textblock_cursor_free() + * The returned cursor could be invalidated during the next update, so you can't keep the cursor */ +static Evas_Textblock_Cursor *_etk_textblock_object_cursor_get_from_iter(Evas_Object *tbo, Etk_Textblock_Iter *iter) +{ + Etk_Textblock_Object_SD *tbo_sd; + Etk_Textblock_Object_Line *line; + Evas_Textblock_Cursor *cur; + int num_text_nodes, i; + + if (!tbo || !iter || !(tbo_sd = evas_object_smart_data_get(tbo)) || !_etk_textblock_iter_is_valid(NULL, iter)) + return NULL; + if (!(line = _etk_textblock_object_line_get_from_node(tbo, iter->node))) + return NULL; + + if (!line->object) + { + line->need_geometry_update = ETK_TRUE; + line->need_content_update = ETK_TRUE; + _etk_textblock_object_line_update(tbo, line, line->geometry.y); + } + + num_text_nodes = _etk_textblock_text_nodes_count(line->node, iter->node); + cur = evas_object_textblock_cursor_new(line->object); + + i = 0; + for (;;) + { + if (evas_textblock_cursor_node_text_get(cur)) + i++; + if (i >= num_text_nodes) + break; + if (!evas_textblock_cursor_node_next(cur)) + break; + } + + if (iter->pos >= iter->node->unicode_length) + evas_textblock_cursor_pos_set(cur, ETK_MAX(0, iter->node->unicode_length - 1)); + else + evas_textblock_cursor_pos_set(cur, iter->pos); + + return cur; +} + +/* Counts recursively the number of text nodes that should be walked through to reach "node", in the line "line". + * This function is used by _etk_textblock_object_cursor_get_from_iter() */ +static int _etk_textblock_text_nodes_count(Etk_Textblock_Node *line, Etk_Textblock_Node *node) +{ + int count = 0; + Etk_Textblock_Node *n; + + if (!line || !node) + return 0; + + if (line != node) + { + for (n = line->children; n; n = n->next) + { + count += _etk_textblock_text_nodes_count(n, node); + if (n == node) + break; + } + } + + if (etk_string_length_get(line->text) > 0) + count++; + + return count; +} + /************************** * * Textblock object's smart object @@ -2496,10 +2892,16 @@ tbo_sd->wrap = ETK_TEXTBLOCK_WRAP_WORD; tbo_sd->update_job = NULL; - tbo_sd->cursor_object = NULL; tbo_sd->clip = evas_object_rectangle_add(evas); + tbo_sd->cursor_object = evas_object_rectangle_add(evas); + evas_object_color_set(tbo_sd->cursor_object, 128, 0, 0, 128); + evas_object_clip_set(tbo_sd->cursor_object, tbo_sd->clip); + tbo_sd->lines = NULL; - tbo_sd->first_visible_line = NULL; + tbo_sd->last_line = NULL; + + tbo_sd->cursor = NULL; + tbo_sd->selection = NULL; tbo_sd->style = evas_textblock_style_new(); evas_textblock_style_set(tbo_sd->style, "DEFAULT='font=Vera font_size=10 align=left color=#000000'"); @@ -2511,7 +2913,7 @@ static void _etk_tb_object_smart_del(Evas_Object *obj) { Etk_Textblock_Object_SD *tbo_sd; - Etk_Textblock_Object_Line *line; + Etk_Textblock_Object_Line *line, *next; if (!obj || !(tbo_sd = evas_object_smart_data_get(obj))) return; @@ -2519,15 +2921,12 @@ if (tbo_sd->update_job) ecore_job_del(tbo_sd->update_job); - while (tbo_sd->lines) + for (line = tbo_sd->lines; line; line = next) { - line = tbo_sd->lines->data; - if (line->object) evas_object_del(line->object); + next = line->next; free(line); - - tbo_sd->lines = evas_list_remove_list(tbo_sd->lines, tbo_sd->lines); } evas_object_del(tbo_sd->cursor_object); evas_textblock_style_free(tbo_sd->style); @@ -2547,31 +2946,33 @@ static void _etk_tb_object_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y) { Etk_Textblock_Object_SD *tbo_sd; - Evas_Coord prev_x, prev_y; - Evas_List *l; + int prev_x, prev_y; + int cursor_x, cursor_y; Etk_Textblock_Object_Line *line; if (!obj || !(tbo_sd = evas_object_smart_data_get(obj))) return; - evas_object_move(tbo_sd->clip, x, y); - evas_object_geometry_get(obj, &prev_x, &prev_y, NULL, NULL); - for (l = tbo_sd->lines; l; l = l->next) + for (line = tbo_sd->lines; line; line = line->next) { - line = l->data; + /* TODO: Ohh!! */ line->geometry.x += x - prev_x; line->geometry.y += y - prev_y; if (line->object) evas_object_move(line->object, line->geometry.x, line->geometry.y); } + + evas_object_geometry_get(tbo_sd->cursor_object, &cursor_x, &cursor_y, NULL, NULL); + evas_object_move(tbo_sd->cursor_object, cursor_x + x - prev_x, cursor_y + y - prev_y); + evas_object_move(tbo_sd->clip, x, y); } /* Resizes the textblock object */ static void _etk_tb_object_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) { Etk_Textblock_Object_SD *tbo_sd; - Evas_List *l; + Etk_Textblock_Object_Line *line; if (!obj || !(tbo_sd = evas_object_smart_data_get(obj))) return; @@ -2579,8 +2980,8 @@ evas_object_resize(tbo_sd->clip, w, h); /* TODO: optimization for non-wrapped lines? */ - for (l = tbo_sd->lines; l; l = l->next) - _etk_textblock_object_line_update_queue(obj, l->data, ETK_FALSE, ETK_TRUE); + for (line = tbo_sd->lines; line; line = line->next) + _etk_textblock_object_line_update_queue(obj, line, ETK_FALSE, ETK_TRUE); } /* Shows the textblock object */ @@ -2588,24 +2989,22 @@ { Etk_Textblock_Object_SD *tbo_sd; Etk_Textblock_Object_Line *line; - Evas_List *l; Etk_Bool show_clip = ETK_FALSE; if (!obj || !(tbo_sd = evas_object_smart_data_get(obj))) return; - for (l = tbo_sd->first_visible_line; l; l = l->next) + for (line = tbo_sd->lines; line; line = line->next) { - line = l->data; if (line->object) { evas_object_show(line->object); show_clip = ETK_TRUE; } - else - break; } + /* TODO: timer! */ + evas_object_show(tbo_sd->cursor_object); if (show_clip) evas_object_show(tbo_sd->clip); } @@ -2615,20 +3014,17 @@ { Etk_Textblock_Object_SD *tbo_sd; Etk_Textblock_Object_Line *line; - Evas_List *l; if (!obj || !(tbo_sd = evas_object_smart_data_get(obj))) return; - for (l = tbo_sd->first_visible_line; l; l = l->next) + for (line = tbo_sd->lines; line; line = line->next) { - line = l->data; if (line->object) evas_object_hide(line->object); - else - break; } + evas_object_hide(tbo_sd->cursor_object); evas_object_hide(tbo_sd->clip); } =================================================================== RCS file: /cvs/e/e17/proto/etk/src/lib/etk_textblock.h,v retrieving revision 1.10 retrieving revision 1.11 diff -u -3 -r1.10 -r1.11 --- etk_textblock.h 4 Jul 2006 21:37:26 -0000 1.10 +++ etk_textblock.h 9 Jul 2006 00:15:57 -0000 1.11 @@ -23,7 +23,7 @@ typedef enum Etk_Textblock_Wrap { ETK_TEXTBLOCK_WRAP_NONE, /**< The text is not wrapped */ - ETK_TEXTBLOCK_WRAP_DEFAULT, /**< TODOC */ + ETK_TEXTBLOCK_WRAP_DEFAULT, /**< TODOC */ ETK_TEXTBLOCK_WRAP_WORD, /**< The text is wrapped between the words (or between the chars if it's not sufficient) */ ETK_TEXTBLOCK_WRAP_CHAR /**< The text is wrapped between the chars */ } Etk_Textblock_Wrap; @@ -167,6 +167,7 @@ Etk_Textblock_Node *prev; Etk_Textblock_Node *next; Etk_Textblock_Node *children; + Etk_Textblock_Node *last_child; }; /** @@ -222,6 +223,8 @@ void etk_textblock_iter_backward_start(Etk_Textblock_Iter *iter); void etk_textblock_iter_forward_end(Etk_Textblock_Iter *iter); +Etk_Bool etk_textblock_iter_backward_char(Etk_Textblock_Iter *iter); +Etk_Bool etk_textblock_iter_forward_char(Etk_Textblock_Iter *iter); void etk_textblock_iter_copy(Etk_Textblock_Iter *dest, const Etk_Textblock_Iter *src); int etk_textblock_iter_compare(Etk_Textblock_Iter *iter1, Etk_Textblock_Iter *iter2); @@ -231,6 +234,9 @@ void etk_textblock_object_wrap_set(Evas_Object *tbo, Etk_Textblock_Wrap wrap); Etk_Textblock_Wrap etk_textblock_object_wrap_get(Evas_Object *tbo); + +Etk_Textblock_Iter *etk_textblock_object_cursor_get(Evas_Object *tbo); +Etk_Textblock_Iter *etk_textblock_object_selection_bound_get(Evas_Object *tbo); /* Misc funcs */ int etk_textblock_unicode_length_get(const char *unicode_string); ------------------------------------------------------------------------- Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642 _______________________________________________ enlightenment-cvs mailing list enlightenment-cvs@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-cvs