summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2001-09-24 23:12:39 +0000
committerHavoc Pennington <hp@src.gnome.org>2001-09-24 23:12:39 +0000
commit1e3b62fd1305a651d279dcf3c2f5929273a8e7a7 (patch)
treea163a43de9cb1a36d66d05202d66736cc62ca21c
parent7837ddcde434db3391766c6b7a61107306ed3cdf (diff)
downloadgtk+-1e3b62fd1305a651d279dcf3c2f5929273a8e7a7.tar.gz
Get rid of the newline-that-could-not-be-deleted; buffers may now be
2001-09-24 Havoc Pennington <hp@redhat.com> * 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.
-rw-r--r--ChangeLog10
-rw-r--r--ChangeLog.pre-2-010
-rw-r--r--ChangeLog.pre-2-1010
-rw-r--r--ChangeLog.pre-2-210
-rw-r--r--ChangeLog.pre-2-410
-rw-r--r--ChangeLog.pre-2-610
-rw-r--r--ChangeLog.pre-2-810
-rw-r--r--docs/reference/gtk/text_widget.sgml8
-rw-r--r--docs/reference/gtk/tmpl/gtkmenu.sgml6
-rw-r--r--gtk/gtktextbtree.c198
-rw-r--r--gtk/gtktextbtree.h13
-rw-r--r--gtk/gtktextbuffer.c55
-rw-r--r--gtk/gtktextiter.c82
-rw-r--r--gtk/gtktextlayout.c34
-rw-r--r--tests/testtextbuffer.c34
15 files changed, 372 insertions, 128 deletions
diff --git a/ChangeLog b/ChangeLog
index 2cd22ef4bf..38dc70b5b7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2001-09-24 Havoc Pennington <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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 <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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 <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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 <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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 <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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 <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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 <hp@redhat.com>
+
+ * 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 <otaylor@redhat.com>
* 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).
</footnote>
</para>
+<para>
+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.
+</para>
+
</refsect1>
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.
</para>
+<!-- # Unused Parameters # -->
@menu: a #GtkMenu.
@child: The #GtkMenuItem to add.
-<!-- # Unused Parameters # -->
@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.
</para>
+<!-- # Unused Parameters # -->
@menu: a #GtkMenu.
@child: The #GtkMenuItem to add.
-<!-- # Unused Parameters # -->
@menu_child:
@m:
@c:
@@ -122,10 +122,10 @@ Adds a new #GtkMenuItem to the menu's item list at the position
indicated by @position.
</para>
+<!-- # Unused Parameters # -->
@menu: a #GtkMenu.
@child: The #GtkMenuItem to add.
@pos:
-<!-- # Unused Parameters # -->
@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;
- 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;
};
@@ -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->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
@@ -1904,10 +1921,19 @@ _gtk_text_btree_get_line (GtkTextBTree *tree,
}
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)
{
@@ -3173,6 +3286,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)
{
GtkTextBTreeNode *node;
@@ -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 == &gtk_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 <stdio.h>
+#include <string.h>
#include <gtk/gtk.h>
#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);