X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextbtree.c;h=81038f427a8bafceba0625298874306e29628a6c;hb=86f27564bd952386b2271dcc8de78a849e66fc73;hp=3f0f7c2c2ef7e37ea05310cea3e456e0cddf2450;hpb=5190c7397c407f07d14989d32e711131aa57d39b;p=~andy%2Fgtk diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c index 3f0f7c2c2..81038f427 100644 --- a/gtk/gtktextbtree.c +++ b/gtk/gtktextbtree.c @@ -1,5 +1,5 @@ /* - * gtktextbtree.c -- + * Gtktextbtree.c -- * * This file contains code that manages the B-tree representation * of text for the text buffer and implements character and @@ -52,18 +52,19 @@ * */ +#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API +#include #include "gtktextbtree.h" #include -#include #include #include -#include "gtksignal.h" #include "gtktexttag.h" #include "gtktexttagtable.h" #include "gtktextlayout.h" #include "gtktextiterprivate.h" #include "gtkdebug.h" #include "gtktextmarkprivate.h" +#include "gtkalias.h" /* * Types @@ -72,7 +73,7 @@ /* * The structure below is used to pass information between - * gtk_text_btree_get_tags and inc_count: + * _gtk_text_btree_get_tags and inc_count: */ typedef struct TagInfo { @@ -100,10 +101,15 @@ struct _NodeData { /* Height and width of this node */ gint height; - gint width : 24; + signed int width : 24; - /* boolean indicating whether the height/width need to be recomputed */ - gint valid : 8; + /* boolean indicating whether the lines below this node are in need of validation. + * However, width/height should always represent the current total width and + * max height for lines below this node; the valid flag indicates whether the + * width/height on the lines needs recomputing, not whether the totals + * need recomputing. + */ + guint valid : 8; /* Actually a boolean */ }; @@ -177,24 +183,36 @@ struct _GtkTextBTree { GtkTextBuffer *buffer; BTreeView *views; GSList *tag_infos; - guint tag_changed_handler; - guint tag_removed_handler; + gulong tag_changed_handler; + /* Incremented when a segment with a byte size > 0 - is added to or removed from the tree (i.e. the - length of a line may have changed, and lines may - have been added or removed). This invalidates - all outstanding iterators. - */ + * is added to or removed from the tree (i.e. the + * length of a line may have changed, and lines may + * have been added or removed). This invalidates + * all outstanding iterators. + */ guint chars_changed_stamp; /* Incremented when any segments are added or deleted; - this makes outstanding iterators recalculate their - pointed-to segment and segment offset. - */ + * this makes outstanding iterators recalculate their + * pointed-to segment and segment offset. + */ guint segments_changed_stamp; - GtkTextLine *end_iter_line; + /* Cache the last line in the buffer */ + GtkTextLine *last_line; + guint last_line_stamp; + /* Cache the next-to-last line in the buffer, + * containing the end iterator + */ + GtkTextLine *end_iter_line; + GtkTextLineSegment *end_iter_segment; + int end_iter_segment_byte_index; + int end_iter_segment_char_offset; guint end_iter_line_stamp; + guint end_iter_segment_stamp; + + GHashTable *child_anchor_table; }; @@ -278,6 +296,8 @@ static void gtk_text_btree_node_remove_view (BTreeView gpointer view_id); static void gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node); +static void gtk_text_btree_node_free_empty (GtkTextBTree *tree, + GtkTextBTreeNode *node); static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id); static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, @@ -295,9 +315,6 @@ static void tag_changed_cb (GtkTextTagTable *table, GtkTextTag *tag, gboolean size_changed, GtkTextBTree *tree); -static void tag_removed_cb (GtkTextTagTable *table, - GtkTextTag *tag, - GtkTextBTree *tree); static void cleanup_line (GtkTextLine *line); static void recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node); @@ -323,7 +340,8 @@ static void gtk_text_btree_remove_tag_info (GtkTextBTree *tre static void redisplay_region (GtkTextBTree *tree, const GtkTextIter *start, - const GtkTextIter *end); + const GtkTextIter *end, + gboolean cursors_only); /* Inline thingies */ @@ -344,8 +362,8 @@ chars_changed (GtkTextBTree *tree) */ GtkTextBTree* -gtk_text_btree_new (GtkTextTagTable *table, - GtkTextBuffer *buffer) +_gtk_text_btree_new (GtkTextTagTable *table, + GtkTextBuffer *buffer) { GtkTextBTree *tree; GtkTextBTreeNode *root_node; @@ -400,24 +418,25 @@ gtk_text_btree_new (GtkTextTagTable *table, tree->chars_changed_stamp = g_random_int (); tree->segments_changed_stamp = g_random_int (); + tree->last_line_stamp = tree->chars_changed_stamp - 1; + tree->last_line = NULL; + tree->end_iter_line_stamp = tree->chars_changed_stamp - 1; + tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1; tree->end_iter_line = NULL; + tree->end_iter_segment_byte_index = 0; + tree->end_iter_segment_char_offset = 0; + + g_object_ref (tree->table); - gtk_object_ref (GTK_OBJECT (tree->table)); - gtk_object_sink (GTK_OBJECT (tree->table)); - - tree->tag_changed_handler = gtk_signal_connect (GTK_OBJECT (tree->table), - "tag_changed", - GTK_SIGNAL_FUNC (tag_changed_cb), - tree); - - tree->tag_removed_handler = gtk_signal_connect (GTK_OBJECT (tree->table), - "tag_removed", - GTK_SIGNAL_FUNC (tag_removed_cb), - tree); + tree->tag_changed_handler = g_signal_connect (tree->table, + "tag_changed", + G_CALLBACK (tag_changed_cb), + tree); tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal); - + tree->child_anchor_table = NULL; + /* We don't ref the buffer, since the buffer owns us; * we'd have some circularity issues. The buffer always * lasts longer than the BTree @@ -428,10 +447,10 @@ gtk_text_btree_new (GtkTextTagTable *table, GtkTextIter start; GtkTextLineSegment *seg; - gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0); + _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0); - tree->insert_mark = gtk_text_btree_set_mark (tree, + tree->insert_mark = _gtk_text_btree_set_mark (tree, NULL, "insert", FALSE, @@ -443,7 +462,7 @@ gtk_text_btree_new (GtkTextTagTable *table, seg->body.mark.not_deleteable = TRUE; seg->body.mark.visible = TRUE; - tree->selection_bound_mark = gtk_text_btree_set_mark (tree, + tree->selection_bound_mark = _gtk_text_btree_set_mark (tree, NULL, "selection_bound", FALSE, @@ -454,8 +473,8 @@ gtk_text_btree_new (GtkTextTagTable *table, seg->body.mark.not_deleteable = TRUE; - g_object_ref (G_OBJECT (tree->insert_mark)); - g_object_ref (G_OBJECT (tree->selection_bound_mark)); + g_object_ref (tree->insert_mark); + g_object_ref (tree->selection_bound_mark); } tree->refcount = 1; @@ -464,7 +483,7 @@ gtk_text_btree_new (GtkTextTagTable *table, } void -gtk_text_btree_ref (GtkTextBTree *tree) +_gtk_text_btree_ref (GtkTextBTree *tree) { g_return_if_fail (tree != NULL); g_return_if_fail (tree->refcount > 0); @@ -472,18 +491,8 @@ gtk_text_btree_ref (GtkTextBTree *tree) tree->refcount += 1; } -static void -mark_destroy_foreach (gpointer key, gpointer value, gpointer user_data) -{ - GtkTextLineSegment *seg = value; - - g_return_if_fail (seg->body.mark.tree == NULL); - - g_object_unref (G_OBJECT (seg->body.mark.obj)); -} - void -gtk_text_btree_unref (GtkTextBTree *tree) +_gtk_text_btree_unref (GtkTextBTree *tree) { g_return_if_fail (tree != NULL); g_return_if_fail (tree->refcount > 0); @@ -491,49 +500,54 @@ gtk_text_btree_unref (GtkTextBTree *tree) tree->refcount -= 1; if (tree->refcount == 0) - { - gtk_text_btree_node_destroy (tree, tree->root_node); + { + g_signal_handler_disconnect (tree->table, + tree->tag_changed_handler); - g_hash_table_foreach (tree->mark_table, - mark_destroy_foreach, - NULL); + g_object_unref (tree->table); + tree->table = NULL; + + gtk_text_btree_node_destroy (tree, tree->root_node); + tree->root_node = NULL; + + g_assert (g_hash_table_size (tree->mark_table) == 0); g_hash_table_destroy (tree->mark_table); - - g_object_unref (G_OBJECT (tree->insert_mark)); - g_object_unref (G_OBJECT (tree->selection_bound_mark)); - - gtk_signal_disconnect (GTK_OBJECT (tree->table), - tree->tag_changed_handler); - - gtk_signal_disconnect (GTK_OBJECT (tree->table), - tree->tag_removed_handler); - - gtk_object_unref (GTK_OBJECT (tree->table)); + tree->mark_table = NULL; + if (tree->child_anchor_table != NULL) + { + g_hash_table_destroy (tree->child_anchor_table); + tree->child_anchor_table = NULL; + } + + g_object_unref (tree->insert_mark); + tree->insert_mark = NULL; + g_object_unref (tree->selection_bound_mark); + tree->selection_bound_mark = NULL; g_free (tree); } } GtkTextBuffer* -gtk_text_btree_get_buffer (GtkTextBTree *tree) +_gtk_text_btree_get_buffer (GtkTextBTree *tree) { return tree->buffer; } guint -gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree) +_gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree) { return tree->chars_changed_stamp; } guint -gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree) +_gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree) { return tree->segments_changed_stamp; } void -gtk_text_btree_segments_changed (GtkTextBTree *tree) +_gtk_text_btree_segments_changed (GtkTextBTree *tree) { g_return_if_fail (tree != NULL); segments_changed (tree); @@ -543,97 +557,213 @@ gtk_text_btree_segments_changed (GtkTextBTree *tree) * Indexable segment mutation */ -void -gtk_text_btree_delete (GtkTextIter *start, - GtkTextIter *end) +/* + * The following function is responsible for resolving the bidi direction + * for the lines between start and end. But it also calculates any + * dependent bidi direction for surrounding lines that change as a result + * of the bidi direction decisions within the range. The function is + * trying to do as little propagation as is needed. + */ +static void +gtk_text_btree_resolve_bidi (GtkTextIter *start, + GtkTextIter *end) { - GtkTextLineSegment *prev_seg; /* The segment just before the start - * of the deletion range. */ - GtkTextLineSegment *last_seg; /* The segment just after the end - * of the deletion range. */ - GtkTextLineSegment *seg, *next; - GtkTextLine *curline; - GtkTextBTreeNode *curnode, *node; - GtkTextBTree *tree; - GtkTextLine *start_line; - GtkTextLine *end_line; - GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */ - gint start_byte_offset; + GtkTextBTree *tree = _gtk_text_iter_get_btree (start); + GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line; + PangoDirection last_strong, dir_above_propagated, dir_below_propagated; - g_return_if_fail (start != NULL); - g_return_if_fail (end != NULL); - g_return_if_fail (gtk_text_iter_get_btree (start) == - gtk_text_iter_get_btree (end)); + /* Resolve the strong bidi direction for all lines between + * start and end. + */ + start_line = _gtk_text_iter_get_text_line (start); + start_line_prev = _gtk_text_line_previous (start_line); + end_line = _gtk_text_iter_get_text_line (end); + end_line_next = _gtk_text_line_next (end_line); + + line = start_line; + while (line && line != end_line_next) + { + /* Loop through the segments and search for a strong character + */ + GtkTextLineSegment *seg = line->segments; + line->dir_strong = PANGO_DIRECTION_NEUTRAL; + + while (seg) + { + if (seg->type == >k_text_char_type && seg->byte_count > 0) + { + PangoDirection pango_dir; - gtk_text_iter_reorder (start, end); + pango_dir = pango_find_base_dir (seg->body.chars, + seg->byte_count); + + if (pango_dir != PANGO_DIRECTION_NEUTRAL) + { + line->dir_strong = pango_dir; + break; + } + } + seg = seg->next; + } - tree = gtk_text_iter_get_btree (start); + line = _gtk_text_line_next (line); + } - { - /* - * The code below is ugly, but it's needed to make sure there - * is always a dummy empty line at the end of the text. If the - * final newline of the file (just before the dummy line) is being - * deleted, then back up index to just before the newline. If - * there is a newline just before the first character being deleted, - * then back up the first index too, so that an even number of lines - * gets deleted. Furthermore, remove any tags that are present on - * the newline that isn't going to be deleted after all (this simulates - * deleting the newline and then adding a "clean" one back again). - */ + /* Sweep forward */ - gint line1; - gint line2; + /* The variable dir_above_propagated contains the forward propagated + * direction before start. It is neutral if start is in the beginning + * of the buffer. + */ + dir_above_propagated = PANGO_DIRECTION_NEUTRAL; + if (start_line_prev) + dir_above_propagated = start_line_prev->dir_propagated_forward; - line1 = gtk_text_iter_get_line (start); - line2 = gtk_text_iter_get_line (end); + /* Loop forward and propagate the direction of each paragraph + * to all neutral lines. + */ + line = start_line; + last_strong = dir_above_propagated; + while (line != end_line_next) + { + if (line->dir_strong != PANGO_DIRECTION_NEUTRAL) + last_strong = line->dir_strong; + + line->dir_propagated_forward = last_strong; + + line = _gtk_text_line_next (line); + } - if (line2 == gtk_text_btree_line_count (tree)) + /* Continue propagating as long as the previous resolved forward + * is different from last_strong. + */ + { + GtkTextIter end_propagate; + + while (line && + line->dir_strong == PANGO_DIRECTION_NEUTRAL && + line->dir_propagated_forward != last_strong) { - GtkTextTag** tags; - int array_size; - GtkTextIter orig_end; + GtkTextLine *prev = line; + line->dir_propagated_forward = last_strong; + + line = _gtk_text_line_next(line); + if (!line) + { + line = prev; + break; + } + } - orig_end = *end; - gtk_text_iter_prev_char (end); + /* The last line to invalidate is the last line before the + * line with the strong character. Or in case of the end of the + * buffer, the last line of the buffer. (There seems to be an + * extra "virtual" last line in the buffer that must not be used + * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the + * _gtk_text_line_previous is ok in that case as well.) + */ + line = _gtk_text_line_previous (line); + _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0); + _gtk_text_btree_invalidate_region (tree, end, &end_propagate, FALSE); + } + + /* Sweep backward */ - --line2; + /* The variable dir_below_propagated contains the backward propagated + * direction after end. It is neutral if end is at the end of + * the buffer. + */ + dir_below_propagated = PANGO_DIRECTION_NEUTRAL; + if (end_line_next) + dir_below_propagated = end_line_next->dir_propagated_back; - if (gtk_text_iter_get_line_offset (start) == 0 && - line1 != 0) - { - gtk_text_iter_prev_char (start); - --line1; - } + /* Loop backward and propagate the direction of each paragraph + * to all neutral lines. + */ + line = end_line; + last_strong = dir_below_propagated; + while (line != start_line_prev) + { + if (line->dir_strong != PANGO_DIRECTION_NEUTRAL) + last_strong = line->dir_strong; - tags = gtk_text_btree_get_tags (end, - &array_size); + line->dir_propagated_back = last_strong; - if (tags != NULL) - { - int i; + line = _gtk_text_line_previous (line); + } - i = 0; - while (i < array_size) - { - gtk_text_btree_tag (end, &orig_end, tags[i], FALSE); + /* Continue propagating as long as the resolved backward dir + * is different from last_strong. + */ + { + GtkTextIter start_propagate; - ++i; - } + while (line && + line->dir_strong == PANGO_DIRECTION_NEUTRAL && + line->dir_propagated_back != last_strong) + { + GtkTextLine *prev = line; + line->dir_propagated_back = last_strong; - g_free (tags); + line = _gtk_text_line_previous (line); + if (!line) + { + line = prev; + break; } } + + /* We only need to invalidate for backwards propagation if the + * line we ended up on didn't get a direction from forwards + * propagation. + */ + if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL) + { + _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0); + _gtk_text_btree_invalidate_region (tree, &start_propagate, start, FALSE); + } } +} + +void +_gtk_text_btree_delete (GtkTextIter *start, + GtkTextIter *end) +{ + GtkTextLineSegment *prev_seg; /* The segment just before the start + * of the deletion range. */ + GtkTextLineSegment *last_seg; /* The segment just after the end + * of the deletion range. */ + GtkTextLineSegment *seg, *next, *next2; + GtkTextLine *curline; + GtkTextBTreeNode *curnode, *node; + GtkTextBTree *tree; + GtkTextLine *start_line; + GtkTextLine *end_line; + GtkTextLine *line; + GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */ + gint start_byte_offset; + + g_return_if_fail (start != NULL); + g_return_if_fail (end != NULL); + g_return_if_fail (_gtk_text_iter_get_btree (start) == + _gtk_text_iter_get_btree (end)); + + gtk_text_iter_order (start, end); + tree = _gtk_text_iter_get_btree (start); + + if (gtk_debug_flags & GTK_DEBUG_TEXT) + _gtk_text_btree_check (tree); + /* Broadcast the need for redisplay before we break the iterators */ - gtk_text_btree_invalidate_region (tree, start, end); + DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC)); + _gtk_text_btree_invalidate_region (tree, start, end, FALSE); /* Save the byte offset so we can reset the iterators */ start_byte_offset = gtk_text_iter_get_line_index (start); - start_line = gtk_text_iter_get_text_line (start); - end_line = gtk_text_iter_get_text_line (end); + start_line = _gtk_text_iter_get_text_line (start); + end_line = _gtk_text_iter_get_text_line (end); /* * Split the start and end segments, so we have a place @@ -686,7 +816,7 @@ gtk_text_btree_delete (GtkTextIter *start, * (unless it's the starting line for the range). */ - nextline = gtk_text_line_next (curline); + nextline = _gtk_text_line_next (curline); if (curline != start_line) { if (curnode == start_line->parent) @@ -697,6 +827,9 @@ gtk_text_btree_delete (GtkTextIter *start, for (node = curnode; node != NULL; node = node->parent) { + /* Don't update node->num_chars, because + * that was done when we deleted the segments. + */ node->num_lines -= 1; } @@ -732,7 +865,7 @@ gtk_text_btree_delete (GtkTextIter *start, prevnode->next = curnode->next; } parent->num_children--; - g_free (curnode); + gtk_text_btree_node_free_empty (tree, curnode); curnode = parent; } curnode = curline->parent; @@ -742,7 +875,7 @@ gtk_text_btree_delete (GtkTextIter *start, next = seg->next; char_count = seg->char_count; - if ((*seg->type->deleteFunc)(seg, curline, 0) != 0) + if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0) { /* * This segment refuses to die. Move it to prev_seg and @@ -754,12 +887,30 @@ gtk_text_btree_delete (GtkTextIter *start, seg->next = start_line->segments; start_line->segments = seg; } - else - { + else if (prev_seg->next && + prev_seg->next != last_seg && + seg->type == >k_text_toggle_off_type && + prev_seg->next->type == >k_text_toggle_on_type && + seg->body.toggle.info == prev_seg->next->body.toggle.info) + { + /* Try to match an off toggle with the matching on toggle + * if it immediately follows. This is a common case, and + * handling it here prevents quadratic blowup in + * cleanup_line() below. See bug 317125. + */ + next2 = prev_seg->next->next; + g_free ((char *)prev_seg->next); + prev_seg->next = next2; + g_free ((char *)seg); + seg = NULL; + } + else + { seg->next = prev_seg->next; prev_seg->next = seg; } - if (seg->type->leftGravity) + + if (seg && seg->type->leftGravity) { prev_seg = seg; } @@ -787,21 +938,32 @@ gtk_text_btree_delete (GtkTextIter *start, { BTreeView *view; GtkTextBTreeNode *ancestor_node; - GtkTextLine *prevline; + int chars_moved; + /* last_seg was appended to start_line up at the top of this function */ + chars_moved = 0; for (seg = last_seg; seg != NULL; seg = seg->next) { + chars_moved += seg->char_count; if (seg->type->lineChangeFunc != NULL) { (*seg->type->lineChangeFunc)(seg, end_line); } } + + for (node = start_line->parent; node != NULL; + node = node->parent) + { + node->num_chars += chars_moved; + } + curnode = end_line->parent; for (node = curnode; node != NULL; node = node->parent) { + node->num_chars -= chars_moved; node->num_lines--; } curnode->num_children--; @@ -829,7 +991,6 @@ gtk_text_btree_delete (GtkTextIter *start, view = tree->views; while (view) { - GtkTextLine *line; GtkTextLineData *ld; gint deleted_width = 0; @@ -839,7 +1000,7 @@ gtk_text_btree_delete (GtkTextIter *start, while (line) { GtkTextLine *next_line = line->next; - ld = gtk_text_line_get_data (line, view->view_id); + ld = _gtk_text_line_get_data (line, view->view_id); if (ld) { @@ -847,24 +1008,27 @@ gtk_text_btree_delete (GtkTextIter *start, deleted_height += ld->height; } - if (!view->next) - gtk_text_line_destroy (tree, line); - line = next_line; } if (deleted_width > 0 || deleted_height > 0) { - ld = gtk_text_line_get_data (start_line, view->view_id); - - /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently - * no way to add ld without also validating the node, which would - * be improper at this point. - */ - /* This assertion does actually fail sometimes, must - fix before stable release -hp */ - g_assert (ld); - + ld = _gtk_text_line_get_data (start_line, view->view_id); + + if (ld == NULL) + { + /* This means that start_line has never been validated. + * We don't really want to do the validation here but + * we do need to store our temporary sizes. So we + * create the line data and assume a line w/h of 0. + */ + ld = _gtk_text_line_data_new (view->layout, start_line); + _gtk_text_line_add_data (start_line, ld); + ld->width = 0; + ld->height = 0; + ld->valid = FALSE; + } + ld->width = MAX (deleted_width, ld->width); ld->height += deleted_height; ld->valid = FALSE; @@ -877,6 +1041,19 @@ gtk_text_btree_delete (GtkTextIter *start, view = view->next; } + line = deleted_lines; + while (line) + { + GtkTextLine *next_line = line->next; + + gtk_text_line_destroy (tree, line); + + line = next_line; + } + + /* avoid dangling pointer */ + deleted_lines = NULL; + gtk_text_btree_rebalance (tree, curnode); } @@ -898,17 +1075,19 @@ gtk_text_btree_delete (GtkTextIter *start, segments_changed (tree); if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); /* Re-initialize our iterators */ - gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset); + _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset); *end = *start; + + gtk_text_btree_resolve_bidi (start, end); } void -gtk_text_btree_insert (GtkTextIter *iter, - const gchar *text, - gint len) +_gtk_text_btree_insert (GtkTextIter *iter, + const gchar *text, + gint len) { GtkTextLineSegment *prev_seg; /* The segment just before the first * new segment (NULL means new segment @@ -921,12 +1100,15 @@ gtk_text_btree_insert (GtkTextIter *iter, * added to this line). */ GtkTextLineSegment *seg; GtkTextLine *newline; - int chunkSize; /* # characters in current chunk. */ - guint sol; /* start of line */ - guint eol; /* Pointer to character just after last - * one in current chunk. */ + int chunk_len; /* # characters in current chunk. */ + gint sol; /* start of line */ + gint eol; /* Pointer to character just after last + * one in current chunk. + */ + gint delim; /* index of paragraph delimiter */ int line_count_delta; /* Counts change to total number of - * lines in file. */ + * lines in file. + */ int char_count_delta; /* change to number of chars */ GtkTextBTree *tree; @@ -940,19 +1122,24 @@ gtk_text_btree_insert (GtkTextIter *iter, len = strlen (text); /* extract iterator info */ - tree = gtk_text_iter_get_btree (iter); - line = gtk_text_iter_get_text_line (iter); + tree = _gtk_text_iter_get_btree (iter); + line = _gtk_text_iter_get_text_line (iter); + start_line = line; start_byte_index = gtk_text_iter_get_line_index (iter); - /* Get our insertion segment split */ + /* Get our insertion segment split. Note this assumes line allows + * char insertions, which isn't true of the "last" line. But iter + * should not be on that line, as we assert here. + */ + g_assert (!_gtk_text_line_is_last (line, tree)); prev_seg = gtk_text_line_segment_split (iter); cur_seg = prev_seg; /* Invalidate all iterators */ chars_changed (tree); segments_changed (tree); - + /* * Chop the text up into lines and create a new segment for * each line, plus a new line for the leftovers from the @@ -965,17 +1152,27 @@ gtk_text_btree_insert (GtkTextIter *iter, char_count_delta = 0; while (eol < len) { - for (; eol < len; eol++) - { - if (text[eol] == '\n') - { - eol++; - break; - } - } - chunkSize = eol - sol; - - seg = _gtk_char_segment_new (&text[sol], chunkSize); + sol = eol; + + pango_find_paragraph_boundary (text + sol, + len - sol, + &delim, + &eol); + + /* make these relative to the start of the text */ + delim += sol; + eol += sol; + + g_assert (eol >= sol); + g_assert (delim >= sol); + g_assert (eol >= delim); + g_assert (sol >= 0); + g_assert (eol <= len); + + chunk_len = eol - sol; + + g_assert (g_utf8_validate (&text[sol], chunk_len, NULL)); + seg = _gtk_char_segment_new (&text[sol], chunk_len); char_count_delta += seg->char_count; @@ -990,8 +1187,10 @@ gtk_text_btree_insert (GtkTextIter *iter, cur_seg->next = seg; } - if (text[eol-1] != '\n') + if (delim == eol) { + /* chunk didn't end with a paragraph separator */ + g_assert (eol == len); break; } @@ -1009,8 +1208,6 @@ gtk_text_btree_insert (GtkTextIter *iter, line = newline; cur_seg = NULL; line_count_delta++; - - sol = eol; } /* @@ -1033,10 +1230,10 @@ gtk_text_btree_insert (GtkTextIter *iter, GtkTextIter end; - gtk_text_btree_get_iter_at_line (tree, - &start, - start_line, - start_byte_index); + _gtk_text_btree_get_iter_at_line (tree, + &start, + start_line, + start_byte_index); end = start; /* We could almost certainly be more efficient here @@ -1044,32 +1241,32 @@ gtk_text_btree_insert (GtkTextIter *iter, above. FIXME */ gtk_text_iter_forward_chars (&end, char_count_delta); - gtk_text_btree_invalidate_region (tree, - &start, &end); + DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC)); + _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE); /* Convenience for the user */ *iter = end; + + gtk_text_btree_resolve_bidi (&start, &end); } } -void -gtk_text_btree_insert_pixbuf (GtkTextIter *iter, - GdkPixbuf *pixbuf) +static void +insert_pixbuf_or_widget_segment (GtkTextIter *iter, + GtkTextLineSegment *seg) + { - GtkTextLineSegment *seg; GtkTextIter start; GtkTextLineSegment *prevPtr; GtkTextLine *line; GtkTextBTree *tree; gint start_byte_offset; - line = gtk_text_iter_get_text_line (iter); - tree = gtk_text_iter_get_btree (iter); + line = _gtk_text_iter_get_text_line (iter); + tree = _gtk_text_iter_get_btree (iter); start_byte_offset = gtk_text_iter_get_line_index (iter); - seg = _gtk_pixbuf_segment_new (pixbuf); - prevPtr = gtk_text_line_segment_split (iter); if (prevPtr == NULL) { @@ -1089,14 +1286,64 @@ gtk_text_btree_insert_pixbuf (GtkTextIter *iter, /* reset *iter for the user, and invalidate tree nodes */ - gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset); + _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset); *iter = start; - gtk_text_iter_next_char (iter); /* skip forward past the pixmap */ + gtk_text_iter_forward_char (iter); /* skip forward past the segment */ - gtk_text_btree_invalidate_region (tree, &start, iter); + DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC)); + _gtk_text_btree_invalidate_region (tree, &start, iter, FALSE); } + +void +_gtk_text_btree_insert_pixbuf (GtkTextIter *iter, + GdkPixbuf *pixbuf) +{ + GtkTextLineSegment *seg; + + seg = _gtk_pixbuf_segment_new (pixbuf); + insert_pixbuf_or_widget_segment (iter, seg); +} + +void +_gtk_text_btree_insert_child_anchor (GtkTextIter *iter, + GtkTextChildAnchor *anchor) +{ + GtkTextLineSegment *seg; + GtkTextBTree *tree; + + if (anchor->segment != NULL) + { + g_warning (G_STRLOC": Same child anchor can't be inserted twice"); + return; + } + + seg = _gtk_widget_segment_new (anchor); + + tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter); + seg->body.child.line = _gtk_text_iter_get_text_line (iter); + + insert_pixbuf_or_widget_segment (iter, seg); + + if (tree->child_anchor_table == NULL) + tree->child_anchor_table = g_hash_table_new (NULL, NULL); + + g_hash_table_insert (tree->child_anchor_table, + seg->body.child.obj, + seg->body.child.obj); +} + +void +_gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor) +{ + GtkTextLineSegment *seg; + + seg = anchor->segment; + + g_hash_table_remove (seg->body.child.tree->child_anchor_table, + anchor); +} /* * View stuff @@ -1110,7 +1357,7 @@ find_line_by_y (GtkTextBTree *tree, BTreeView *view, gint current_y = 0; if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); if (node->level == 0) { @@ -1122,7 +1369,7 @@ find_line_by_y (GtkTextBTree *tree, BTreeView *view, { GtkTextLineData *ld; - ld = gtk_text_line_get_data (line, view->view_id); + ld = _gtk_text_line_get_data (line, view->view_id); if (ld) { @@ -1167,10 +1414,10 @@ find_line_by_y (GtkTextBTree *tree, BTreeView *view, } GtkTextLine * -gtk_text_btree_find_line_by_y (GtkTextBTree *tree, - gpointer view_id, - gint ypixel, - gint *line_top_out) +_gtk_text_btree_find_line_by_y (GtkTextBTree *tree, + gpointer view_id, + gint ypixel, + gint *line_top_out) { GtkTextLine *line; BTreeView *view; @@ -1205,7 +1452,7 @@ find_line_top_in_line_list (GtkTextBTree *tree, if (line == target_line) return y; - ld = gtk_text_line_get_data (line, view->view_id); + ld = _gtk_text_line_get_data (line, view->view_id); if (ld) y += ld->height; @@ -1219,7 +1466,7 @@ find_line_top_in_line_list (GtkTextBTree *tree, } gint -gtk_text_btree_find_line_top (GtkTextBTree *tree, +_gtk_text_btree_find_line_top (GtkTextBTree *tree, GtkTextLine *target_line, gpointer view_id) { @@ -1290,7 +1537,7 @@ gtk_text_btree_find_line_top (GtkTextBTree *tree, } void -gtk_text_btree_add_view (GtkTextBTree *tree, +_gtk_text_btree_add_view (GtkTextBTree *tree, GtkTextLayout *layout) { BTreeView *view; @@ -1298,7 +1545,7 @@ gtk_text_btree_add_view (GtkTextBTree *tree, GtkTextLineData *line_data; g_return_if_fail (tree != NULL); - + view = g_new (BTreeView, 1); view->view_id = layout; @@ -1307,6 +1554,12 @@ gtk_text_btree_add_view (GtkTextBTree *tree, view->next = tree->views; view->prev = NULL; + if (tree->views) + { + g_assert (tree->views->prev == NULL); + tree->views->prev = view; + } + tree->views = view; /* The last line in the buffer has identity values for the per-view @@ -1322,19 +1575,19 @@ gtk_text_btree_add_view (GtkTextBTree *tree, line_data->height = 0; line_data->valid = TRUE; - gtk_text_line_add_data (last_line, line_data); + _gtk_text_line_add_data (last_line, line_data); } void -gtk_text_btree_remove_view (GtkTextBTree *tree, - gpointer view_id) +_gtk_text_btree_remove_view (GtkTextBTree *tree, + gpointer view_id) { BTreeView *view; GtkTextLine *last_line; GtkTextLineData *line_data; g_return_if_fail (tree != NULL); - + view = tree->views; while (view != NULL) @@ -1360,18 +1613,22 @@ gtk_text_btree_remove_view (GtkTextBTree *tree, * (Do this first, so that we don't try to call the view's line data destructor on it.) */ last_line = get_last_line (tree); - line_data = gtk_text_line_remove_data (last_line, view_id); + line_data = _gtk_text_line_remove_data (last_line, view_id); g_free (line_data); gtk_text_btree_node_remove_view (view, tree->root_node, view_id); + view->layout = (gpointer) 0xdeadbeef; + view->view_id = (gpointer) 0xdeadbeef; + g_free (view); } void -gtk_text_btree_invalidate_region (GtkTextBTree *tree, - const GtkTextIter *start, - const GtkTextIter *end) +_gtk_text_btree_invalidate_region (GtkTextBTree *tree, + const GtkTextIter *start, + const GtkTextIter *end, + gboolean cursors_only) { BTreeView *view; @@ -1379,14 +1636,17 @@ gtk_text_btree_invalidate_region (GtkTextBTree *tree, while (view != NULL) { - gtk_text_layout_invalidate (view->layout, start, end); + if (cursors_only) + gtk_text_layout_invalidate_cursors (view->layout, start, end); + else + gtk_text_layout_invalidate (view->layout, start, end); view = view->next; } } void -gtk_text_btree_get_view_size (GtkTextBTree *tree, +_gtk_text_btree_get_view_size (GtkTextBTree *tree, gpointer view_id, gint *width, gint *height) @@ -1412,7 +1672,7 @@ static IterStack* iter_stack_new (void) { IterStack *stack; - stack = g_new (IterStack, 1); + stack = g_slice_new (IterStack); stack->iters = NULL; stack->count = 0; stack->alloced = 0; @@ -1420,20 +1680,22 @@ iter_stack_new (void) } static void -iter_stack_push (IterStack *stack, const GtkTextIter *iter) +iter_stack_push (IterStack *stack, + const GtkTextIter *iter) { stack->count += 1; if (stack->count > stack->alloced) { stack->alloced = stack->count*2; stack->iters = g_realloc (stack->iters, - stack->alloced*sizeof (GtkTextIter)); + stack->alloced * sizeof (GtkTextIter)); } stack->iters[stack->count-1] = *iter; } static gboolean -iter_stack_pop (IterStack *stack, GtkTextIter *iter) +iter_stack_pop (IterStack *stack, + GtkTextIter *iter) { if (stack->count == 0) return FALSE; @@ -1449,7 +1711,7 @@ static void iter_stack_free (IterStack *stack) { g_free (stack->iters); - g_free (stack); + g_slice_free (IterStack, stack); } static void @@ -1479,25 +1741,25 @@ queue_tag_redisplay (GtkTextBTree *tree, const GtkTextIter *start, const GtkTextIter *end) { - if (gtk_text_tag_affects_size (tag)) + if (_gtk_text_tag_affects_size (tag)) { - gtk_text_btree_invalidate_region (tree, start, end); - + DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC)); + _gtk_text_btree_invalidate_region (tree, start, end, FALSE); } - else if (gtk_text_tag_affects_nonsize_appearance (tag)) + else if (_gtk_text_tag_affects_nonsize_appearance (tag)) { /* We only need to queue a redraw, not a relayout */ - redisplay_region (tree, start, end); + redisplay_region (tree, start, end, FALSE); } /* We don't need to do anything if the tag doesn't affect display */ } void -gtk_text_btree_tag (const GtkTextIter *start_orig, - const GtkTextIter *end_orig, - GtkTextTag *tag, - gboolean add) +_gtk_text_btree_tag (const GtkTextIter *start_orig, + const GtkTextIter *end_orig, + GtkTextTag *tag, + gboolean add) { GtkTextLineSegment *seg, *prev; GtkTextLine *cleanupline; @@ -1513,9 +1775,10 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, g_return_if_fail (start_orig != NULL); g_return_if_fail (end_orig != NULL); g_return_if_fail (GTK_IS_TEXT_TAG (tag)); - g_return_if_fail (gtk_text_iter_get_btree (start_orig) == - gtk_text_iter_get_btree (end_orig)); - + g_return_if_fail (_gtk_text_iter_get_btree (start_orig) == + _gtk_text_iter_get_btree (end_orig)); + g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table); + #if 0 printf ("%s tag %s from %d to %d\n", add ? "Adding" : "Removing", @@ -1530,16 +1793,16 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, start = *start_orig; end = *end_orig; - gtk_text_iter_reorder (&start, &end); + gtk_text_iter_order (&start, &end); - tree = gtk_text_iter_get_btree (&start); + tree = _gtk_text_iter_get_btree (&start); queue_tag_redisplay (tree, tag, &start, &end); info = gtk_text_btree_get_tag_info (tree, tag); - start_line = gtk_text_iter_get_text_line (&start); - end_line = gtk_text_iter_get_text_line (&end); + start_line = _gtk_text_iter_get_text_line (&start); + end_line = _gtk_text_iter_get_text_line (&end); /* Find all tag toggles in the region; we are going to delete them. We need to find them in advance, because @@ -1547,8 +1810,11 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, with the tree. */ stack = iter_stack_new (); iter = start; - /* We don't want to delete a toggle that's at the start iterator. */ - gtk_text_iter_next_char (&iter); + + /* forward_to_tag_toggle() skips a toggle at the start iterator, + * which is deliberate - we don't want to delete a toggle at the + * start. + */ while (gtk_text_iter_forward_to_tag_toggle (&iter, tag)) { if (gtk_text_iter_compare (&iter, &end) >= 0) @@ -1610,9 +1876,9 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, GtkTextLineSegment *indexable_seg; GtkTextLine *line; - line = gtk_text_iter_get_text_line (&iter); - seg = gtk_text_iter_get_any_segment (&iter); - indexable_seg = gtk_text_iter_get_indexable_segment (&iter); + line = _gtk_text_iter_get_text_line (&iter); + seg = _gtk_text_iter_get_any_segment (&iter); + indexable_seg = _gtk_text_iter_get_indexable_segment (&iter); g_assert (seg != NULL); g_assert (indexable_seg != NULL); @@ -1626,7 +1892,7 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, g_assert (seg != NULL); g_assert (indexable_seg != NULL); g_assert (seg != indexable_seg); - + if ( (seg->type == >k_text_toggle_on_type || seg->type == >k_text_toggle_off_type) && (seg->body.toggle.info == info) ) @@ -1740,8 +2006,10 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, segments_changed (tree); + queue_tag_redisplay (tree, tag, &start, &end); + if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); } @@ -1749,18 +2017,21 @@ gtk_text_btree_tag (const GtkTextIter *start_orig, * "Getters" */ -GtkTextLine* -gtk_text_btree_get_line (GtkTextBTree *tree, - gint line_number, - gint *real_line_number) +static GtkTextLine* +get_line_internal (GtkTextBTree *tree, + gint line_number, + gint *real_line_number, + gboolean include_last) { GtkTextBTreeNode *node; GtkTextLine *line; int lines_left; int line_count; - line_count = gtk_text_btree_line_count (tree); - + line_count = _gtk_text_btree_line_count (tree); + if (!include_last) + line_count -= 1; + if (line_number < 0) { line_number = line_count; @@ -1816,24 +2087,51 @@ gtk_text_btree_get_line (GtkTextBTree *tree, } GtkTextLine* -gtk_text_btree_get_line_at_char (GtkTextBTree *tree, - gint char_index, - gint *line_start_index, - gint *real_char_index) +_gtk_text_btree_get_end_iter_line (GtkTextBTree *tree) +{ + return + _gtk_text_btree_get_line (tree, + _gtk_text_btree_line_count (tree) - 1, + NULL); +} + +GtkTextLine* +_gtk_text_btree_get_line (GtkTextBTree *tree, + gint line_number, + gint *real_line_number) +{ + return get_line_internal (tree, line_number, real_line_number, TRUE); +} + +GtkTextLine* +_gtk_text_btree_get_line_no_last (GtkTextBTree *tree, + gint line_number, + gint *real_line_number) +{ + return get_line_internal (tree, line_number, real_line_number, FALSE); +} + +GtkTextLine* +_gtk_text_btree_get_line_at_char (GtkTextBTree *tree, + gint char_index, + gint *line_start_index, + gint *real_char_index) { GtkTextBTreeNode *node; GtkTextLine *line; GtkTextLineSegment *seg; int chars_left; int chars_in_line; - int bytes_in_line; node = tree->root_node; - /* Clamp to valid indexes (-1 is magic for "highest index") */ - if (char_index < 0 || char_index >= node->num_chars) + /* Clamp to valid indexes (-1 is magic for "highest index"), + * node->num_chars includes the two newlines that aren't really + * in the buffer. + */ + if (char_index < 0 || char_index >= (node->num_chars - 1)) { - char_index = node->num_chars - 1; + char_index = node->num_chars - 2; } *real_char_index = char_index; @@ -1869,7 +2167,6 @@ gtk_text_btree_get_line_at_char (GtkTextBTree *tree, */ chars_in_line = 0; - bytes_in_line = 0; seg = NULL; for (line = node->children.line; line != NULL; line = line->next) { @@ -1899,8 +2196,10 @@ gtk_text_btree_get_line_at_char (GtkTextBTree *tree, return line; } +/* It returns an array sorted by tags priority, ready to pass to + * _gtk_text_attributes_fill_from_tags() */ GtkTextTag** -gtk_text_btree_get_tags (const GtkTextIter *iter, +_gtk_text_btree_get_tags (const GtkTextIter *iter, gint *num_tags) { GtkTextBTreeNode *node; @@ -1909,13 +2208,11 @@ gtk_text_btree_get_tags (const GtkTextIter *iter, int src, dst, index; TagInfo tagInfo; GtkTextLine *line; - GtkTextBTree *tree; gint byte_index; #define NUM_TAG_INFOS 10 - line = gtk_text_iter_get_text_line (iter); - tree = gtk_text_iter_get_btree (iter); + line = _gtk_text_iter_get_text_line (iter); byte_index = gtk_text_iter_get_line_index (iter); tagInfo.numTags = 0; @@ -2009,6 +2306,10 @@ gtk_text_btree_get_tags (const GtkTextIter *iter, g_free (tagInfo.tags); return NULL; } + + /* Sort tags in ascending order of priority */ + _gtk_text_tag_array_sort (tagInfo.tags, dst); + return tagInfo.tags; } @@ -2025,8 +2326,8 @@ copy_segment (GString *string, if (gtk_text_iter_equal (start, end)) return; - seg = gtk_text_iter_get_indexable_segment (start); - end_seg = gtk_text_iter_get_indexable_segment (end); + seg = _gtk_text_iter_get_indexable_segment (start); + end_seg = _gtk_text_iter_get_indexable_segment (end); if (seg->type == >k_text_char_type) { @@ -2037,18 +2338,18 @@ copy_segment (GString *string, /* Don't copy if we're invisible; segments are invisible/not as a whole, no need to check each char */ if (!include_hidden && - gtk_text_btree_char_is_invisible (start)) + _gtk_text_btree_char_is_invisible (start)) { copy = FALSE; /* printf (" \n"); */ } - copy_start = gtk_text_iter_get_segment_byte (start); + copy_start = _gtk_text_iter_get_segment_byte (start); if (seg == end_seg) { /* End is in the same segment; need to copy fewer bytes. */ - gint end_byte = gtk_text_iter_get_segment_byte (end); + gint end_byte = _gtk_text_iter_get_segment_byte (end); copy_bytes = end_byte - copy_start; } @@ -2069,7 +2370,8 @@ copy_segment (GString *string, /* printf (" :%s\n", string->str); */ } - else if (seg->type == >k_text_pixbuf_type) + else if (seg->type == >k_text_pixbuf_type || + seg->type == >k_text_child_type) { gboolean copy = TRUE; @@ -2078,7 +2380,7 @@ copy_segment (GString *string, copy = FALSE; } else if (!include_hidden && - gtk_text_btree_char_is_invisible (start)) + _gtk_text_btree_char_is_invisible (start)) { copy = FALSE; } @@ -2094,7 +2396,7 @@ copy_segment (GString *string, } gchar* -gtk_text_btree_get_text (const GtkTextIter *start_orig, +_gtk_text_btree_get_text (const GtkTextIter *start_orig, const GtkTextIter *end_orig, gboolean include_hidden, gboolean include_nonchars) @@ -2102,7 +2404,6 @@ gtk_text_btree_get_text (const GtkTextIter *start_orig, GtkTextLineSegment *seg; GtkTextLineSegment *end_seg; GString *retval; - GtkTextBTree *tree; gchar *str; GtkTextIter iter; GtkTextIter start; @@ -2110,29 +2411,27 @@ gtk_text_btree_get_text (const GtkTextIter *start_orig, g_return_val_if_fail (start_orig != NULL, NULL); g_return_val_if_fail (end_orig != NULL, NULL); - g_return_val_if_fail (gtk_text_iter_get_btree (start_orig) == - gtk_text_iter_get_btree (end_orig), NULL); + g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) == + _gtk_text_iter_get_btree (end_orig), NULL); start = *start_orig; end = *end_orig; - gtk_text_iter_reorder (&start, &end); - - retval = g_string_new (""); + gtk_text_iter_order (&start, &end); - tree = gtk_text_iter_get_btree (&start); + retval = g_string_new (NULL); - end_seg = gtk_text_iter_get_indexable_segment (&end); + end_seg = _gtk_text_iter_get_indexable_segment (&end); iter = start; - seg = gtk_text_iter_get_indexable_segment (&iter); + seg = _gtk_text_iter_get_indexable_segment (&iter); while (seg != end_seg) { copy_segment (retval, include_hidden, include_nonchars, &iter, &end); - gtk_text_iter_forward_indexable_segment (&iter); + _gtk_text_iter_forward_indexable_segment (&iter); - seg = gtk_text_iter_get_indexable_segment (&iter); + seg = _gtk_text_iter_get_indexable_segment (&iter); } copy_segment (retval, include_hidden, include_nonchars, &iter, &end); @@ -2143,7 +2442,7 @@ gtk_text_btree_get_text (const GtkTextIter *start_orig, } gint -gtk_text_btree_line_count (GtkTextBTree *tree) +_gtk_text_btree_line_count (GtkTextBTree *tree) { /* Subtract bogus line at the end; we return a count of usable lines. */ @@ -2151,19 +2450,21 @@ gtk_text_btree_line_count (GtkTextBTree *tree) } gint -gtk_text_btree_char_count (GtkTextBTree *tree) +_gtk_text_btree_char_count (GtkTextBTree *tree) { - /* Exclude newline in bogus last line */ - return tree->root_node->num_chars - 1; + /* Exclude newline in bogus last line and the + * one in the last line that is after the end iterator + */ + return tree->root_node->num_chars - 2; } #define LOTSA_TAGS 1000 gboolean -gtk_text_btree_char_is_invisible (const GtkTextIter *iter) +_gtk_text_btree_char_is_invisible (const GtkTextIter *iter) { gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */ - int deftagCnts[LOTSA_TAGS]; + int deftagCnts[LOTSA_TAGS] = { 0, }; int *tagCnts = deftagCnts; GtkTextTag *deftags[LOTSA_TAGS]; GtkTextTag **tags = deftags; @@ -2177,24 +2478,19 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter) GtkTextBTree *tree; gint byte_index; - line = gtk_text_iter_get_text_line (iter); - tree = gtk_text_iter_get_btree (iter); + line = _gtk_text_iter_get_text_line (iter); + tree = _gtk_text_iter_get_btree (iter); byte_index = gtk_text_iter_get_line_index (iter); - numTags = gtk_text_tag_table_size (tree->table); + numTags = gtk_text_tag_table_get_size (tree->table); /* almost always avoid malloc, so stay out of system calls */ if (LOTSA_TAGS < numTags) { - tagCnts = g_new (int, numTags); + tagCnts = g_new0 (int, numTags); tags = g_new (GtkTextTag*, numTags); } - for (i=0; itype == >k_text_toggle_off_type)) { tag = seg->body.toggle.info->tag; - if (tag->invisible_set && tag->values->invisible) + if (tag->invisible_set) { tags[tag->priority] = tag; tagCnts[tag->priority]++; @@ -2232,7 +2528,7 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter) || (seg->type == >k_text_toggle_off_type)) { tag = seg->body.toggle.info->tag; - if (tag->invisible_set && tag->values->invisible) + if (tag->invisible_set) { tags[tag->priority] = tag; tagCnts[tag->priority]++; @@ -2261,7 +2557,7 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter) if (summary->toggle_count & 1) { tag = summary->info->tag; - if (tag->invisible_set && tag->values->invisible) + if (tag->invisible_set) { tags[tag->priority] = tag; tagCnts[tag->priority] += summary->toggle_count; @@ -2313,7 +2609,8 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter) static void redisplay_region (GtkTextBTree *tree, const GtkTextIter *start, - const GtkTextIter *end) + const GtkTextIter *end, + gboolean cursors_only) { BTreeView *view; GtkTextLine *start_line, *end_line; @@ -2325,8 +2622,8 @@ redisplay_region (GtkTextBTree *tree, end = tmp; } - start_line = gtk_text_iter_get_text_line (start); - end_line = gtk_text_iter_get_text_line (end); + start_line = _gtk_text_iter_get_text_line (start); + end_line = _gtk_text_iter_get_text_line (end); view = tree->views; while (view != NULL) @@ -2334,20 +2631,25 @@ redisplay_region (GtkTextBTree *tree, gint start_y, end_y; GtkTextLineData *ld; - start_y = gtk_text_btree_find_line_top (tree, start_line, view->view_id); + start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id); if (end_line == start_line) end_y = start_y; else - end_y = gtk_text_btree_find_line_top (tree, end_line, view->view_id); + end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id); - ld = gtk_text_line_get_data (end_line, view->view_id); + ld = _gtk_text_line_get_data (end_line, view->view_id); if (ld) end_y += ld->height; - gtk_text_layout_changed (view->layout, start_y, - end_y - start_y, - end_y - start_y); + if (cursors_only) + gtk_text_layout_cursors_changed (view->layout, start_y, + end_y - start_y, + end_y - start_y); + else + gtk_text_layout_changed (view->layout, start_y, + end_y - start_y, + end_y - start_y); view = view->next; } @@ -2359,15 +2661,15 @@ redisplay_mark (GtkTextLineSegment *mark) GtkTextIter iter; GtkTextIter end; - gtk_text_btree_get_iter_at_mark (mark->body.mark.tree, + _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree, &iter, mark->body.mark.obj); end = iter; - gtk_text_iter_next_char (&end); + gtk_text_iter_forward_char (&end); - gtk_text_btree_invalidate_region (mark->body.mark.tree, - &iter, &end); + DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC)); + _gtk_text_btree_invalidate_region (mark->body.mark.tree, &iter, &end, TRUE); } static void @@ -2384,29 +2686,33 @@ ensure_not_off_end (GtkTextBTree *tree, GtkTextLineSegment *mark, GtkTextIter *iter) { - if (gtk_text_iter_get_line (iter) == - gtk_text_btree_line_count (tree)) - gtk_text_iter_prev_char (iter); + if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree)) + gtk_text_iter_backward_char (iter); } static GtkTextLineSegment* -real_set_mark (GtkTextBTree *tree, - GtkTextMark *existing_mark, - const gchar *name, - gboolean left_gravity, +real_set_mark (GtkTextBTree *tree, + GtkTextMark *existing_mark, + const gchar *name, + gboolean left_gravity, const GtkTextIter *where, - gboolean should_exist, - gboolean redraw_selections) + gboolean should_exist, + gboolean redraw_selections) { GtkTextLineSegment *mark; GtkTextIter iter; g_return_val_if_fail (tree != NULL, NULL); g_return_val_if_fail (where != NULL, NULL); - g_return_val_if_fail (gtk_text_iter_get_btree (where) == tree, NULL); + g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL); if (existing_mark) - mark = existing_mark->segment; + { + if (gtk_text_mark_get_buffer (existing_mark) != NULL) + mark = existing_mark->segment; + else + mark = NULL; + } else if (name != NULL) mark = g_hash_table_lookup (tree->mark_table, name); @@ -2420,10 +2726,14 @@ real_set_mark (GtkTextBTree *tree, } /* OK if !should_exist and it does already exist, in that case - we just move it. */ - + * we just move it. + */ + iter = *where; + if (gtk_debug_flags & GTK_DEBUG_TEXT) + _gtk_text_iter_check (&iter); + if (mark != NULL) { if (redraw_selections && @@ -2432,9 +2742,9 @@ real_set_mark (GtkTextBTree *tree, { GtkTextIter old_pos; - gtk_text_btree_get_iter_at_mark (tree, &old_pos, + _gtk_text_btree_get_iter_at_mark (tree, &old_pos, mark->body.mark.obj); - redisplay_region (tree, &old_pos, where); + redisplay_region (tree, &old_pos, where, TRUE); } /* @@ -2454,19 +2764,23 @@ real_set_mark (GtkTextBTree *tree, This could hose our iterator... */ gtk_text_btree_unlink_segment (tree, mark, mark->body.mark.line); - mark->body.mark.line = gtk_text_iter_get_text_line (&iter); - g_assert (mark->body.mark.line == gtk_text_iter_get_text_line (&iter)); + mark->body.mark.line = _gtk_text_iter_get_text_line (&iter); + g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter)); segments_changed (tree); /* make sure the iterator recomputes its segment */ } else { - mark = _gtk_mark_segment_new (tree, - left_gravity, - name); + if (existing_mark) + g_object_ref (existing_mark); + else + existing_mark = gtk_text_mark_new (name, left_gravity); + + mark = existing_mark->segment; + _gtk_mark_segment_set_tree (mark, tree); - mark->body.mark.line = gtk_text_iter_get_text_line (&iter); + mark->body.mark.line = _gtk_text_iter_get_text_line (&iter); if (mark->body.mark.name) g_hash_table_insert (tree->mark_table, @@ -2474,6 +2788,9 @@ real_set_mark (GtkTextBTree *tree, mark); } + if (gtk_debug_flags & GTK_DEBUG_TEXT) + _gtk_text_iter_check (&iter); + /* Link mark into new location */ gtk_text_btree_link_segment (mark, &iter); @@ -2486,12 +2803,18 @@ real_set_mark (GtkTextBTree *tree, redisplay_mark_if_visible (mark); + if (gtk_debug_flags & GTK_DEBUG_TEXT) + _gtk_text_iter_check (&iter); + + if (gtk_debug_flags & GTK_DEBUG_TEXT) + _gtk_text_btree_check (tree); + return mark; } GtkTextMark* -gtk_text_btree_set_mark (GtkTextBTree *tree, +_gtk_text_btree_set_mark (GtkTextBTree *tree, GtkTextMark *existing_mark, const gchar *name, gboolean left_gravity, @@ -2508,15 +2831,15 @@ gtk_text_btree_set_mark (GtkTextBTree *tree, } gboolean -gtk_text_btree_get_selection_bounds (GtkTextBTree *tree, +_gtk_text_btree_get_selection_bounds (GtkTextBTree *tree, GtkTextIter *start, GtkTextIter *end) { GtkTextIter tmp_start, tmp_end; - gtk_text_btree_get_iter_at_mark (tree, &tmp_start, + _gtk_text_btree_get_iter_at_mark (tree, &tmp_start, tree->insert_mark); - gtk_text_btree_get_iter_at_mark (tree, &tmp_end, + _gtk_text_btree_get_iter_at_mark (tree, &tmp_end, tree->selection_bound_mark); if (gtk_text_iter_equal (&tmp_start, &tmp_end)) @@ -2531,7 +2854,7 @@ gtk_text_btree_get_selection_bounds (GtkTextBTree *tree, } else { - gtk_text_iter_reorder (&tmp_start, &tmp_end); + gtk_text_iter_order (&tmp_start, &tmp_end); if (start) *start = tmp_start; @@ -2544,23 +2867,44 @@ gtk_text_btree_get_selection_bounds (GtkTextBTree *tree, } void -gtk_text_btree_place_cursor (GtkTextBTree *tree, +_gtk_text_btree_place_cursor (GtkTextBTree *tree, const GtkTextIter *iter) { - GtkTextIter start, end; + _gtk_text_btree_select_range (tree, iter, iter); +} - if (gtk_text_btree_get_selection_bounds (tree, &start, &end)) - redisplay_region (tree, &start, &end); +void +_gtk_text_btree_select_range (GtkTextBTree *tree, + const GtkTextIter *ins, + const GtkTextIter *bound) +{ + GtkTextIter old_ins, old_bound; + + _gtk_text_btree_get_iter_at_mark (tree, &old_ins, + tree->insert_mark); + _gtk_text_btree_get_iter_at_mark (tree, &old_bound, + tree->selection_bound_mark); + + /* Check if it's no-op since gtk_text_buffer_place_cursor() + * also calls this, and this will redraw the cursor line. */ + if (!gtk_text_iter_equal (&old_ins, ins) || + !gtk_text_iter_equal (&old_bound, bound)) + { + redisplay_region (tree, &old_ins, &old_bound, TRUE); - /* Move insert AND selection_bound before we redisplay */ - real_set_mark (tree, tree->insert_mark, - "insert", FALSE, iter, TRUE, FALSE); - real_set_mark (tree, tree->selection_bound_mark, - "selection_bound", FALSE, iter, TRUE, FALSE); + /* Move insert AND selection_bound before we redisplay */ + real_set_mark (tree, tree->insert_mark, + "insert", FALSE, ins, TRUE, FALSE); + real_set_mark (tree, tree->selection_bound_mark, + "selection_bound", FALSE, bound, TRUE, FALSE); + + redisplay_region (tree, ins, bound, TRUE); + } } + void -gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree, +_gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree, const gchar *name) { GtkTextMark *mark; @@ -2571,12 +2915,29 @@ gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree, mark = g_hash_table_lookup (tree->mark_table, name); - gtk_text_btree_remove_mark (tree, mark); + _gtk_text_btree_remove_mark (tree, mark); +} + +void +_gtk_text_btree_release_mark_segment (GtkTextBTree *tree, + GtkTextLineSegment *segment) +{ + + if (segment->body.mark.name) + g_hash_table_remove (tree->mark_table, segment->body.mark.name); + + segment->body.mark.tree = NULL; + segment->body.mark.line = NULL; + + /* Remove the ref on the mark, which frees segment as a side effect + * if this is the last reference. + */ + g_object_unref (segment->body.mark.obj); } void -gtk_text_btree_remove_mark (GtkTextBTree *tree, - GtkTextMark *mark) +_gtk_text_btree_remove_mark (GtkTextBTree *tree, + GtkTextMark *mark) { GtkTextLineSegment *segment; @@ -2593,34 +2954,39 @@ gtk_text_btree_remove_mark (GtkTextBTree *tree, /* This calls cleanup_line and segments_changed */ gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line); - - if (segment->body.mark.name) - g_hash_table_remove (tree->mark_table, segment->body.mark.name); - - /* Remove the ref on the mark that belonged to the segment. */ - g_object_unref (G_OBJECT (mark)); - - segment->body.mark.tree = NULL; - segment->body.mark.line = NULL; + + _gtk_text_btree_release_mark_segment (tree, segment); } gboolean -gtk_text_btree_mark_is_insert (GtkTextBTree *tree, - GtkTextMark *segment) +_gtk_text_btree_mark_is_insert (GtkTextBTree *tree, + GtkTextMark *segment) { return segment == tree->insert_mark; } gboolean -gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree, - GtkTextMark *segment) +_gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree, + GtkTextMark *segment) { return segment == tree->selection_bound_mark; } +GtkTextMark * +_gtk_text_btree_get_insert (GtkTextBTree *tree) +{ + return tree->insert_mark; +} + +GtkTextMark * +_gtk_text_btree_get_selection_bound (GtkTextBTree *tree) +{ + return tree->selection_bound_mark; +} + GtkTextMark* -gtk_text_btree_get_mark_by_name (GtkTextBTree *tree, - const gchar *name) +_gtk_text_btree_get_mark_by_name (GtkTextBTree *tree, + const gchar *name) { GtkTextLineSegment *seg; @@ -2632,6 +2998,18 @@ gtk_text_btree_get_mark_by_name (GtkTextBTree *tree, return seg ? seg->body.mark.obj : NULL; } +/** + * gtk_text_mark_set_visible: + * @mark: a #GtkTextMark + * @setting: visibility of mark + * + * Sets the visibility of @mark; the insertion point is normally + * visible, i.e. you can see it as a vertical bar. Also, the text + * widget uses a visible mark to indicate where a drop will occur when + * dragging-and-dropping text. Most other marks are not visible. + * Marks are not visible by default. + * + **/ void gtk_text_mark_set_visible (GtkTextMark *mark, gboolean setting) @@ -2648,12 +3026,13 @@ gtk_text_mark_set_visible (GtkTextMark *mark, { seg->body.mark.visible = setting; - redisplay_mark (seg); + if (seg->body.mark.tree) + redisplay_mark (seg); } } GtkTextLine* -gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree, +_gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree, GtkTextTag *tag) { GtkTextBTreeNode *node; @@ -2705,12 +3084,12 @@ gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree, Unfortunately this can't be done in a simple and efficient way right now; so I'm just going to return the first line in the btree. FIXME */ - return gtk_text_btree_get_line (tree, 0, NULL); + return _gtk_text_btree_get_line (tree, 0, NULL); } } GtkTextLine* -gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree, +_gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree, GtkTextTag *tag) { GtkTextBTreeNode *node; @@ -2764,7 +3143,7 @@ gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree, at least not without complexity. So, we just return the last line. */ - return gtk_text_btree_get_line (tree, -1, NULL); + return _gtk_text_btree_get_end_iter_line (tree); } } @@ -2774,7 +3153,7 @@ gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree, */ gint -gtk_text_line_get_number (GtkTextLine *line) +_gtk_text_line_get_number (GtkTextLine *line) { GtkTextLine *line2; GtkTextBTreeNode *node, *parent, *node2; @@ -2959,10 +3338,10 @@ find_toggle_outside_current_line (GtkTextLine *line, /* FIXME this function is far too slow, for no good reason. */ gboolean -gtk_text_line_char_has_tag (GtkTextLine *line, - GtkTextBTree *tree, - gint char_in_line, - GtkTextTag *tag) +_gtk_text_line_char_has_tag (GtkTextLine *line, + GtkTextBTree *tree, + gint char_in_line, + GtkTextTag *tag) { GtkTextLineSegment *toggle_seg; @@ -2983,10 +3362,10 @@ gtk_text_line_char_has_tag (GtkTextLine *line, } gboolean -gtk_text_line_byte_has_tag (GtkTextLine *line, - GtkTextBTree *tree, - gint byte_in_line, - GtkTextTag *tag) +_gtk_text_line_byte_has_tag (GtkTextLine *line, + GtkTextBTree *tree, + gint byte_in_line, + GtkTextTag *tag) { GtkTextLineSegment *toggle_seg; @@ -3007,14 +3386,96 @@ gtk_text_line_byte_has_tag (GtkTextLine *line, } gboolean -gtk_text_line_is_last (GtkTextLine *line, - GtkTextBTree *tree) +_gtk_text_line_is_last (GtkTextLine *line, + GtkTextBTree *tree) { return line == get_last_line (tree); } +static void +ensure_end_iter_line (GtkTextBTree *tree) +{ + if (tree->end_iter_line_stamp != tree->chars_changed_stamp) + { + gint real_line; + + /* n_lines is without the magic line at the end */ + g_assert (_gtk_text_btree_line_count (tree) >= 1); + + tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line); + + tree->end_iter_line_stamp = tree->chars_changed_stamp; + } +} + +static void +ensure_end_iter_segment (GtkTextBTree *tree) +{ + if (tree->end_iter_segment_stamp != tree->segments_changed_stamp) + { + GtkTextLineSegment *seg; + GtkTextLineSegment *last_with_chars; + + ensure_end_iter_line (tree); + + last_with_chars = NULL; + + seg = tree->end_iter_line->segments; + while (seg != NULL) + { + if (seg->char_count > 0) + last_with_chars = seg; + seg = seg->next; + } + + tree->end_iter_segment = last_with_chars; + + /* We know the last char in the last line is '\n' */ + tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1; + tree->end_iter_segment_char_offset = last_with_chars->char_count - 1; + + tree->end_iter_segment_stamp = tree->segments_changed_stamp; + + g_assert (tree->end_iter_segment->type == >k_text_char_type); + g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n'); + } +} + +gboolean +_gtk_text_line_contains_end_iter (GtkTextLine *line, + GtkTextBTree *tree) +{ + ensure_end_iter_line (tree); + + return line == tree->end_iter_line; +} + +gboolean +_gtk_text_btree_is_end (GtkTextBTree *tree, + GtkTextLine *line, + GtkTextLineSegment *seg, + int byte_index, + int char_offset) +{ + g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE); + + /* Do this first to avoid walking segments in most cases */ + if (!_gtk_text_line_contains_end_iter (line, tree)) + return FALSE; + + ensure_end_iter_segment (tree); + + if (seg != tree->end_iter_segment) + return FALSE; + + if (byte_index >= 0) + return byte_index == tree->end_iter_segment_byte_index; + else + return char_offset == tree->end_iter_segment_char_offset; +} + GtkTextLine* -gtk_text_line_next (GtkTextLine *line) +_gtk_text_line_next (GtkTextLine *line) { GtkTextBTreeNode *node; @@ -3049,7 +3510,24 @@ gtk_text_line_next (GtkTextLine *line) } GtkTextLine* -gtk_text_line_previous (GtkTextLine *line) +_gtk_text_line_next_excluding_last (GtkTextLine *line) +{ + GtkTextLine *next; + + next = _gtk_text_line_next (line); + + /* If we were on the end iter line, we can't go to + * the last line + */ + if (next && next->next == NULL && /* these checks are optimization only */ + _gtk_text_line_next (next) == NULL) + return NULL; + + return next; +} + +GtkTextLine* +_gtk_text_line_previous (GtkTextLine *line) { GtkTextBTreeNode *node; GtkTextBTreeNode *node2; @@ -3105,9 +3583,27 @@ gtk_text_line_previous (GtkTextLine *line) return NULL; } + +GtkTextLineData* +_gtk_text_line_data_new (GtkTextLayout *layout, + GtkTextLine *line) +{ + GtkTextLineData *line_data; + + line_data = g_new (GtkTextLineData, 1); + + line_data->view_id = layout; + line_data->next = NULL; + line_data->width = 0; + line_data->height = 0; + line_data->valid = FALSE; + + return line_data; +} + void -gtk_text_line_add_data (GtkTextLine *line, - GtkTextLineData *data) +_gtk_text_line_add_data (GtkTextLine *line, + GtkTextLineData *data) { g_return_if_fail (line != NULL); g_return_if_fail (data != NULL); @@ -3125,7 +3621,7 @@ gtk_text_line_add_data (GtkTextLine *line, } gpointer -gtk_text_line_remove_data (GtkTextLine *line, +_gtk_text_line_remove_data (GtkTextLine *line, gpointer view_id) { GtkTextLineData *prev; @@ -3158,8 +3654,8 @@ gtk_text_line_remove_data (GtkTextLine *line, } gpointer -gtk_text_line_get_data (GtkTextLine *line, - gpointer view_id) +_gtk_text_line_get_data (GtkTextLine *line, + gpointer view_id) { GtkTextLineData *iter; @@ -3178,8 +3674,8 @@ gtk_text_line_get_data (GtkTextLine *line, } void -gtk_text_line_invalidate_wrap (GtkTextLine *line, - GtkTextLineData *ld) +_gtk_text_line_invalidate_wrap (GtkTextLine *line, + GtkTextLineData *ld) { /* For now this is totally unoptimized. FIXME? @@ -3187,15 +3683,15 @@ gtk_text_line_invalidate_wrap (GtkTextLine *line, is less than the max width for the parent node, and the case where the height is unchanged when we re-wrap. */ - + g_return_if_fail (ld != NULL); - + ld->valid = FALSE; gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id); } gint -gtk_text_line_char_count (GtkTextLine *line) +_gtk_text_line_char_count (GtkTextLine *line) { GtkTextLineSegment *seg; gint size; @@ -3211,7 +3707,7 @@ gtk_text_line_char_count (GtkTextLine *line) } gint -gtk_text_line_byte_count (GtkTextLine *line) +_gtk_text_line_byte_count (GtkTextLine *line) { GtkTextLineSegment *seg; gint size; @@ -3228,7 +3724,7 @@ gtk_text_line_byte_count (GtkTextLine *line) } gint -gtk_text_line_char_index (GtkTextLine *target_line) +_gtk_text_line_char_index (GtkTextLine *target_line) { GSList *node_stack = NULL; GtkTextBTreeNode *iter; @@ -3302,7 +3798,7 @@ gtk_text_line_char_index (GtkTextLine *target_line) { g_assert (line != NULL); - num_chars += gtk_text_line_char_count (line); + num_chars += _gtk_text_line_char_count (line); line = line->next; } @@ -3313,7 +3809,7 @@ gtk_text_line_char_index (GtkTextLine *target_line) } GtkTextLineSegment* -gtk_text_line_byte_to_segment (GtkTextLine *line, +_gtk_text_line_byte_to_segment (GtkTextLine *line, gint byte_offset, gint *seg_offset) { @@ -3327,9 +3823,9 @@ gtk_text_line_byte_to_segment (GtkTextLine *line, while (offset >= seg->byte_count) { - g_assert (seg != NULL); /* means an invalid byte index */ offset -= seg->byte_count; seg = seg->next; + g_assert (seg != NULL); /* means an invalid byte index */ } if (seg_offset) @@ -3339,7 +3835,7 @@ gtk_text_line_byte_to_segment (GtkTextLine *line, } GtkTextLineSegment* -gtk_text_line_char_to_segment (GtkTextLine *line, +_gtk_text_line_char_to_segment (GtkTextLine *line, gint char_offset, gint *seg_offset) { @@ -3353,9 +3849,9 @@ gtk_text_line_char_to_segment (GtkTextLine *line, while (offset >= seg->char_count) { - g_assert (seg != NULL); /* means an invalid char index */ offset -= seg->char_count; seg = seg->next; + g_assert (seg != NULL); /* means an invalid char index */ } if (seg_offset) @@ -3365,7 +3861,7 @@ gtk_text_line_char_to_segment (GtkTextLine *line, } GtkTextLineSegment* -gtk_text_line_byte_to_any_segment (GtkTextLine *line, +_gtk_text_line_byte_to_any_segment (GtkTextLine *line, gint byte_offset, gint *seg_offset) { @@ -3379,9 +3875,9 @@ gtk_text_line_byte_to_any_segment (GtkTextLine *line, while (offset > 0 && offset >= seg->byte_count) { - g_assert (seg != NULL); /* means an invalid byte index */ offset -= seg->byte_count; seg = seg->next; + g_assert (seg != NULL); /* means an invalid byte index */ } if (seg_offset) @@ -3391,7 +3887,7 @@ gtk_text_line_byte_to_any_segment (GtkTextLine *line, } GtkTextLineSegment* -gtk_text_line_char_to_any_segment (GtkTextLine *line, +_gtk_text_line_char_to_any_segment (GtkTextLine *line, gint char_offset, gint *seg_offset) { @@ -3405,9 +3901,9 @@ gtk_text_line_char_to_any_segment (GtkTextLine *line, while (offset > 0 && offset >= seg->char_count) { - g_assert (seg != NULL); /* means an invalid byte index */ offset -= seg->char_count; seg = seg->next; + g_assert (seg != NULL); /* means an invalid byte index */ } if (seg_offset) @@ -3417,7 +3913,7 @@ gtk_text_line_char_to_any_segment (GtkTextLine *line, } gint -gtk_text_line_byte_to_char (GtkTextLine *line, +_gtk_text_line_byte_to_char (GtkTextLine *line, gint byte_offset) { gint char_offset; @@ -3431,12 +3927,10 @@ gtk_text_line_byte_to_char (GtkTextLine *line, while (byte_offset >= seg->byte_count) /* while (we need to go farther than the next segment) */ { - g_assert (seg != NULL); /* our byte_index was bogus if this happens */ - byte_offset -= seg->byte_count; char_offset += seg->char_count; - seg = seg->next; + g_assert (seg != NULL); /* our byte_index was bogus if this happens */ } g_assert (seg != NULL); @@ -3461,7 +3955,7 @@ gtk_text_line_byte_to_char (GtkTextLine *line, } gint -gtk_text_line_char_to_byte (GtkTextLine *line, +_gtk_text_line_char_to_byte (GtkTextLine *line, gint char_offset) { g_warning ("FIXME not implemented"); @@ -3471,31 +3965,22 @@ gtk_text_line_char_to_byte (GtkTextLine *line, /* FIXME sync with char_locate (or figure out a clean way to merge the two functions) */ -void -gtk_text_line_byte_locate (GtkTextLine *line, - gint byte_offset, - GtkTextLineSegment **segment, - GtkTextLineSegment **any_segment, - gint *seg_byte_offset, - gint *line_byte_offset) +gboolean +_gtk_text_line_byte_locate (GtkTextLine *line, + gint byte_offset, + GtkTextLineSegment **segment, + GtkTextLineSegment **any_segment, + gint *seg_byte_offset, + gint *line_byte_offset) { GtkTextLineSegment *seg; - GtkTextLineSegment *after_prev_indexable; GtkTextLineSegment *after_last_indexable; GtkTextLineSegment *last_indexable; gint offset; gint bytes_in_line; - g_return_if_fail (line != NULL); - - if (byte_offset < 0) - { - /* -1 means end of line; we here assume no line is - longer than 1 bazillion bytes, of course we assumed - that anyway since we'd wrap around... */ - - byte_offset = G_MAXINT; - } + g_return_val_if_fail (line != NULL, FALSE); + g_return_val_if_fail (byte_offset >= 0, FALSE); *segment = NULL; *any_segment = NULL; @@ -3505,7 +3990,6 @@ gtk_text_line_byte_locate (GtkTextLine *line, last_indexable = NULL; after_last_indexable = line->segments; - after_prev_indexable = line->segments; seg = line->segments; /* The loop ends when we're inside a segment; @@ -3518,7 +4002,6 @@ gtk_text_line_byte_locate (GtkTextLine *line, offset -= seg->byte_count; bytes_in_line += seg->byte_count; last_indexable = seg; - after_prev_indexable = after_last_indexable; after_last_indexable = last_indexable->next; } @@ -3528,11 +4011,10 @@ gtk_text_line_byte_locate (GtkTextLine *line, if (seg == NULL) { /* We went off the end of the line */ - *segment = last_indexable; - *any_segment = after_prev_indexable; - /* subtracting 1 is OK, we know it's a newline at the end. */ - offset = (*segment)->byte_count - 1; - bytes_in_line -= (*segment)->byte_count; + if (offset != 0) + g_warning ("%s: byte index off the end of the line", G_STRLOC); + + return FALSE; } else { @@ -3554,36 +4036,29 @@ gtk_text_line_byte_locate (GtkTextLine *line, g_assert (*seg_byte_offset < (*segment)->byte_count); *line_byte_offset = bytes_in_line + *seg_byte_offset; + + return TRUE; } /* FIXME sync with byte_locate (or figure out a clean way to merge the two functions) */ -void -gtk_text_line_char_locate (GtkTextLine *line, - gint char_offset, - GtkTextLineSegment **segment, - GtkTextLineSegment **any_segment, - gint *seg_char_offset, - gint *line_char_offset) +gboolean +_gtk_text_line_char_locate (GtkTextLine *line, + gint char_offset, + GtkTextLineSegment **segment, + GtkTextLineSegment **any_segment, + gint *seg_char_offset, + gint *line_char_offset) { GtkTextLineSegment *seg; - GtkTextLineSegment *after_prev_indexable; GtkTextLineSegment *after_last_indexable; GtkTextLineSegment *last_indexable; gint offset; gint chars_in_line; - g_return_if_fail (line != NULL); - - if (char_offset < 0) - { - /* -1 means end of line; we here assume no line is - longer than 1 bazillion chars, of course we assumed - that anyway since we'd wrap around... */ - - char_offset = G_MAXINT; - } - + g_return_val_if_fail (line != NULL, FALSE); + g_return_val_if_fail (char_offset >= 0, FALSE); + *segment = NULL; *any_segment = NULL; chars_in_line = 0; @@ -3592,7 +4067,6 @@ gtk_text_line_char_locate (GtkTextLine *line, last_indexable = NULL; after_last_indexable = line->segments; - after_prev_indexable = line->segments; seg = line->segments; /* The loop ends when we're inside a segment; @@ -3605,7 +4079,6 @@ gtk_text_line_char_locate (GtkTextLine *line, offset -= seg->char_count; chars_in_line += seg->char_count; last_indexable = seg; - after_prev_indexable = after_last_indexable; after_last_indexable = last_indexable->next; } @@ -3614,12 +4087,11 @@ gtk_text_line_char_locate (GtkTextLine *line, if (seg == NULL) { - /* We went off the end of the line */ - *segment = last_indexable; - *any_segment = after_prev_indexable; - /* subtracting 1 is OK, we know it's a newline at the end. */ - offset = (*segment)->char_count - 1; - chars_in_line -= (*segment)->char_count; + /* end of the line */ + if (offset != 0) + g_warning ("%s: char offset off the end of the line", G_STRLOC); + + return FALSE; } else { @@ -3641,10 +4113,12 @@ gtk_text_line_char_locate (GtkTextLine *line, g_assert (*seg_char_offset < (*segment)->char_count); *line_char_offset = chars_in_line + *seg_char_offset; + + return TRUE; } void -gtk_text_line_byte_to_char_offsets (GtkTextLine *line, +_gtk_text_line_byte_to_char_offsets (GtkTextLine *line, gint byte_offset, gint *line_char_offset, gint *seg_char_offset) @@ -3671,7 +4145,8 @@ gtk_text_line_byte_to_char_offsets (GtkTextLine *line, g_assert (seg->char_count > 0); /* indexable. */ /* offset is now the number of bytes into the current segment we - want to go. Count chars into the current segment. */ + * want to go. Count chars into the current segment. + */ if (seg->type == >k_text_char_type) { @@ -3689,7 +4164,7 @@ gtk_text_line_byte_to_char_offsets (GtkTextLine *line, } void -gtk_text_line_char_to_byte_offsets (GtkTextLine *line, +_gtk_text_line_char_to_byte_offsets (GtkTextLine *line, gint char_offset, gint *line_byte_offset, gint *seg_byte_offset) @@ -3720,16 +4195,16 @@ gtk_text_line_char_to_byte_offsets (GtkTextLine *line, if (seg->type == >k_text_char_type) { - *seg_byte_offset = 0; - while (offset > 0) - { - gint bytes; - const char * start = seg->body.chars + *seg_byte_offset; + const char *p; - bytes = g_utf8_next_char (start) - start; - *seg_byte_offset += bytes; - offset -= 1; - } + /* if in the last fourth of the segment walk backwards */ + if (seg->char_count - offset < seg->char_count / 4) + p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count, + offset - seg->char_count); + else + p = g_utf8_offset_to_pointer (seg->body.chars, offset); + + *seg_byte_offset = p - seg->body.chars; g_assert (*seg_byte_offset < seg->byte_count); @@ -3839,9 +4314,9 @@ node_compare (GtkTextBTreeNode *lhs, /* remember that tag == NULL means "any tag" */ GtkTextLine* -gtk_text_line_next_could_contain_tag (GtkTextLine *line, - GtkTextBTree *tree, - GtkTextTag *tag) +_gtk_text_line_next_could_contain_tag (GtkTextLine *line, + GtkTextBTree *tree, + GtkTextTag *tag) { GtkTextBTreeNode *node; GtkTextTagInfo *info; @@ -3850,24 +4325,24 @@ gtk_text_line_next_could_contain_tag (GtkTextLine *line, g_return_val_if_fail (line != NULL, NULL); if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); if (tag == NULL) { /* Right now we can only offer linear-search if the user wants * to know about any tag toggle at all. */ - return gtk_text_line_next (line); + return _gtk_text_line_next_excluding_last (line); } /* Our tag summaries only have node precision, not line - precision. This means that if any line under a node could contain a - tag, then any of the others could also contain a tag. - - In the future we could have some mechanism to keep track of how - many toggles we've found under a node so far, since we have a - count of toggles under the node. But for now I'm going with KISS. - */ + * precision. This means that if any line under a node could contain a + * tag, then any of the others could also contain a tag. + * + * In the future we could have some mechanism to keep track of how + * many toggles we've found under a node so far, since we have a + * count of toggles under the node. But for now I'm going with KISS. + */ /* return same-node line, if any. */ if (line->next) @@ -3994,7 +4469,7 @@ prev_line_under_node (GtkTextBTreeNode *node, } GtkTextLine* -gtk_text_line_previous_could_contain_tag (GtkTextLine *line, +_gtk_text_line_previous_could_contain_tag (GtkTextLine *line, GtkTextBTree *tree, GtkTextTag *tag) { @@ -4013,14 +4488,14 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine *line, g_return_val_if_fail (line != NULL, NULL); if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); if (tag == NULL) { /* Right now we can only offer linear-search if the user wants * to know about any tag toggle at all. */ - return gtk_text_line_previous (line); + return _gtk_text_line_previous (line); } /* Return same-node line, if any. */ @@ -4067,9 +4542,7 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine *line, line_ancestor = line->parent; line_ancestor_parent = line->parent->parent; - node = line_ancestor_parent->children.node; - while (node != line_ancestor && - line_ancestor != info->tag_root) + while (line_ancestor != info->tag_root) { GSList *child_nodes = NULL; GSList *tmp; @@ -4077,8 +4550,12 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine *line, /* Create reverse-order list of nodes before * line_ancestor */ - while (node != line_ancestor - && node != NULL) + if (line_ancestor_parent != NULL) + node = line_ancestor_parent->children.node; + else + node = line_ancestor; + + while (node != line_ancestor && node != NULL) { child_nodes = g_slist_prepend (child_nodes, node); @@ -4108,8 +4585,6 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine *line, /* Didn't find anything on this level; go up one level. */ line_ancestor = line_ancestor_parent; line_ancestor_parent = line_ancestor->parent; - - node = line_ancestor_parent->children.node; } /* No dice. */ @@ -4207,35 +4682,29 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine *line, static void summary_list_destroy (Summary *summary) { - Summary *next; - while (summary != NULL) - { - next = summary->next; - summary_destroy (summary); - summary = next; - } + g_slice_free_chain (Summary, summary, next); } static GtkTextLine* get_last_line (GtkTextBTree *tree) { - if (tree->end_iter_line_stamp != tree->chars_changed_stamp) + if (tree->last_line_stamp != tree->chars_changed_stamp) { gint n_lines; GtkTextLine *line; gint real_line; - n_lines = gtk_text_btree_line_count (tree); + n_lines = _gtk_text_btree_line_count (tree); g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */ - line = gtk_text_btree_get_line (tree, n_lines, &real_line); + line = _gtk_text_btree_get_line (tree, n_lines, &real_line); - tree->end_iter_line_stamp = tree->chars_changed_stamp; - tree->end_iter_line = line; + tree->last_line_stamp = tree->chars_changed_stamp; + tree->last_line = line; } - return tree->end_iter_line; + return tree->last_line; } /* @@ -4248,6 +4717,9 @@ gtk_text_line_new (void) GtkTextLine *line; line = g_new0(GtkTextLine, 1); + line->dir_strong = PANGO_DIRECTION_NEUTRAL; + line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL; + line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL; return line; } @@ -4308,16 +4780,20 @@ cleanup_line (GtkTextLine *line) while (changed) { changed = FALSE; - for (prev_p = &line->segments, seg = *prev_p; - seg != NULL; - prev_p = &(*prev_p)->next, seg = *prev_p) + prev_p = &line->segments; + for (seg = *prev_p; seg != NULL; seg = *prev_p) { if (seg->type->cleanupFunc != NULL) { *prev_p = (*seg->type->cleanupFunc)(seg, line); if (seg != *prev_p) - changed = TRUE; + { + changed = TRUE; + continue; + } } + + prev_p = &(*prev_p)->next; } } } @@ -4330,8 +4806,8 @@ static NodeData* node_data_new (gpointer view_id) { NodeData *nd; - - nd = g_new (NodeData, 1); + + nd = g_slice_new (NodeData); nd->view_id = view_id; nd->next = NULL; @@ -4345,27 +4821,18 @@ node_data_new (gpointer view_id) static void node_data_destroy (NodeData *nd) { - - g_free (nd); + g_slice_free (NodeData, nd); } static void node_data_list_destroy (NodeData *nd) { - NodeData *iter; - NodeData *next; - - iter = nd; - while (iter != NULL) - { - next = iter->next; - node_data_destroy (iter); - iter = next; - } + g_slice_free_chain (NodeData, nd, next); } static NodeData* -node_data_find (NodeData *nd, gpointer view_id) +node_data_find (NodeData *nd, + gpointer view_id) { while (nd != NULL) { @@ -4383,7 +4850,7 @@ summary_destroy (Summary *summary) summary->info = (void*)0x1; summary->toggle_count = 567; summary->next = (void*)0x1; - g_free (summary); + g_slice_free (Summary, summary); } static GtkTextBTreeNode* @@ -4421,7 +4888,7 @@ gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node, { /* didn't find a summary for our tag. */ g_return_if_fail (adjust > 0); - summary = g_new (Summary, 1); + summary = g_slice_new (Summary); summary->info = info; summary->toggle_count = adjust; summary->next = node->summary; @@ -4543,7 +5010,7 @@ gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id) /** - * gtk_text_btree_is_valid: + * _gtk_text_btree_is_valid: * @tree: a #GtkTextBTree * @view_id: ID for the view * @@ -4553,7 +5020,7 @@ gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id) * Return value: %TRUE if the entire #GtkTextBTree is valid **/ gboolean -gtk_text_btree_is_valid (GtkTextBTree *tree, +_gtk_text_btree_is_valid (GtkTextBTree *tree, gpointer view_id) { NodeData *nd; @@ -4595,7 +5062,7 @@ gtk_text_btree_node_validate (BTreeView *view, /* Iterate over leading valid lines */ while (line != NULL) { - ld = gtk_text_line_get_data (line, view_id); + ld = _gtk_text_line_get_data (line, view_id); if (!ld || !ld->valid) break; @@ -4619,7 +5086,7 @@ gtk_text_btree_node_validate (BTreeView *view, /* Iterate over invalid lines */ while (line != NULL) { - ld = gtk_text_line_get_data (line, view_id); + ld = _gtk_text_line_get_data (line, view_id); if (ld && ld->valid) break; @@ -4647,7 +5114,7 @@ gtk_text_btree_node_validate (BTreeView *view, /* Iterate over the remaining lines */ while (line != NULL) { - ld = gtk_text_line_get_data (line, view_id); + ld = _gtk_text_line_get_data (line, view_id); state->in_validation = FALSE; if (!ld || !ld->valid) @@ -4739,7 +5206,7 @@ gtk_text_btree_node_validate (BTreeView *view, } /** - * gtk_text_btree_validate: + * _gtk_text_btree_validate: * @tree: a #GtkTextBTree * @view_id: view id * @max_pixels: the maximum number of pixels to validate. (No more @@ -4755,7 +5222,7 @@ gtk_text_btree_node_validate (BTreeView *view, * entire tree was already valid. **/ gboolean -gtk_text_btree_validate (GtkTextBTree *tree, +_gtk_text_btree_validate (GtkTextBTree *tree, gpointer view_id, gint max_pixels, gint *y, @@ -4769,7 +5236,7 @@ gtk_text_btree_validate (GtkTextBTree *tree, view = gtk_text_btree_get_view (tree, view_id); g_return_val_if_fail (view != NULL, FALSE); - if (!gtk_text_btree_is_valid (tree, view_id)) + if (!_gtk_text_btree_is_valid (tree, view_id)) { ValidateState state; @@ -4791,7 +5258,7 @@ gtk_text_btree_validate (GtkTextBTree *tree, *new_height = state.new_height; if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); return TRUE; } @@ -4816,7 +5283,7 @@ gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node, while (line != NULL) { - GtkTextLineData *ld = gtk_text_line_get_data (line, view_id); + GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id); if (!ld || !ld->valid) valid = FALSE; @@ -4925,7 +5392,7 @@ gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node, /** - * gtk_text_btree_validate_line: + * _gtk_text_btree_validate_line: * @tree: a #GtkTextBTree * @line: line to validate * @view_id: view ID for the view to validate @@ -4934,12 +5401,11 @@ gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node, * results up through the entire tree. **/ void -gtk_text_btree_validate_line (GtkTextBTree *tree, - GtkTextLine *line, - gpointer view_id) +_gtk_text_btree_validate_line (GtkTextBTree *tree, + GtkTextLine *line, + gpointer view_id) { GtkTextLineData *ld; - GtkTextLine *last_line; BTreeView *view; g_return_if_fail (tree != NULL); @@ -4947,13 +5413,12 @@ gtk_text_btree_validate_line (GtkTextBTree *tree, view = gtk_text_btree_get_view (tree, view_id); g_return_if_fail (view != NULL); - - ld = gtk_text_line_get_data (line, view_id); + + ld = _gtk_text_line_get_data (line, view_id); if (!ld || !ld->valid) { ld = gtk_text_layout_wrap (view->layout, line, ld); - last_line = get_last_line (tree); - + gtk_text_btree_node_check_valid_upward (line->parent, view_id); } } @@ -4970,7 +5435,7 @@ gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpoint { GtkTextLineData *ld; - ld = gtk_text_line_remove_data (line, view_id); + ld = _gtk_text_line_remove_data (line, view_id); if (ld) gtk_text_layout_free_line_data (view->layout, line, ld); @@ -5013,14 +5478,7 @@ gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node) seg = line->segments; line->segments = seg->next; - if (GTK_IS_TEXT_MARK_SEGMENT (seg)) - { - /* Set the mark as deleted */ - seg->body.mark.tree = NULL; - seg->body.mark.line = NULL; - } - - (*seg->type->deleteFunc) (seg, line, 1); + (*seg->type->deleteFunc) (seg, line, TRUE); } gtk_text_line_destroy (tree, line); } @@ -5037,6 +5495,16 @@ gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node) } } + gtk_text_btree_node_free_empty (tree, node); +} + +static void +gtk_text_btree_node_free_empty (GtkTextBTree *tree, + GtkTextBTreeNode *node) +{ + g_return_if_fail ((node->level > 0 && node->children.node == NULL) || + (node->level == 0 && node->children.line == NULL)); + summary_list_destroy (node->summary); node_data_list_destroy (node->node_data); g_free (node); @@ -5056,14 +5524,15 @@ gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id) nd = nd->next; } - if (nd == NULL) { - nd = node_data_new (view_id); - - if (node->node_data) - nd->next = node->node_data; - - node->node_data = nd; - } + if (nd == NULL) + { + nd = node_data_new (view_id); + + if (node->node_data) + nd->next = node->node_data; + + node->node_data = nd; + } return nd; } @@ -5163,8 +5632,8 @@ get_tree_bounds (GtkTextBTree *tree, GtkTextIter *start, GtkTextIter *end) { - gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0); - gtk_text_btree_get_last_iter (tree, end); + _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0); + _gtk_text_btree_get_end_iter (tree, end); } static void @@ -5182,12 +5651,12 @@ tag_changed_cb (GtkTextTagTable *table, GtkTextIter start; GtkTextIter end; - if (gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag)) + if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag)) { /* Must be a last toggle if there was a first one. */ - gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag); - gtk_text_btree_invalidate_region (tree, - &start, &end); + _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag); + DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC)); + _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE); } } @@ -5202,7 +5671,7 @@ tag_changed_cb (GtkTextTagTable *table, { gint width, height; - gtk_text_btree_get_view_size (tree, view->view_id, &width, &height); + _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height); gtk_text_layout_changed (view->layout, 0, height, height); view = view->next; @@ -5210,10 +5679,9 @@ tag_changed_cb (GtkTextTagTable *table, } } -static void -tag_removed_cb (GtkTextTagTable *table, - GtkTextTag *tag, - GtkTextBTree *tree) +void +_gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree, + GtkTextTag *tag) { /* Remove the tag from the tree */ @@ -5222,7 +5690,7 @@ tag_removed_cb (GtkTextTagTable *table, get_tree_bounds (tree, &start, &end); - gtk_text_btree_tag (&start, &end, tag, FALSE); + _gtk_text_btree_tag (&start, &end, tag, FALSE); gtk_text_btree_remove_tag_info (tree, tag); } @@ -5333,8 +5801,9 @@ gtk_text_btree_rebalance (GtkTextBTree *tree, { tree->root_node = node->children.node; tree->root_node->parent = NULL; - summary_list_destroy (node->summary); - g_free (node); + + node->children.node = NULL; + gtk_text_btree_node_free_empty (tree, node); } return; } @@ -5438,8 +5907,10 @@ gtk_text_btree_rebalance (GtkTextBTree *tree, recompute_node_counts (tree, node); node->next = other->next; node->parent->num_children--; - summary_list_destroy (other->summary); - g_free (other); + + other->children.node = NULL; + other->children.line = NULL; + gtk_text_btree_node_free_empty (tree, other); continue; } @@ -5496,7 +5967,7 @@ post_insert_fixup (GtkTextBTree *tree, } if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); } static GtkTextTagInfo* @@ -5532,14 +6003,20 @@ gtk_text_btree_get_tag_info (GtkTextBTree *tree, { /* didn't find it, create. */ - info = g_new (GtkTextTagInfo, 1); + info = g_slice_new (GtkTextTagInfo); info->tag = tag; - gtk_object_ref (GTK_OBJECT (tag)); + g_object_ref (tag); info->tag_root = NULL; info->toggle_count = 0; tree->tag_infos = g_slist_prepend (tree->tag_infos, info); + +#if 0 + g_print ("Created tag info %p for tag %s(%p)\n", + info, info->tag->name ? info->tag->name : "anon", + info->tag); +#endif } return info; @@ -5560,6 +6037,12 @@ gtk_text_btree_remove_tag_info (GtkTextBTree *tree, info = list->data; if (info->tag == tag) { +#if 0 + g_print ("Removing tag info %p for tag %s(%p)\n", + info, info->tag->name ? info->tag->name : "anon", + info->tag); +#endif + if (prev != NULL) { prev->next = list->next; @@ -5571,17 +6054,15 @@ gtk_text_btree_remove_tag_info (GtkTextBTree *tree, list->next = NULL; g_slist_free (list); - gtk_object_unref (GTK_OBJECT (info->tag)); + g_object_unref (info->tag); - g_free (info); + g_slice_free (GtkTextTagInfo, info); return; } + prev = list; list = g_slist_next (list); } - - g_assert_not_reached (); - return; } static void @@ -5725,7 +6206,7 @@ recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node) gtk_text_btree_node_check_valid (node, view->view_id); view = view->next; } - + /* * Scan through the GtkTextBTreeNode's tag records again and delete any Summary * records that still have a zero count, or that have all the toggles. @@ -5875,7 +6356,7 @@ _gtk_change_node_toggle_count (GtkTextBTreeNode *node, */ GtkTextBTreeNode *rootnode = info->tag_root; - summary = (Summary *) g_malloc (sizeof (Summary)); + summary = g_slice_new (Summary); summary->info = info; summary->toggle_count = info->toggle_count - delta; summary->next = rootnode->summary; @@ -5884,7 +6365,7 @@ _gtk_change_node_toggle_count (GtkTextBTreeNode *node, rootLevel = rootnode->level; info->tag_root = rootnode; } - summary = (Summary *) g_malloc (sizeof (Summary)); + summary = g_slice_new (Summary); summary->info = info; summary->toggle_count = delta; summary->next = node->summary; @@ -5965,7 +6446,7 @@ _gtk_change_node_toggle_count (GtkTextBTreeNode *node, * * inc_count -- * - * This is a utility procedure used by gtk_text_btree_get_tags. It + * This is a utility procedure used by _gtk_text_btree_get_tags. It * increments the count for a particular tag, adding a new * entry for that tag if there wasn't one previously. * @@ -6034,8 +6515,8 @@ gtk_text_btree_link_segment (GtkTextLineSegment *seg, GtkTextLine *line; GtkTextBTree *tree; - line = gtk_text_iter_get_text_line (iter); - tree = gtk_text_iter_get_btree (iter); + line = _gtk_text_iter_get_text_line (iter); + tree = _gtk_text_iter_get_btree (iter); prev = gtk_text_line_segment_split (iter); if (prev == NULL) @@ -6052,7 +6533,7 @@ gtk_text_btree_link_segment (GtkTextLineSegment *seg, segments_changed (tree); if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_btree_check (tree); + _gtk_text_btree_check (tree); } static void @@ -6148,18 +6629,45 @@ _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr, */ static void -gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node, +gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree, + GtkTextBTreeNode *node, NodeData *nd) { gint width; gint height; gboolean valid; + BTreeView *view; + + view = tree->views; + while (view != NULL) + { + if (view->view_id == nd->view_id) + break; + + view = view->next; + } + + if (view == NULL) + g_error ("Node has data for a view %p no longer attached to the tree", + nd->view_id); + gtk_text_btree_node_compute_view_aggregates (node, nd->view_id, &width, &height, &valid); + + /* valid aggregate not checked the same as width/height, because on + * btree rebalance we can have invalid nodes where all lines below + * them are actually valid, due to moving lines around between + * nodes. + * + * The guarantee is that if there are invalid lines the node is + * invalid - we don't guarantee that if the node is invalid there + * are invalid lines. + */ + if (nd->width != width || nd->height != height || - !nd->valid != !valid) + (nd->valid && !valid)) { g_error ("Node aggregates for view %p are invalid:\n" "Are (%d,%d,%s), should be (%d,%d,%s)", @@ -6170,7 +6678,8 @@ gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node, } static void -gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node) +gtk_text_btree_node_check_consistency (GtkTextBTree *tree, + GtkTextBTreeNode *node) { GtkTextBTreeNode *childnode; Summary *summary, *summary2; @@ -6201,7 +6710,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node) nd = node->node_data; while (nd != NULL) { - gtk_text_btree_node_view_check_consistency (node, nd); + gtk_text_btree_node_view_check_consistency (tree, node, nd); nd = nd->next; } @@ -6270,7 +6779,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node) g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)", node->level, childnode->level); } - gtk_text_btree_node_check_consistency (childnode); + gtk_text_btree_node_check_consistency (tree, childnode); for (summary = childnode->summary; summary != NULL; summary = summary->next) { @@ -6399,22 +6908,22 @@ list_of_tags (GtkTextTagTable *table) } void -gtk_text_btree_check (GtkTextBTree *tree) +_gtk_text_btree_check (GtkTextBTree *tree) { Summary *summary; GtkTextBTreeNode *node; GtkTextLine *line; GtkTextLineSegment *seg; GtkTextTag *tag; - GSList *taglist = NULL; + GSList *all_tags, *taglist = NULL; int count; GtkTextTagInfo *info; /* * Make sure that the tag toggle counts and the tag root pointers are OK. */ - for (taglist = list_of_tags (tree->table); - taglist != NULL ; taglist = taglist->next) + all_tags = list_of_tags (tree->table); + for (taglist = all_tags; taglist != NULL ; taglist = taglist->next) { tag = taglist->data; info = gtk_text_btree_get_existing_tag_info (tree, tag); @@ -6425,19 +6934,19 @@ gtk_text_btree_check (GtkTextBTree *tree) { if (info->toggle_count != 0) { - g_error ("gtk_text_btree_check found \"%s\" with toggles (%d) but no root", + g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root", tag->name, info->toggle_count); } continue; /* no ranges for the tag */ } else if (info->toggle_count == 0) { - g_error ("gtk_text_btree_check found root for \"%s\" with no toggles", + g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles", tag->name); } else if (info->toggle_count & 1) { - g_error ("gtk_text_btree_check found odd toggle count for \"%s\" (%d)", + g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)", tag->name, info->toggle_count); } for (summary = node->summary; summary != NULL; @@ -6445,7 +6954,7 @@ gtk_text_btree_check (GtkTextBTree *tree) { if (summary->info->tag == tag) { - g_error ("gtk_text_btree_check found root GtkTextBTreeNode with summary info"); + g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info"); } } count = 0; @@ -6466,7 +6975,7 @@ gtk_text_btree_check (GtkTextBTree *tree) } else { - GtkTextLineSegmentClass * last = NULL; + const GtkTextLineSegmentClass *last = NULL; for (line = node->children.line ; line != NULL ; line = line->next) @@ -6492,21 +7001,20 @@ gtk_text_btree_check (GtkTextBTree *tree) } if (count != info->toggle_count) { - g_error ("gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)", + g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)", info->toggle_count, tag->name, count); } } } - g_slist_free (taglist); - taglist = NULL; + g_slist_free (all_tags); /* * Call a recursive procedure to do the main body of checks. */ node = tree->root_node; - gtk_text_btree_node_check_consistency (tree->root_node); + gtk_text_btree_node_check_consistency (tree, tree->root_node); /* * Make sure that there are at least two lines in the text and @@ -6515,11 +7023,11 @@ gtk_text_btree_check (GtkTextBTree *tree) if (node->num_lines < 2) { - g_error ("gtk_text_btree_check: less than 2 lines in tree"); + g_error ("_gtk_text_btree_check: less than 2 lines in tree"); } if (node->num_chars < 2) { - g_error ("gtk_text_btree_check: less than 2 chars in tree"); + g_error ("_gtk_text_btree_check: less than 2 chars in tree"); } while (node->level > 0) { @@ -6549,44 +7057,44 @@ gtk_text_btree_check (GtkTextBTree *tree) } if (seg->type != >k_text_char_type) { - g_error ("gtk_text_btree_check: last line has bogus segment type"); + g_error ("_gtk_text_btree_check: last line has bogus segment type"); } if (seg->next != NULL) { - g_error ("gtk_text_btree_check: last line has too many segments"); + g_error ("_gtk_text_btree_check: last line has too many segments"); } if (seg->byte_count != 1) { - g_error ("gtk_text_btree_check: last line has wrong # characters: %d", + g_error ("_gtk_text_btree_check: last line has wrong # characters: %d", seg->byte_count); } if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0)) { - g_error ("gtk_text_btree_check: last line had bad value: %s", + g_error ("_gtk_text_btree_check: last line had bad value: %s", seg->body.chars); } } -void gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line); -void gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg); -void gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent); -void gtk_text_btree_spew_line_short (GtkTextLine *line, int indent); +void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line); +void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg); +void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent); +void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent); void -gtk_text_btree_spew (GtkTextBTree *tree) +_gtk_text_btree_spew (GtkTextBTree *tree) { GtkTextLine * line; int real_line; printf ("%d lines in tree %p\n", - gtk_text_btree_line_count (tree), tree); + _gtk_text_btree_line_count (tree), tree); - line = gtk_text_btree_get_line (tree, 0, &real_line); + line = _gtk_text_btree_get_line (tree, 0, &real_line); while (line != NULL) { - gtk_text_btree_spew_line (tree, line); - line = gtk_text_line_next (line); + _gtk_text_btree_spew_line (tree, line); + line = _gtk_text_line_next (line); } printf ("=================== Tag information\n"); @@ -6617,12 +7125,12 @@ gtk_text_btree_spew (GtkTextBTree *tree) printf ("=================== Tree nodes\n"); { - gtk_text_btree_spew_node (tree->root_node, 0); + _gtk_text_btree_spew_node (tree->root_node, 0); } } void -gtk_text_btree_spew_line_short (GtkTextLine *line, int indent) +_gtk_text_btree_spew_line_short (GtkTextLine *line, int indent) { gchar * spaces; GtkTextLineSegment *seg; @@ -6631,8 +7139,8 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent) printf ("%sline %p chars %d bytes %d\n", spaces, line, - gtk_text_line_char_count (line), - gtk_text_line_byte_count (line)); + _gtk_text_line_char_count (line), + _gtk_text_line_byte_count (line)); seg = line->segments; while (seg != NULL) @@ -6644,7 +7152,7 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent) s = str; while (*s) { - if (*s == '\n') + if (*s == '\n' || *s == '\r') *s = '\\'; ++s; } @@ -6680,7 +7188,7 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent) } void -gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent) +_gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent) { gchar * spaces; GtkTextBTreeNode *iter; @@ -6707,7 +7215,7 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent) iter = node->children.node; while (iter != NULL) { - gtk_text_btree_spew_node (iter, indent + 2); + _gtk_text_btree_spew_node (iter, indent + 2); iter = iter->next; } @@ -6717,7 +7225,7 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent) GtkTextLine *line = node->children.line; while (line != NULL) { - gtk_text_btree_spew_line_short (line, indent + 2); + _gtk_text_btree_spew_line_short (line, indent + 2); line = line->next; } @@ -6725,24 +7233,24 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent) } void -gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line) +_gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line) { GtkTextLineSegment * seg; printf ("%4d| line: %p parent: %p next: %p\n", - gtk_text_line_get_number (line), line, line->parent, line->next); + _gtk_text_line_get_number (line), line, line->parent, line->next); seg = line->segments; while (seg != NULL) { - gtk_text_btree_spew_segment (tree, seg); + _gtk_text_btree_spew_segment (tree, seg); seg = seg->next; } } void -gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg) +_gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg) { printf (" segment: %p type: %s bytes: %d chars: %d\n", seg, seg->type->name, seg->byte_count, seg->char_count); @@ -6776,4 +7284,5 @@ gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg) } } - +#define __GTK_TEXT_BTREE_C__ +#include "gtkaliasdef.c"