diff --git a/ChangeLog b/ChangeLog index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 2cd22ef4bf..38dc70b5b7 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,13 @@ +2001-09-24 Havoc Pennington + + * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c, + gtktextlayout.c: + Get rid of the newline-that-could-not-be-deleted; buffers may + now be zero-length. Much easier to fix than expected, once + I figured out the right way to do it. However, there are + various subtle bugs introduced by this that will have to get + sorted out. Please use bugzilla. + Mon Sep 24 15:09:08 2001 Owen Taylor * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a diff --git a/docs/reference/gtk/text_widget.sgml b/docs/reference/gtk/text_widget.sgml index 27a059a96d..42d5525e29 100644 --- a/docs/reference/gtk/text_widget.sgml +++ b/docs/reference/gtk/text_widget.sgml @@ -102,6 +102,14 @@ between the old and new positions). + +Text buffers always contain at least one line, but may be empty (that is, +buffers can contain zero characters). The last line in the text buffer never +ends in a line separator (such as newline); the other lines in the buffer always +end in a line separator. Line separators count as characters when computing +character counts and character offsets. + + diff --git a/docs/reference/gtk/tmpl/gtkmenu.sgml b/docs/reference/gtk/tmpl/gtkmenu.sgml index b8f6c87935..b51c366a4b 100644 --- a/docs/reference/gtk/tmpl/gtkmenu.sgml +++ b/docs/reference/gtk/tmpl/gtkmenu.sgml @@ -96,9 +96,9 @@ Creates a new #GtkMenu. Adds a new #GtkMenuItem to the end of the menu's item list. + @menu: a #GtkMenu. @child: The #GtkMenuItem to add. - @m: @c: @@ -108,9 +108,9 @@ Adds a new #GtkMenuItem to the end of the menu's item list. Adds a new #GtkMenuItem to the beginning of the menu's item list. + @menu: a #GtkMenu. @child: The #GtkMenuItem to add. - @menu_child: @m: @c: @@ -122,10 +122,10 @@ Adds a new #GtkMenuItem to the menu's item list at the position indicated by @position. + @menu: a #GtkMenu. @child: The #GtkMenuItem to add. @pos: - @position: The position in the item list where @child is added. Positions are numbered from 0 to n-1. diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c index 810f3405f8..73cf60de37 100644 --- a/gtk/gtktextbtree.c +++ b/gtk/gtktextbtree.c @@ -185,22 +185,32 @@ struct _GtkTextBTree { guint tag_changed_handler; guint tag_removed_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; + /* 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; }; @@ -409,9 +419,15 @@ _gtk_text_btree_new (GtkTextTagTable *table, tree->chars_changed_stamp = g_random_int (); tree->segments_changed_stamp = g_random_int (); - tree->end_iter_line_stamp = tree->chars_changed_stamp - 1; - tree->end_iter_line = NULL; + 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 (G_OBJECT (tree->table)); tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table), @@ -570,6 +586,7 @@ _gtk_text_btree_delete (GtkTextIter *start, _gtk_text_btree_check (tree); { + /* FIXME this code should no longer be required */ /* * 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 @@ -1903,11 +1920,20 @@ _gtk_text_btree_get_line (GtkTextBTree *tree, return line; } +GtkTextLine* +_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_at_char (GtkTextBTree *tree, - gint char_index, - gint *line_start_index, - gint *real_char_index) + gint char_index, + gint *line_start_index, + gint *real_char_index) { GtkTextBTreeNode *node; GtkTextLine *line; @@ -1918,10 +1944,13 @@ _gtk_text_btree_get_line_at_char (GtkTextBTree *tree, 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; @@ -2242,8 +2271,10 @@ _gtk_text_btree_line_count (GtkTextBTree *tree) gint _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 @@ -3137,6 +3168,88 @@ _gtk_text_line_is_last (GtkTextLine *line, return line == get_last_line (tree); } +static void +ensure_end_iter_line (GtkTextBTree *tree) +{ + if (tree->end_iter_line_stamp != tree->chars_changed_stamp) + { + int n_lines; + int real_line; + + /* n_lines is without the magic line at the end */ + n_lines = _gtk_text_btree_line_count (tree); + + g_assert (n_lines >= 1); + + tree->end_iter_line = _gtk_text_btree_get_line (tree, n_lines - 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; + } +} + +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) { @@ -3172,6 +3285,23 @@ _gtk_text_line_next (GtkTextLine *line) } } +GtkTextLine* +_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) { @@ -3950,9 +4080,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; @@ -3968,17 +4098,17 @@ _gtk_text_line_next_could_contain_tag (GtkTextLine *line, /* 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) @@ -4330,7 +4460,7 @@ summary_list_destroy (Summary *summary) 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; @@ -4342,11 +4472,11 @@ get_last_line (GtkTextBTree *tree) 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; } /* diff --git a/gtk/gtktextbtree.h b/gtk/gtktextbtree.h index 4e3b122368..b3434ff4d6 100644 --- a/gtk/gtktextbtree.h +++ b/gtk/gtktextbtree.h @@ -23,6 +23,11 @@ guint _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree); guint _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree); void _gtk_text_btree_segments_changed (GtkTextBTree *tree); +gboolean _gtk_text_btree_is_end (GtkTextBTree *tree, + GtkTextLine *line, + GtkTextLineSegment *seg, + int byte_index, + int char_offset); /* Indexable segment mutation */ @@ -82,6 +87,7 @@ void _gtk_text_btree_tag (const GtkTextIter *start, GtkTextLine * _gtk_text_btree_get_line (GtkTextBTree *tree, gint line_number, gint *real_line_number); +GtkTextLine * _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree); GtkTextLine * _gtk_text_btree_get_line_at_char (GtkTextBTree *tree, gint char_index, gint *line_start_index, @@ -205,9 +211,12 @@ gboolean _gtk_text_line_byte_has_tag (GtkTextLine GtkTextBTree *tree, gint byte_in_line, GtkTextTag *tag); -gboolean _gtk_text_line_is_last (GtkTextLine *line, - GtkTextBTree *tree); +gboolean _gtk_text_line_is_last (GtkTextLine *line, + GtkTextBTree *tree); +gboolean _gtk_text_line_contains_end_iter (GtkTextLine *line, + GtkTextBTree *tree); GtkTextLine * _gtk_text_line_next (GtkTextLine *line); +GtkTextLine * _gtk_text_line_next_excluding_last (GtkTextLine *line); GtkTextLine * _gtk_text_line_previous (GtkTextLine *line); void _gtk_text_line_add_data (GtkTextLine *line, GtkTextLineData *data); diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 48fe365726..3a11c9dfa8 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -407,11 +407,8 @@ gtk_text_buffer_get_tag_table (GtkTextBuffer *buffer) * @text: UTF-8 text to insert * @len: length of @text in bytes * - * Deletes current contents of @buffer, and inserts @text instead. If - * @text doesn't end with a newline, a newline is added; - * #GtkTextBuffer contents must always end with a newline. If @text - * ends with a newline, the new buffer contents will be exactly - * @text. If @len is -1, @text must be nul-terminated. + * Deletes current contents of @buffer, and inserts @text instead. If + * @len is -1, @text must be nul-terminated. @text must be valid UTF-8. **/ void gtk_text_buffer_set_text (GtkTextBuffer *buffer, @@ -426,12 +423,6 @@ gtk_text_buffer_set_text (GtkTextBuffer *buffer, if (len < 0) len = strlen (text); - /* Chop newline, since the buffer will already have one - * in it. - */ - if (len > 0 && text[len-1] == '\n') - len -= 1; - gtk_text_buffer_get_bounds (buffer, &start, &end); gtk_text_buffer_delete (buffer, &start, &end); @@ -1135,14 +1126,6 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer, gtk_text_iter_order (start, end); - /* Somewhat annoyingly, if you try to delete the final newline - * the BTree will put it back; which means you can't deduce the - * final contents of the buffer purely by monitoring insert/delete - * signals on the buffer. But if you delete the final newline, any - * tags on the newline will go away, oddly. See comment in - * gtktextbtree.c. This is all sort of annoying, but really hard - * to fix. - */ g_signal_emit (G_OBJECT (buffer), signals[DELETE_RANGE], 0, @@ -1163,11 +1146,6 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer, * calling this function; however, the @start and @end will be * re-initialized to point to the location where text was deleted. * - * Note that the final newline in the buffer may not be deleted; a - * #GtkTextBuffer always contains at least one newline. You can - * safely include the final newline in the range [@start,@end) but it - * won't be affected by the deletion. - * **/ void gtk_text_buffer_delete (GtkTextBuffer *buffer, @@ -1529,14 +1507,15 @@ gtk_text_buffer_mark_set (GtkTextBuffer *buffer, GtkTextMark *mark) { /* IMO this should NOT work like insert_text and delete_range, - where the real action happens in the default handler. - - The reason is that the default handler would be _required_, - i.e. the whole widget would start breaking and segfaulting - if the default handler didn't get run. So you can't really - override the default handler or stop the emission; that is, - this signal is purely for notification, and not to allow users - to modify the default behavior. */ + * where the real action happens in the default handler. + * + * The reason is that the default handler would be _required_, + * i.e. the whole widget would start breaking and segfaulting if the + * default handler didn't get run. So you can't really override the + * default handler or stop the emission; that is, this signal is + * purely for notification, and not to allow users to modify the + * default behavior. + */ g_object_ref (G_OBJECT (mark)); @@ -2701,8 +2680,6 @@ clipboard_get_contents_cb (GtkClipboard *clipboard, GtkTextIter start, end; gtk_text_buffer_get_bounds (contents, &start, &end); - /* strip off the trailing newline, it isn't part of the text that was cut */ - gtk_text_iter_backward_char (&end); str = gtk_text_iter_get_visible_text (&start, &end); gtk_selection_data_set_text (selection_data, str); @@ -3466,8 +3443,16 @@ _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer *buffer, g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); g_return_val_if_fail (anywhere_in_line != NULL, NULL); - g_return_val_if_fail (!gtk_text_iter_is_end (anywhere_in_line), NULL); + /* special-case for empty last line in buffer */ + if (gtk_text_iter_is_end (anywhere_in_line) && + gtk_text_iter_get_line_offset (anywhere_in_line) == 0) + { + if (char_len) + *char_len = 0; + return NULL; + } + /* FIXME we also need to recompute log attrs if the language tag at * the start of a paragraph changes */ diff --git a/gtk/gtktextiter.c b/gtk/gtktextiter.c index ddbd273b4c..6f7c778cdc 100644 --- a/gtk/gtktextiter.c +++ b/gtk/gtktextiter.c @@ -1532,7 +1532,19 @@ gtk_text_iter_is_end (const GtkTextIter *iter) check_invariants (iter); - return _gtk_text_line_is_last (real->line, real->tree); + if (!_gtk_text_line_contains_end_iter (real->line, real->tree)) + return FALSE; + + /* Now we need the segments validated */ + real = gtk_text_iter_make_real (iter); + + if (real == NULL) + return FALSE; + + return _gtk_text_btree_is_end (real->tree, real->line, + real->segment, + real->segment_byte_offset, + real->segment_char_offset); } /** @@ -1701,18 +1713,23 @@ gtk_text_iter_get_attributes (const GtkTextIter *iter, /* The return value of this indicates WHETHER WE MOVED. * The return value of public functions indicates * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE) + * + * This function will not change the iterator if + * it's already on the last (end iter) line, i.e. it + * won't move to the end of the last line. */ static gboolean forward_line_leaving_caches_unmodified (GtkTextRealIter *real) { - GtkTextLine *new_line; - - new_line = _gtk_text_line_next (real->line); - - g_assert (new_line != real->line); - - if (new_line != NULL) + if (!_gtk_text_line_contains_end_iter (real->line, real->tree)) { + GtkTextLine *new_line; + + new_line = _gtk_text_line_next (real->line); + g_assert (new_line); + g_assert (new_line != real->line); + g_assert (!_gtk_text_line_is_last (new_line, real->tree)); + real->line = new_line; real->line_byte_offset = 0; @@ -1731,24 +1748,11 @@ forward_line_leaving_caches_unmodified (GtkTextRealIter *real) } else { - /* There is no way to move forward; we were already - at the "end" index. (the end index is the last - line pointer, segment_byte_offset of 0) */ - - g_assert (real->line_char_offset == 0 || - real->line_byte_offset == 0); - - /* The only indexable segment allowed on the bogus - line at the end is a single char segment containing - a newline. */ - if (real->segments_changed_stamp == - _gtk_text_btree_get_segments_changed_stamp (real->tree)) - { - g_assert (real->segment->type == >k_text_char_type); - g_assert (real->segment->char_count == 1); - } - /* We leave real->line as-is */ - + /* There is no way to move forward a line; we were already at + * the line containing the end iterator. + * However we may not be at the end iterator itself. + */ + return FALSE; } } @@ -1950,6 +1954,8 @@ _gtk_text_iter_forward_indexable_segment (GtkTextIter *iter) { /* End of buffer */ + g_assert (gtk_text_iter_is_end (iter)); + check_invariants (iter); return FALSE; @@ -2442,6 +2448,11 @@ gtk_text_iter_forward_line (GtkTextIter *iter) } else { + /* On the last line, move to end of it */ + + if (!gtk_text_iter_is_end (iter)) + gtk_text_iter_forward_to_end (iter); + check_invariants (iter); return FALSE; } @@ -2789,7 +2800,9 @@ test_log_attrs (const GtkTextIter *iter, offset = gtk_text_iter_get_line_offset (iter); - g_assert (char_len > 0); + /* char_len may be 0 and attrs will be NULL if so, if + * iter is the end iter and the last line is empty + */ if (offset < char_len) result = (* func) (attrs, offset, 0, char_len); @@ -2814,8 +2827,10 @@ find_line_log_attrs (const GtkTextIter *iter, iter, &char_len); offset = gtk_text_iter_get_line_offset (iter); - - g_assert (char_len > 0); + + /* char_len may be 0 and attrs will be NULL if so, if + * iter is the end iter and the last line is empty + */ if (offset < char_len) result = (* func) (attrs, offset, 0, char_len, found_offset, @@ -3734,7 +3749,7 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter, current_line = real->line; next_line = _gtk_text_line_next_could_contain_tag (current_line, - real->tree, tag); + real->tree, tag); while (_gtk_text_iter_forward_indexable_segment (iter)) { @@ -3756,8 +3771,8 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter, current_line = real->line; next_line = _gtk_text_line_next_could_contain_tag (current_line, - real->tree, - tag); + real->tree, + tag); } if (gtk_text_iter_toggles_tag (iter, tag)) @@ -5175,5 +5190,8 @@ _gtk_text_iter_check (const GtkTextIter *iter) g_error ("wrong char index was cached"); } } + + if (_gtk_text_line_is_last (real->line, real->tree)) + g_error ("Iterator was on last line (past the end iterator)"); } diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index 2b77c91b3c..bcb1aceaff 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -578,13 +578,11 @@ gtk_text_layout_get_lines (GtkTextLayout *layout, /* -1 since bottom_y is one past */ last_btree_line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), - layout, bottom_y - 1, NULL); + layout, bottom_y - 1, NULL); if (!last_btree_line) last_btree_line = - _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer), - _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, - NULL); + _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer)); g_assert (last_btree_line != NULL); @@ -596,7 +594,7 @@ gtk_text_layout_get_lines (GtkTextLayout *layout, if (line == last_btree_line) break; - line = _gtk_text_line_next (line); + line = _gtk_text_line_next_excluding_last (line); } retval = g_slist_reverse (retval); @@ -722,7 +720,7 @@ gtk_text_layout_real_invalidate (GtkTextLayout *layout, if (line == last_line) break; - line = _gtk_text_line_next (line); + line = _gtk_text_line_next_excluding_last (line); } gtk_text_layout_invalidated (layout); @@ -862,7 +860,7 @@ gtk_text_layout_validate_yrange (GtkTextLayout *layout, } seen += line_data->height; - line = _gtk_text_line_next (line); + line = _gtk_text_line_next_excluding_last (line); } /* If we found and validated any invalid lines, update size and @@ -2005,6 +2003,9 @@ line_display_index_to_iter (GtkTextLayout *layout, gint index, gint trailing) { + g_return_if_fail (!_gtk_text_line_is_last (display->line, + _gtk_text_buffer_get_btree (layout->buffer))); + if (index >= display->insert_index + layout->preedit_len) index -= layout->preedit_len; else if (index > display->insert_index) @@ -2066,8 +2067,8 @@ get_line_at_y (GtkTextLayout *layout, layout, y, line_top); if (*line == NULL) { - *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer), - _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL); + *line = _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer)); + if (line_top) *line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), @@ -2306,9 +2307,8 @@ find_display_line_below (GtkTextLayout *layout, if (!line) { line = - _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer), - _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, - NULL); + _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer)); + line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout); @@ -2346,7 +2346,7 @@ find_display_line_below (GtkTextLayout *layout, line_top += display->bottom_margin; gtk_text_layout_free_line_display (layout, display); - next = _gtk_text_line_next (line); + next = _gtk_text_line_next_excluding_last (line); if (!next) found_line = line; @@ -2374,8 +2374,8 @@ find_display_line_above (GtkTextLayout *layout, line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top); if (!line) { - line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer), - _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL); + line = _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer)); + line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout); } @@ -2658,7 +2658,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout, gtk_text_layout_free_line_display (layout, display); - line = _gtk_text_line_next (line); + line = _gtk_text_line_next_excluding_last (line); } return @@ -2939,7 +2939,7 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout, } else if (new_index > byte_count) { - line = _gtk_text_line_next (line); + line = _gtk_text_line_next_excluding_last (line); if (!line) goto done; diff --git a/tests/testtextbuffer.c b/tests/testtextbuffer.c index 4ac0dafefc..f58383e9ea 100644 --- a/tests/testtextbuffer.c +++ b/tests/testtextbuffer.c @@ -2,6 +2,7 @@ #include +#include #include #include "../gtk/gtktexttypes.h" /* Private header, for UNKNOWN_CHAR */ @@ -28,7 +29,8 @@ main (int argc, char** argv) int n; gunichar ch; GtkTextIter start, end; - + gchar *text; + gtk_init (&argc, &argv); /* Check UTF8 unknown char thing */ @@ -49,12 +51,34 @@ main (int argc, char** argv) g_error ("%d lines, expected 1", n); n = gtk_text_buffer_get_char_count (buffer); - if (n != 1) - g_error ("%d chars, expected 1", n); + if (n != 0) + g_error ("%d chars, expected 0", n); /* Run gruesome alien test suite on buffer */ run_tests (buffer); + /* Check set/get text */ + gtk_text_buffer_set_text (buffer, "Hello", -1); + if (gtk_text_buffer_get_char_count (buffer) != g_utf8_strlen ("Hello", -1)) + g_error ("Wrong number of chars (%d not %d)", + gtk_text_buffer_get_char_count (buffer), + (int) g_utf8_strlen ("Hello", -1)); + gtk_text_buffer_get_bounds (buffer, &start, &end); + text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); + if (strcmp (text, "Hello") != 0) + g_error ("Got '%s' as buffer contents", text); + g_free (text); + + gtk_text_buffer_set_text (buffer, "", -1); + + n = gtk_text_buffer_get_line_count (buffer); + if (n != 1) + g_error ("%d lines, expected 1", n); + + n = gtk_text_buffer_get_char_count (buffer); + if (n != 0) + g_error ("%d chars, expected 0", n); + /* Put stuff in the buffer */ fill_buffer (buffer); @@ -73,8 +97,8 @@ main (int argc, char** argv) g_error ("%d lines, expected 1", n); n = gtk_text_buffer_get_char_count (buffer); - if (n != 1) - g_error ("%d chars, expected 1", n); + if (n != 0) + g_error ("%d chars, expected 0", n); run_tests (buffer);