/* vim: set sw=8 ts=8 sts=8 noexpandtab: */ #include "ewl_text_fmt.h" #include "ewl_private.h" #include "ewl_macros.h" #include "ewl_debug.h" #define FMT_PRINT(fmt) \ { \ printf("Set current format %d\n" \ "\tindex: %d chars %d bytes\n" \ "\tlength: %d chars %d bytes\n", \ __LINE__, \ fmt->current_node.char_idx, \ fmt->current_node.byte_idx, \ fmt->length.char_len, \ fmt->length.byte_len); \ } static void ewl_text_fmt_node_free(void *node); Ewl_Text_Fmt * ewl_text_fmt_new(Ewl_Text *t) { Ewl_Text_Fmt *fmt; DENTER_FUNCTION(DLEVEL_STABLE); fmt = NEW(Ewl_Text_Fmt, 1); if (!fmt) DRETURN_PTR(NULL, DLEVEL_STABLE); fmt->nodes = ecore_dlist_new(); if (!fmt->nodes) { ewl_text_fmt_destroy(fmt); DRETURN_PTR(NULL, DLEVEL_STABLE); } ecore_dlist_set_free_cb(fmt->nodes, ewl_text_fmt_node_free); fmt->text = t; DRETURN_PTR(fmt, DLEVEL_STABLE); } void ewl_text_fmt_destroy(Ewl_Text_Fmt *fmt) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); if (fmt->nodes) ecore_dlist_destroy(fmt->nodes); FREE(fmt); DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_clear(Ewl_Text_Fmt *fmt) { Ewl_Text_Fmt_Node *node; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); while ((node = ecore_dlist_remove_first(fmt->nodes))) { if (node->tx) ewl_text_context_release(node->tx); FREE(node); } fmt->current_node.char_idx = 0; fmt->current_node.byte_idx = 0; FMT_PRINT(fmt); fmt->length.char_len = 0; fmt->length.byte_len = 0; DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_node_prepend(Ewl_Text_Fmt *fmt, Ewl_Text_Fmt_Node *node) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); DCHECK_PARAM_PTR("node", node); ecore_dlist_prepend(fmt->nodes, node); /* we prepended so we need to add the nodes lengths to the current * format position length so we're still in the right spot */ fmt->current_node.char_idx += node->char_len; fmt->current_node.byte_idx += node->byte_len; fmt->length.char_len += node->char_len; fmt->length.byte_len += node->byte_len; DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_node_append(Ewl_Text_Fmt *fmt, Ewl_Text_Fmt_Node *node) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); DCHECK_PARAM_PTR("node", node); ecore_dlist_append(fmt->nodes, node); fmt->length.char_len += node->char_len; fmt->length.byte_len += node->byte_len; DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_node_insert(Ewl_Text_Fmt *fmt, Ewl_Text_Fmt_Node *node, unsigned int idx) { Ewl_Text_Fmt_Node *cur_node, *left, *next; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); DCHECK_PARAM_PTR("node", node); cur_node = ecore_dlist_current(fmt->nodes); /* insert at the current location */ if (fmt->current_node.char_idx == idx) ecore_dlist_insert(fmt->nodes, node); /* insert at the end of the current node */ else if ((fmt->current_node.char_idx + cur_node->char_len) == idx) { ecore_dlist_next(fmt->nodes); next = ecore_dlist_current(fmt->nodes); if (next) { ecore_dlist_insert(fmt->nodes, node); /* update our index as we're currently on the next node */ fmt->current_node.char_idx += cur_node->char_len; fmt->current_node.byte_idx += cur_node->byte_len; } else { ecore_dlist_append(fmt->nodes, node); ecore_dlist_goto_last(fmt->nodes); /* we haven't incremented the sizes yet so we're at * the end of the last set of nodes */ fmt->current_node.char_idx = fmt->length.char_len; fmt->current_node.byte_idx = fmt->length.byte_len; FMT_PRINT(fmt); } } else { /* insert into the middle of the current node */ left = ewl_text_fmt_node_new(node->tx, idx - fmt->current_node.char_idx, 0); ewl_text_fmt_char_to_byte(fmt, idx, idx - fmt->current_node.char_idx, NULL, &(left->byte_len)); cur_node->char_len -= left->char_len; cur_node->byte_len -= left->byte_len; /* insert inserts to the right, so move past the current node so we * can insert the two new ones. If we have a node to the left we * insert the left node then the new node so the new one is in the * middle. If there is no node to the left we insert the new node * then the left node. */ ecore_dlist_next(fmt->nodes); next = ecore_dlist_current(fmt->nodes); if (next) { ecore_dlist_insert(fmt->nodes, left); ecore_dlist_insert(fmt->nodes, node); } else { ecore_dlist_append(fmt->nodes, node); ecore_dlist_goto_last(fmt->nodes); ecore_dlist_append(fmt->nodes, left); } fmt->current_node.char_idx += cur_node->char_len; fmt->current_node.byte_idx += cur_node->byte_len; } fmt->length.char_len += node->char_len; fmt->length.byte_len += node->byte_len; DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_node_delete(Ewl_Text_Fmt *fmt, unsigned int idx, unsigned int char_len) { Ewl_Text_Fmt_Node *node; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); node = ecore_dlist_current(fmt->nodes); while (char_len > 0) { unsigned int available; /* how much space is available in this node to be removed */ available = node->char_len - (idx - fmt->current_node.char_idx); if (available > char_len) available = char_len; node->char_len -= available; if (node->char_len > 0) { node->byte_len = 0; ewl_text_fmt_char_to_byte(fmt, idx, node->char_len, NULL, &(node->byte_len)); ecore_dlist_next(fmt->nodes); fmt->current_node.char_idx += node->char_len; fmt->current_node.byte_idx += node->byte_len; node = ecore_dlist_current(fmt->nodes); } else { ecore_dlist_remove(fmt->nodes); ewl_text_fmt_node_free(node); node = ecore_dlist_current(fmt->nodes); } char_len -= available; } /* make sure we've still got a current node. if not reset to the * first node in the list */ node = ecore_dlist_current(fmt->nodes); if (!node) { ecore_dlist_goto_first(fmt->nodes); fmt->current_node.char_idx = 0; fmt->current_node.byte_idx = 0; FMT_PRINT(fmt); } DLEAVE_FUNCTION(DLEVEL_STABLE); } unsigned int ewl_text_fmt_node_count_get(Ewl_Text_Fmt *fmt) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR_RET("fmt", fmt, 0); DRETURN_INT(ecore_dlist_nodes(fmt->nodes), DLEVEL_STABLE); } Ewl_Text_Fmt_Node * ewl_text_fmt_get_last(Ewl_Text_Fmt *fmt) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR_RET("fmt", fmt, NULL); DRETURN_PTR(ecore_dlist_goto_last(fmt->nodes),DLEVEL_STABLE); } Ewl_Text_Fmt_Node * ewl_text_fmt_get_first(Ewl_Text_Fmt *fmt) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR_RET("fmt", fmt, NULL); DRETURN_PTR(ecore_dlist_goto_first(fmt->nodes),DLEVEL_STABLE); } Ewl_Text_Fmt_Node * ewl_text_fmt_get_current(Ewl_Text_Fmt *fmt) { DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR_RET("fmt", fmt, NULL); DRETURN_PTR(ecore_dlist_current(fmt->nodes),DLEVEL_STABLE); } Ewl_Text_Fmt_Node * ewl_text_fmt_get(Ewl_Text_Fmt *fmt, unsigned int idx) { Ewl_Text_Fmt_Node *node, *current_node; unsigned int cur_byte, cur_char; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR_RET("fmt", fmt, NULL); cur_char = fmt->current_node.char_idx; cur_byte = fmt->current_node.byte_idx; current_node = ecore_dlist_current(fmt->nodes); ewl_text_fmt_goto(fmt, idx); node = ecore_dlist_current(fmt->nodes); ecore_dlist_goto(fmt->nodes, current_node); fmt->current_node.char_idx = cur_char; fmt->current_node.byte_idx = cur_byte; FMT_PRINT(fmt); DRETURN_PTR(node, DLEVEL_STABLE); } void ewl_text_fmt_goto(Ewl_Text_Fmt *fmt, unsigned int idx) { Ewl_Text_Fmt_Node *node; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); /* set to start of text */ if (idx == 0) { fmt->current_node.char_idx = 0; fmt->current_node.byte_idx = 0; FMT_PRINT(fmt); ecore_dlist_goto_first(fmt->nodes); } /* set to end of text */ else if (idx >= fmt->length.char_len) { node = ecore_dlist_goto_last(fmt->nodes); fmt->current_node.char_idx = fmt->length.char_len - node->char_len; fmt->current_node.byte_idx = fmt->length.byte_len - node->byte_len; printf("Node len: %d chars %d bytes\n", node->char_len, node->byte_len); FMT_PRINT(fmt); } /* set to after our current position * walk forward until we cover the given position */ else if (fmt->current_node.char_idx < idx) { node = ecore_dlist_current(fmt->nodes); while ((node->char_len + fmt->current_node.char_idx) < idx) { fmt->current_node.char_idx += node->char_len; fmt->current_node.byte_idx += node->byte_len; ecore_dlist_next(fmt->nodes); node = ecore_dlist_current(fmt->nodes); if (!node) break; } } /* set before our position * walk back until we're less then the given position */ else { while (fmt->current_node.char_idx > idx) { ecore_dlist_previous(fmt->nodes); node = ecore_dlist_current(fmt->nodes); if (!node) break; fmt->current_node.char_idx -= node->char_len; fmt->current_node.byte_idx -= node->byte_len; } } node = ecore_dlist_current(fmt->nodes); if (!node) { DWARNING("Unable to place cursor at %d", idx); ecore_dlist_goto_first(fmt->nodes); fmt->current_node.char_idx = 0; fmt->current_node.byte_idx = 0; FMT_PRINT(fmt); } DLEAVE_FUNCTION(DLEVEL_STABLE); } /** * @param t: The text to dump the formatting from * @return Returns no value * @brief Prints out the formatting information for the given text object */ void ewl_text_fmt_dump(Ewl_Text_Fmt *fmt) { Ewl_Text_Fmt_Node *node, *cur_node; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); cur_node = ecore_dlist_current(fmt->nodes); ecore_dlist_goto_first(fmt->nodes); while ((node = ecore_dlist_next(fmt->nodes))) { printf("%d chars, %d bytes\n", node->char_len, node->byte_len); ewl_text_context_print(node->tx, " "); printf("\n\n"); } ecore_dlist_goto(fmt->nodes, cur_node); DLEAVE_FUNCTION(DLEVEL_STABLE); } /* This will determine the number of bytes to get to char_pos in the text * and, if needed will get the number of bytes between char_pos and * char_pos + char_len * * This _HAS_ to leave the list with the same current pointer is when it * started */ void ewl_text_fmt_char_to_byte(Ewl_Text_Fmt *fmt, unsigned int char_idx, unsigned int char_len, unsigned int *byte_idx, unsigned int *byte_len) { Ewl_Text *t; Ewl_Text_Fmt_Node *current_node, *node = NULL; unsigned int cur_char_idx = 0, bidx = 0; void *(*move)(Ecore_DList *list); DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); t = fmt->text; current_node = ecore_dlist_current(fmt->nodes); cur_char_idx = fmt->current_node.char_idx; bidx = fmt->current_node.byte_idx; printf("cur_node %p char_idx %u cur_char_idx %u\n", current_node, char_idx, cur_char_idx); if (cur_char_idx > 10000) ewl_text_fmt_dump(fmt); /* first step is to find the actual node that this text represents */ if (fmt->current_node.char_idx < char_idx) { /* walk forward until we cover the given position */ node = ecore_dlist_current(fmt->nodes); while ((node->char_len + cur_char_idx) < char_idx) { cur_char_idx += node->char_len; bidx += node->byte_len; ecore_dlist_next(fmt->nodes); node = ecore_dlist_current(fmt->nodes); if (!node) break; } move = ecore_dlist_previous; if (!node) { node = ecore_dlist_goto_last(fmt->nodes); fmt->current_node.char_idx = fmt->length.char_len - node->char_len; fmt->current_node.byte_idx = fmt->length.byte_len - node->byte_len; FMT_PRINT(fmt); } } else { /* walk back until we're less then the given position */ while (cur_char_idx > char_idx) { ecore_dlist_previous(fmt->nodes); node = ecore_dlist_current(fmt->nodes); if (!node) break; cur_char_idx -= node->char_len; bidx -= node->byte_len; } move = ecore_dlist_next; if (!node) { node = ecore_dlist_goto_first(fmt->nodes); fmt->current_node.char_idx = 0; fmt->current_node.byte_idx = 0; FMT_PRINT(fmt); } } /* we still need to count within this node */ while (cur_char_idx < char_idx) { unsigned int bytes; ewl_text_text_next_char(t->text + bidx, &bytes); bidx += bytes; cur_char_idx ++; } if (byte_len) { if (char_len == 0) *byte_len = 0; else { char *txt; txt = t->text + bidx; cur_char_idx = 0; while (cur_char_idx < char_len) { unsigned int bytes; txt = ewl_text_text_next_char(txt, &bytes); *byte_len += bytes; cur_char_idx ++; } } } if (byte_idx) *byte_idx = bidx; node = ecore_dlist_current(fmt->nodes); while (node != current_node) { move(fmt->nodes); node = ecore_dlist_current(fmt->nodes); } DLEAVE_FUNCTION(DLEVEL_STABLE); } /* This will determine the number of chars to get to byte_idx in the text * and, if needed will get the number of chars between byte_idx and * byte_idx + byte_len */ void ewl_text_fmt_byte_to_char(Ewl_Text_Fmt *fmt, unsigned int byte_idx, unsigned int byte_len, unsigned int *char_idx, unsigned int *char_len) { Ewl_Text *t; Ewl_Text_Fmt_Node *current_node, *node = NULL; unsigned int byte_count = 0, cidx = 0; void *(*move)(Ecore_DList *list); DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); t = fmt->text; current_node = ecore_dlist_current(fmt->nodes); if (fmt->current_node.byte_idx < byte_idx) { byte_count = fmt->current_node.byte_idx; cidx = fmt->current_node.char_idx; /* walk forward until we cover the given position */ node = ecore_dlist_current(fmt->nodes); while ((node->byte_len + byte_count) < byte_idx) { byte_count += node->byte_len; cidx += node->char_len; ecore_dlist_next(fmt->nodes); node = ecore_dlist_current(fmt->nodes); if (!node) break; } if (!node) { ecore_dlist_goto_last(fmt->nodes); fmt->current_node.char_idx = fmt->length.char_len - node->char_len; fmt->current_node.byte_idx = fmt->length.byte_len - node->byte_len; FMT_PRINT(fmt); } move = ecore_dlist_previous; } else { /* walk back until we're less then the given position */ node = ecore_dlist_current(fmt->nodes); while (byte_count > byte_idx) { ecore_dlist_previous(fmt->nodes); node = ecore_dlist_current(fmt->nodes); if (!node) break; byte_count -= node->byte_len; cidx -= node->char_len; } if (!node) { ecore_dlist_goto_first(fmt->nodes); fmt->current_node.char_idx = 0; fmt->current_node.byte_idx = 0; FMT_PRINT(fmt); } move = ecore_dlist_next; } /* we still need to count within this node */ while (byte_count < byte_idx) { unsigned int bytes; ewl_text_text_next_char(t->text + byte_count, &bytes); byte_count += bytes; cidx ++; } if (char_len) { if (byte_len == 0) *char_len = 0; else { char *txt; txt = t->text + byte_idx; byte_count = 0; while (byte_count < byte_len) { unsigned int bytes; txt = ewl_text_text_next_char(txt, &bytes); byte_count += bytes; (*char_len) ++; } } } if (char_idx) *char_idx = cidx; node = ecore_dlist_current(fmt->nodes); while (node != current_node) { move(fmt->nodes); node = ecore_dlist_current(fmt->nodes); } DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_walk(Ewl_Text_Fmt *fmt, void (*cb)(Ewl_Text_Fmt_Node *, Ewl_Text *, unsigned int byte_idx)) { Ewl_Text_Fmt_Node *current_node, *node; unsigned int byte_idx = 0; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("fmt", fmt); DCHECK_PARAM_PTR("cb", cb); current_node = ecore_dlist_current(fmt->nodes); ecore_dlist_goto_first(fmt->nodes); while ((node = ecore_dlist_next(fmt->nodes))) { cb(node, fmt->text, byte_idx); byte_idx += node->byte_len; } ecore_dlist_goto(fmt->nodes, current_node); DLEAVE_FUNCTION(DLEVEL_STABLE); } void ewl_text_fmt_apply(Ewl_Text_Fmt *fmt, unsigned int context_mask, Ewl_Text_Context *change, unsigned int char_idx, unsigned int char_len) { Ewl_Text *t; Ewl_Text_Fmt_Node *node; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("change", change); if ((char_len == 0) || (context_mask == EWL_TEXT_CONTEXT_MASK_NONE)) DRETURN(DLEVEL_STABLE); t = fmt->text; node = ecore_dlist_current(fmt->nodes); while (char_len > 0) { /* we've walked off the end of the list */ if (!node) break; /* covers entire node */ if ((char_idx == fmt->current_node.char_idx) && (node->char_len <= char_len)) { Ewl_Text_Context *new_tx; new_tx = ewl_text_context_find(node->tx, context_mask, change); ewl_text_context_release(node->tx); node->tx = new_tx; char_idx += node->char_len; char_len -= node->char_len; ecore_dlist_next(fmt->nodes); fmt->current_node.char_idx += node->char_len; fmt->current_node.byte_idx += node->byte_len; } /* start is the same, node is longer then needed */ else if (char_idx == fmt->current_node.char_idx) { unsigned int blen = 0; Ewl_Text_Fmt_Node *new; ewl_text_fmt_char_to_byte(fmt, char_idx, char_len, NULL, &blen); new = ewl_text_fmt_node_new( ewl_text_context_find(node->tx, context_mask, change), char_len, blen); node->char_len -= new->char_len; node->byte_len -= new->byte_len; ecore_dlist_insert(fmt->nodes, new); ecore_dlist_next(fmt->nodes); fmt->current_node.char_idx += new->char_len; fmt->current_node.byte_idx += new->byte_len; char_idx += new->char_len; char_len -= new->char_len; } /* starts are different, need to set context on end of node */ else { Ewl_Text_Fmt_Node *new; unsigned int blen = 0, right_skip; right_skip = char_idx - fmt->current_node.char_idx; ewl_text_fmt_char_to_byte(fmt, fmt->current_node.char_idx, right_skip, NULL, &blen); new = ewl_text_fmt_node_new(node->tx, right_skip, blen); ecore_dlist_insert(fmt->nodes, new); ecore_dlist_next(fmt->nodes); if (!ecore_dlist_current(fmt->nodes)) ecore_dlist_goto_last(fmt->nodes); fmt->current_node.char_idx += new->char_len; fmt->current_node.byte_idx += new->byte_len; /* update node to be the new formatting piece */ node->tx = ewl_text_context_find(node->tx, context_mask, change); node->char_len -= new->char_len; node->byte_len -= new->byte_len; /* the rest of the node is covered */ if (node->char_len <= char_len) { char_len -= node->char_len; char_idx += node->char_len; ecore_dlist_next(fmt->nodes); fmt->current_node.char_idx += node->char_len; fmt->current_node.byte_idx += node->byte_len; } /* we need to split the node */ else { Ewl_Text_Context *tx; unsigned int blen = 0; tx = new->tx; ewl_text_fmt_char_to_byte(fmt, char_idx, char_len, NULL, &blen); /* we create a new node which will be our * formatting and reset node to be original * formatting */ new = ewl_text_fmt_node_new(node->tx, char_len, blen); ecore_dlist_insert(fmt->nodes, new); ecore_dlist_next(fmt->nodes); if (!ecore_dlist_current(fmt->nodes)) ecore_dlist_goto_last(fmt->nodes); fmt->current_node.char_idx += new->char_len; fmt->current_node.byte_idx += new->byte_len; node->tx = tx; node->char_len -= new->char_len; node->byte_len -= new->byte_len; char_len -= new->char_len; char_idx += new->char_len; } } node = ecore_dlist_current(fmt->nodes); } /* make sure we're still pointing at a node */ if (!node) node = ecore_dlist_goto_last(fmt->nodes); DLEAVE_FUNCTION(DLEVEL_STABLE); } /* * Ewl_Text_Fmt_Node stuff */ Ewl_Text_Fmt_Node * ewl_text_fmt_node_new(Ewl_Text_Context *tx, unsigned int char_len, unsigned int byte_len) { Ewl_Text_Fmt_Node *node; DENTER_FUNCTION(DLEVEL_STABLE); node = NEW(Ewl_Text_Fmt_Node, 1); if (!node) DRETURN_PTR(NULL, DLEVEL_STABLE); node->tx = tx; ewl_text_context_acquire(node->tx); node->char_len = char_len; node->byte_len = byte_len; DRETURN_PTR(node, DLEVEL_STABLE); } static void ewl_text_fmt_node_free(void *node) { Ewl_Text_Fmt_Node *n; DENTER_FUNCTION(DLEVEL_STABLE); DCHECK_PARAM_PTR("node", node); n = node; if (n->tx) ewl_text_context_release(n->tx); n->tx = NULL; FREE(node); DLEAVE_FUNCTION(DLEVEL_STABLE); }