summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2000-10-04 14:23:09 +0000
committerHavoc Pennington <hp@src.gnome.org>2000-10-04 14:23:09 +0000
commit928c069883b807b67e964b90aa8dd3d5f9bc5bb2 (patch)
tree5799591cec57c1abb9fb0f156721b9e546e07fca /gtk
parent69ac9451a6dac6513f1325122bdf9664040267c9 (diff)
downloadgtk+-928c069883b807b67e964b90aa8dd3d5f9bc5bb2.tar.gz
clean up trailing whitespace, add extensive tests for tag toggle
2000-10-03 Havoc Pennington <hp@redhat.com> * gtk/testtextbuffer.c: clean up trailing whitespace, add extensive tests for tag toggle iteration. * gtk/gtktextiter.c (MAX_LINEAR_SCAN): decrease linear scan distance * gtk/gtktextbtree.c (gtk_text_line_next_could_contain_tag): optimize case where the tag root is on level 1 (gtk_text_line_previous_could_contain_tag): attempt to implement (gtk_text_line_next_could_contain_tag): Abstract out node_compare functionality * gtk/gtktextiter.c (gtk_text_iter_backward_to_tag_toggle): Implement this, though not very efficiently. * gtk/gtktextiterprivate.h: reformat * gtk/gtktextiter.c (gtk_text_iter_get_char): return 0 on the end iterator * gtk/gtktextbuffer.c (gtk_text_buffer_delete_interactive): Fix this to properly revalidate the iterators. * gtk/gtktextview.c (gtk_text_view_delete): fix control-K to work properly at the end of the line (and therefore on empty lines) * gtk/gtktextbtree.c (gtk_text_btree_get_selection_bounds): Gee, maybe we should return a value...
Diffstat (limited to 'gtk')
-rw-r--r--gtk/gtktextbtree.c457
-rw-r--r--gtk/gtktextbuffer.c30
-rw-r--r--gtk/gtktextiter.c460
-rw-r--r--gtk/gtktextiterprivate.h19
-rw-r--r--gtk/gtktextlayout.c8
-rw-r--r--gtk/gtktextlayout.h2
-rw-r--r--gtk/gtktextview.c22
-rw-r--r--gtk/testtextbuffer.c440
8 files changed, 1202 insertions, 236 deletions
diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c
index a82f0d1caa..4fbf2ddd71 100644
--- a/gtk/gtktextbtree.c
+++ b/gtk/gtktextbtree.c
@@ -414,9 +414,9 @@ gtk_text_btree_new (GtkTextTagTable *table,
tree->mark_table = g_hash_table_new(g_str_hash, g_str_equal);
/* 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
- */
+ * we'd have some circularity issues. The buffer always
+ * lasts longer than the BTree
+ */
tree->buffer = buffer;
{
@@ -434,7 +434,6 @@ gtk_text_btree_new (GtkTextTagTable *table,
FALSE);
tree->insert_mark->body.mark.not_deleteable = TRUE;
-
tree->insert_mark->body.mark.visible = TRUE;
tree->selection_bound_mark =
@@ -2486,6 +2485,8 @@ gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
if (end)
*end = tmp_end;
+
+ return FALSE;
}
else
{
@@ -2502,7 +2503,7 @@ 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;
@@ -2585,7 +2586,7 @@ gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
void
gtk_text_mark_set_visible (GtkTextMark *mark,
- gboolean setting)
+ gboolean setting)
{
GtkTextLineSegment *seg;
@@ -2599,7 +2600,7 @@ gtk_text_mark_set_visible (GtkTextMark *mark,
{
seg->body.mark.visible = setting;
- redisplay_mark(seg);
+ redisplay_mark (seg);
}
}
@@ -3052,12 +3053,12 @@ gtk_text_line_previous (GtkTextLine *line)
return prev;
}
- g_assert_not_reached();
+ g_assert_not_reached ();
return NULL;
}
void
-gtk_text_line_add_data (GtkTextLine *line,
+gtk_text_line_add_data (GtkTextLine *line,
GtkTextLineData *data)
{
g_return_if_fail(line != NULL);
@@ -3418,7 +3419,6 @@ gtk_text_line_char_to_byte (GtkTextLine *line,
g_warning("FIXME not implemented");
}
-
/* FIXME sync with char_locate (or figure out a clean
way to merge the two functions) */
void
@@ -3692,6 +3692,101 @@ gtk_text_line_char_to_byte_offsets(GtkTextLine *line,
}
}
+static gint
+node_compare (GtkTextBTreeNode *lhs,
+ GtkTextBTreeNode *rhs)
+{
+ GtkTextBTreeNode *iter;
+ GtkTextBTreeNode *node;
+ GtkTextBTreeNode *common_parent;
+ GtkTextBTreeNode *parent_of_lower;
+ GtkTextBTreeNode *parent_of_higher;
+ gboolean lhs_is_lower;
+ GtkTextBTreeNode *lower;
+ GtkTextBTreeNode *higher;
+
+ /* This function assumes that lhs and rhs are not underneath each
+ * other.
+ */
+
+ if (lhs == rhs)
+ return 0;
+
+ if (lhs->level < rhs->level)
+ {
+ lhs_is_lower = TRUE;
+ lower = lhs;
+ higher = rhs;
+ }
+ else
+ {
+ lhs_is_lower = FALSE;
+ lower = rhs;
+ higher = lhs;
+ }
+
+ /* Algorithm: find common parent of lhs/rhs. Save the child nodes
+ * of the common parent we used to reach the common parent; the
+ * ordering of these child nodes in the child list is the ordering
+ * of lhs and rhs.
+ */
+
+ /* Get on the same level (may be on same level already) */
+ node = lower;
+ while (node->level < higher->level)
+ node = node->parent;
+
+ g_assert (node->level == higher->level);
+
+ g_assert (node != higher); /* Happens if lower is underneath higher */
+
+ /* Go up until we have two children with a common parent.
+ */
+ parent_of_lower = node;
+ parent_of_higher = higher;
+
+ while (parent_of_lower->parent != parent_of_higher->parent)
+ {
+ parent_of_lower = parent_of_lower->parent;
+ parent_of_higher = parent_of_higher->parent;
+ }
+
+ g_assert (parent_of_lower->parent == parent_of_higher->parent);
+
+ common_parent = parent_of_lower->parent;
+
+ g_assert (common_parent != NULL);
+
+ /* See which is first in the list of common_parent's children */
+ iter = common_parent->children.node;
+ while (iter != NULL)
+ {
+ if (iter == parent_of_higher)
+ {
+ /* higher is less than lower */
+
+ if (lhs_is_lower)
+ return 1; /* lhs > rhs */
+ else
+ return -1;
+ }
+ else if (iter == parent_of_lower)
+ {
+ /* lower is less than higher */
+
+ if (lhs_is_lower)
+ return -1; /* lhs < rhs */
+ else
+ return 1;
+ }
+
+ iter = iter->next;
+ }
+
+ g_assert_not_reached ();
+ return 0;
+}
+
/* remember that tag == NULL means "any tag" */
GtkTextLine*
gtk_text_line_next_could_contain_tag(GtkTextLine *line,
@@ -3710,7 +3805,8 @@ gtk_text_line_next_could_contain_tag(GtkTextLine *line,
if (tag == NULL)
{
/* Right now we can only offer linear-search if the user wants
- to know about any tag toggle at all. */
+ * to know about any tag toggle at all.
+ */
return gtk_text_line_next (line);
}
@@ -3733,6 +3829,9 @@ gtk_text_line_next_could_contain_tag(GtkTextLine *line,
if (info->tag_root == NULL)
return NULL;
+
+ if (info->tag_root == line->parent)
+ return NULL; /* we were at the last line under the tag root */
/* We need to go up out of this node, and on to the next one with
toggles for the target tag. If we're below the tag root, we need to
@@ -3773,57 +3872,25 @@ gtk_text_line_next_could_contain_tag(GtkTextLine *line,
}
else
{
- GtkTextBTreeNode * iter;
- GtkTextBTreeNode * common_parent;
- GtkTextBTreeNode * parent_of_tag_root;
- GtkTextBTreeNode * parent_of_node;
-
- /* Find common parent between our current line, and the tag
- root. Save the child nodes of the common parent we used to get
- to the common parent; we then use these two child nodes to
- determine whether the ordering of the tag root and the current
- line in the tree. (Nice code cleanup: write
- gtk_btree_node_compare() to compute node ordering.)
- */
-
- /* Get on the same level */
- node = line->parent;
- while (node->level < info->tag_root->level)
- node = node->parent;
-
- common_parent = info->tag_root->parent;
+ gint ordering;
- /* Find common parent, and children of that parent above
- tag root and our current node */
- parent_of_node = node;
- parent_of_tag_root = info->tag_root;
+ ordering = node_compare (line->parent, info->tag_root);
- while (node->parent != common_parent)
+ if (ordering < 0)
{
- parent_of_node = node;
- parent_of_tag_root = common_parent;
- node = node->parent;
- common_parent = common_parent->parent;
+ /* Tag root is ahead of us, so search there. */
+ node = info->tag_root;
+ goto found;
}
-
- /* See which is first in the list of common_parent's children */
- iter = common_parent->children.node;
- while (iter != NULL)
+ else
{
- if (iter == parent_of_tag_root)
- return NULL; /* Tag root was before us in the tree */
- else if (iter == parent_of_node)
- {
- /* We want the first inside-tag-root node,
- since we're before the tag root */
- node = info->tag_root;
- goto found;
- }
-
- iter = iter->next;
+ /* Tag root is after us, so no more lines that
+ * could contain the tag.
+ */
+ return NULL;
}
-
- return NULL;
+
+ g_assert_not_reached ();
}
found:
@@ -3831,42 +3898,261 @@ gtk_text_line_next_could_contain_tag(GtkTextLine *line,
g_assert(node != NULL);
/* We have to find the first sub-node of this node that contains
- the target tag. */
+ * the target tag.
+ */
- continue_outer_loop:
while (node->level > 0)
{
- g_assert(node != NULL); /* If this fails, it likely means an
- incorrect tag summary led us on a
- wild goose chase down this branch of
- the tree. */
+ g_assert (node != NULL); /* If this fails, it likely means an
+ incorrect tag summary led us on a
+ wild goose chase down this branch of
+ the tree. */
node = node->children.node;
while (node != NULL)
{
- if (gtk_text_btree_node_has_tag(node, tag))
- goto continue_outer_loop;
+ if (gtk_text_btree_node_has_tag (node, tag))
+ break;
node = node->next;
}
- g_assert(node != NULL);
}
- g_assert(node != NULL);
- g_assert(node->level == 0);
+ g_assert (node != NULL);
+ g_assert (node->level == 0);
return node->children.line;
}
-
+
+static GtkTextLine*
+prev_line_under_node (GtkTextBTreeNode *node,
+ GtkTextLine *line)
+{
+ GtkTextLine *prev;
+
+ prev = node->children.line;
+
+ g_assert (prev);
+
+ if (prev != line)
+ {
+ while (prev->next != line)
+ prev = prev->next;
+
+ return prev;
+ }
+
+ return NULL;
+}
+
GtkTextLine*
-gtk_text_line_previous_could_contain_tag(GtkTextLine *line,
- GtkTextBTree *tree,
- GtkTextTag *tag)
+gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag)
{
- g_warning("FIXME");
+ GtkTextBTreeNode *node;
+ GtkTextBTreeNode *found_node = NULL;
+ GtkTextTagInfo *info;
+ gboolean below_tag_root;
+ GtkTextLine *prev;
+ GtkTextBTreeNode *line_ancestor;
+ GtkTextBTreeNode *line_ancestor_parent;
+
+ /* See next_could_contain_tag() for more extensive comments
+ * on what's going on here.
+ */
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ 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 same-node line, if any. */
+ prev = prev_line_under_node (line->parent, line);
+ if (prev)
+ return prev;
+
+ info = gtk_text_btree_get_existing_tag_info (tree, tag);
+ if (info == NULL)
+ return NULL;
+
+ if (info->tag_root == NULL)
+ return NULL;
+
+ if (info->tag_root == line->parent)
+ return NULL; /* we were at the first line under the tag root */
+
+ /* Are we below the tag root */
+ node = line->parent;
+ below_tag_root = FALSE;
+ while (node != NULL)
+ {
+ if (node == info->tag_root)
+ {
+ below_tag_root = TRUE;
+ break;
+ }
+
+ node = node->parent;
+ }
+
+ if (below_tag_root)
+ {
+ /* Look for a previous node under this tag root that has our
+ * tag.
+ */
+
+ /* this assertion holds because line->parent is not the
+ * tag root, we are below the tag root, and the tag
+ * root exists.
+ */
+ g_assert (line->parent->parent != NULL);
+
+ 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)
+ {
+ GSList *child_nodes = NULL;
+ GSList *tmp;
+
+ /* Create reverse-order list of nodes before
+ * line_ancestor
+ */
+ while (node != line_ancestor
+ && node != NULL)
+ {
+ child_nodes = g_slist_prepend (child_nodes, node);
+
+ node = node->next;
+ }
+
+ /* Try to find a node with our tag on it in the list */
+ tmp = child_nodes;
+ while (tmp != NULL)
+ {
+ GtkTextBTreeNode *this_node = tmp->data;
+
+ g_assert (this_node != line_ancestor);
+
+ if (gtk_text_btree_node_has_tag (this_node, tag))
+ {
+ found_node = this_node;
+ g_slist_free (child_nodes);
+ goto found;
+ }
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (child_nodes);
+
+ /* 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. */
+ return NULL;
+ }
+ else
+ {
+ gint ordering;
+
+ ordering = node_compare (line->parent, info->tag_root);
+
+ if (ordering < 0)
+ {
+ /* Tag root is ahead of us, so no more lines
+ * with this tag.
+ */
+ return NULL;
+ }
+ else
+ {
+ /* Tag root is after us, so grab last tagged
+ * line underneath the tag root.
+ */
+ found_node = info->tag_root;
+ goto found;
+ }
+
+ g_assert_not_reached ();
+ }
+
+ found:
+
+ g_assert (found_node != NULL);
+
+ /* We have to find the last sub-node of this node that contains
+ * the target tag.
+ */
+ node = found_node;
+
+ while (node->level > 0)
+ {
+ GSList *child_nodes = NULL;
+ GSList *iter;
+ g_assert (node != NULL); /* If this fails, it likely means an
+ incorrect tag summary led us on a
+ wild goose chase down this branch of
+ the tree. */
+
+ node = node->children.node;
+ while (node != NULL)
+ {
+ child_nodes = g_slist_prepend (child_nodes, node);
+ node = node->next;
+ }
+
+ node = NULL; /* detect failure to find a child node. */
+
+ iter = child_nodes;
+ while (iter != NULL)
+ {
+ if (gtk_text_btree_node_has_tag (iter->data, tag))
+ {
+ /* recurse into this node. */
+ node = iter->data;
+ break;
+ }
+
+ iter = g_slist_next (iter);
+ }
+
+ g_slist_free (child_nodes);
+
+ g_assert (node != NULL);
+ }
+
+ g_assert (node != NULL);
+ g_assert (node->level == 0);
+
+ /* this assertion is correct, but slow. */
+ /* g_assert (node_compare (node, line->parent) < 0); */
+
+ /* Return last line in this node. */
+
+ prev = node->children.line;
+ while (prev->next)
+ prev = prev->next;
+
+ return prev;
}
/*
- * Non-public function implementations */
+ * Non-public function implementations
+ */
static void
summary_list_destroy(Summary *summary)
@@ -6260,6 +6546,11 @@ gtk_text_btree_spew (GtkTextBTree *tree)
list = g_slist_next (list);
}
+
+ if (tree->tag_infos == NULL)
+ {
+ printf (" (no tags in the tree)\n");
+ }
}
printf("=================== Tree nodes\n");
@@ -6301,11 +6592,17 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
}
else if (seg->type == &gtk_text_right_mark_type)
{
- printf("%s right mark `%s'\n", spaces, seg->body.mark.name);
+ printf("%s right mark `%s' visible: %d\n",
+ spaces,
+ seg->body.mark.name,
+ seg->body.mark.visible);
}
else if (seg->type == &gtk_text_left_mark_type)
{
- printf("%s left mark `%s'\n", spaces, seg->body.mark.name);
+ printf("%s left mark `%s' visible: %d\n",
+ spaces,
+ seg->body.mark.name,
+ seg->body.mark.visible);
}
else if (seg->type == &gtk_text_toggle_on_type ||
seg->type == &gtk_text_toggle_off_type)
@@ -6397,11 +6694,17 @@ gtk_text_btree_spew_segment(GtkTextBTree* tree, GtkTextLineSegment * seg)
}
else if (seg->type == &gtk_text_right_mark_type)
{
- printf(" right mark `%s'\n", seg->body.mark.name);
+ printf(" right mark `%s' visible: %d not_deleteable: %d\n",
+ seg->body.mark.name,
+ seg->body.mark.visible,
+ seg->body.mark.not_deleteable);
}
else if (seg->type == &gtk_text_left_mark_type)
{
- printf(" left mark `%s'\n", seg->body.mark.name);
+ printf(" left mark `%s' visible: %d not_deleteable: %d\n",
+ seg->body.mark.name,
+ seg->body.mark.visible,
+ seg->body.mark.not_deleteable);
}
else if (seg->type == &gtk_text_toggle_on_type ||
seg->type == &gtk_text_toggle_off_type)
diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c
index 9a70c78a10..627341f431 100644
--- a/gtk/gtktextbuffer.c
+++ b/gtk/gtktextbuffer.c
@@ -821,8 +821,8 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
**/
void
gtk_text_buffer_delete (GtkTextBuffer *buffer,
- GtkTextIter *start,
- GtkTextIter *end)
+ GtkTextIter *start,
+ GtkTextIter *end)
{
g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
g_return_if_fail(start != NULL);
@@ -840,7 +840,9 @@ gtk_text_buffer_delete (GtkTextBuffer *buffer,
*
* Deletes all <emphasis>editable</emphasis> text in the given range.
* Calls gtk_text_buffer_delete() for each editable sub-range of
- * [@start,@end).
+ * [@start,@end). @start and @end are revalidated to point to
+ * the location of the last deleted range, or left untouched if
+ * no text was deleted.
*
* Return value: whether some text was actually deleted
**/
@@ -904,6 +906,10 @@ gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
gtk_text_buffer_emit_delete (buffer, &start, &iter, TRUE);
deleted_stuff = TRUE;
+
+ /* revalidate user's iterators. */
+ *start_iter = start;
+ *end_iter = iter;
}
break;
@@ -923,6 +929,10 @@ gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
current_state = FALSE;
deleted_stuff = TRUE;
+
+ /* revalidate user's iterators. */
+ *start_iter = start;
+ *end_iter = iter;
}
else
{
@@ -1478,7 +1488,7 @@ gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
const GtkTextIter *start,
const GtkTextIter *end)
{
- gtk_text_btree_tag(start, end, tag, TRUE);
+ gtk_text_btree_tag (start, end, tag, TRUE);
}
static void
@@ -1487,7 +1497,7 @@ gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
const GtkTextIter *start,
const GtkTextIter *end)
{
- gtk_text_btree_tag(start, end, tag, FALSE);
+ gtk_text_btree_tag (start, end, tag, FALSE);
}
@@ -1500,12 +1510,14 @@ gtk_text_buffer_emit_tag(GtkTextBuffer *buffer,
{
g_return_if_fail(tag != NULL);
+ gtk_text_iter_reorder (start, end);
+
if (apply)
- gtk_signal_emit(GTK_OBJECT(buffer), signals[APPLY_TAG],
- tag, start, end);
+ gtk_signal_emit (GTK_OBJECT(buffer), signals[APPLY_TAG],
+ tag, start, end);
else
- gtk_signal_emit(GTK_OBJECT(buffer), signals[REMOVE_TAG],
- tag, start, end);
+ gtk_signal_emit (GTK_OBJECT(buffer), signals[REMOVE_TAG],
+ tag, start, end);
}
diff --git a/gtk/gtktextiter.c b/gtk/gtktextiter.c
index b2ec510fe8..e061e1978e 100644
--- a/gtk/gtktextiter.c
+++ b/gtk/gtktextiter.c
@@ -33,7 +33,8 @@
typedef struct _GtkTextRealIter GtkTextRealIter;
-struct _GtkTextRealIter {
+struct _GtkTextRealIter
+{
/* Always-valid information */
GtkTextBTree *tree;
GtkTextLine *line;
@@ -53,7 +54,7 @@ struct _GtkTextRealIter {
/* Valid if the segments_changed_stamp is up-to-date */
GtkTextLineSegment *segment; /* indexable segment we index */
GtkTextLineSegment *any_segment; /* first segment in our location,
- maybe same as "segment" */
+ maybe same as "segment" */
/* One of these will always be valid if segments_changed_stamp is
up-to-date. If invalid, they are -1.
@@ -94,7 +95,7 @@ iter_set_from_byte_offset(GtkTextRealIter *iter,
{
iter_set_common(iter, line);
- gtk_text_line_byte_locate(iter->line,
+ gtk_text_line_byte_locate (iter->line,
byte_offset,
&iter->segment,
&iter->any_segment,
@@ -110,7 +111,7 @@ iter_set_from_char_offset(GtkTextRealIter *iter,
{
iter_set_common(iter, line);
- gtk_text_line_char_locate(iter->line,
+ gtk_text_line_char_locate (iter->line,
char_offset,
&iter->segment,
&iter->any_segment,
@@ -362,6 +363,12 @@ ensure_byte_offsets(GtkTextRealIter *iter)
}
}
+static inline gboolean
+is_segment_start (GtkTextRealIter *real)
+{
+ return real->segment_byte_offset == 0 || real->segment_char_offset == 0;
+}
+
#if 1
static void
check_invariants(const GtkTextIter *iter)
@@ -689,12 +696,15 @@ gtk_text_iter_get_line_index(const GtkTextIter *iter)
* Returns the Unicode character at this iterator. (Equivalent to
* operator* on a C++ iterator.) If the iterator points at a
* non-character element, such as an image embedded in the buffer, the
- * Unicode "unknown" character 0xFFFD is returned.
+ * Unicode "unknown" character 0xFFFD is returned. If invoked on
+ * the end iterator, zero is returned; zero is not a valid Unicode character.
+ * So you can write a loop which ends when gtk_text_iter_get_char()
+ * returns 0.
*
- * Return value: a Unicode character
+ * Return value: a Unicode character, or 0 if @iter is not dereferenceable
**/
gunichar
-gtk_text_iter_get_char(const GtkTextIter *iter)
+gtk_text_iter_get_char (const GtkTextIter *iter)
{
GtkTextRealIter *real;
@@ -705,12 +715,11 @@ gtk_text_iter_get_char(const GtkTextIter *iter)
if (real == NULL)
return 0;
- check_invariants(iter);
-
- /* FIXME probably want to special-case the end iterator
- and either have an error or return 0 */
-
- if (real->segment->type == &gtk_text_char_type)
+ check_invariants(iter);
+
+ if (gtk_text_iter_is_last (iter))
+ return 0;
+ else if (real->segment->type == &gtk_text_char_type)
{
ensure_byte_offsets(real);
@@ -1392,13 +1401,13 @@ gtk_text_iter_get_attributes (const GtkTextIter *iter,
* (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
*/
static gboolean
-forward_line_leaving_caches_unmodified(GtkTextRealIter *real)
+forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
{
GtkTextLine *new_line;
- new_line = gtk_text_line_next(real->line);
+ new_line = gtk_text_line_next (real->line);
- g_assert(new_line != real->line);
+ g_assert (new_line != real->line);
if (new_line != NULL)
{
@@ -1442,11 +1451,56 @@ forward_line_leaving_caches_unmodified(GtkTextRealIter *real)
}
}
+
+/* The return value of this indicates WHETHER WE MOVED.
+ * The return value of public functions indicates
+ * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
+ */
+static gboolean
+backward_line_leaving_caches_unmodified (GtkTextRealIter *real)
+{
+ GtkTextLine *new_line;
+
+ new_line = gtk_text_line_previous (real->line);
+
+ g_assert (new_line != real->line);
+
+ if (new_line != NULL)
+ {
+ real->line = new_line;
+
+ real->line_byte_offset = 0;
+ real->line_char_offset = 0;
+
+ real->segment_byte_offset = 0;
+ real->segment_char_offset = 0;
+
+ /* Find first segments in new line */
+ real->any_segment = real->line->segments;
+ real->segment = real->any_segment;
+ while (real->segment->char_count == 0)
+ real->segment = real->segment->next;
+
+ return TRUE;
+ }
+ else
+ {
+ /* There is no way to move backward; we were already
+ at the first line. */
+
+ /* We leave real->line as-is */
+
+ /* Note that we didn't clamp to the start of the first line. */
+
+ return FALSE;
+ }
+}
+
/* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
* DEREFERENCEABLE)
*/
static gboolean
-forward_char(GtkTextRealIter *real)
+forward_char (GtkTextRealIter *real)
{
GtkTextIter *iter = (GtkTextIter*)real;
@@ -1465,7 +1519,7 @@ forward_char(GtkTextRealIter *real)
/* Just moving within a segment. Keep byte count
up-to-date, if it was already up-to-date. */
- g_assert(real->segment->type == &gtk_text_char_type);
+ g_assert (real->segment->type == &gtk_text_char_type);
if (real->line_byte_offset >= 0)
{
@@ -1502,7 +1556,7 @@ forward_char(GtkTextRealIter *real)
}
gboolean
-gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
+gtk_text_iter_forward_indexable_segment (GtkTextIter *iter)
{
/* Need to move to the next segment; if no next segment,
need to move to next line. */
@@ -1524,7 +1578,7 @@ gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
if (real->line_char_offset >= 0)
{
chars_skipped = real->segment->char_count - real->segment_char_offset;
- g_assert(chars_skipped > 0);
+ g_assert (chars_skipped > 0);
}
else
chars_skipped = 0;
@@ -1532,7 +1586,7 @@ gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
if (real->line_byte_offset >= 0)
{
bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
- g_assert(bytes_skipped > 0);
+ g_assert (bytes_skipped > 0);
}
else
bytes_skipped = 0;
@@ -1551,17 +1605,17 @@ gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
if (real->line_byte_offset >= 0)
{
- g_assert(bytes_skipped > 0);
+ g_assert (bytes_skipped > 0);
real->segment_byte_offset = 0;
real->line_byte_offset += bytes_skipped;
}
if (real->line_char_offset >= 0)
{
- g_assert(chars_skipped > 0);
+ g_assert (chars_skipped > 0);
real->segment_char_offset = 0;
real->line_char_offset += chars_skipped;
- adjust_char_index(real, chars_skipped);
+ adjust_char_index (real, chars_skipped);
}
check_invariants(iter);
@@ -1571,21 +1625,19 @@ gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
else
{
/* End of the line */
- if (forward_line_leaving_caches_unmodified(real))
+ if (forward_line_leaving_caches_unmodified (real))
{
- adjust_line_number(real, 1);
+ adjust_line_number (real, 1);
if (real->line_char_offset >= 0)
- adjust_char_index(real, chars_skipped);
-
- check_invariants(iter);
+ adjust_char_index (real, chars_skipped);
- g_assert(real->line_byte_offset == 0);
- g_assert(real->line_char_offset == 0);
- g_assert(real->segment_byte_offset == 0);
- g_assert(real->segment_char_offset == 0);
- g_assert(gtk_text_iter_starts_line(iter));
+ g_assert (real->line_byte_offset == 0);
+ g_assert (real->line_char_offset == 0);
+ g_assert (real->segment_byte_offset == 0);
+ g_assert (real->segment_char_offset == 0);
+ g_assert (gtk_text_iter_starts_line(iter));
- check_invariants(iter);
+ check_invariants (iter);
if (gtk_text_iter_is_last (iter))
return FALSE;
@@ -1603,13 +1655,156 @@ gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
}
}
+static gboolean
+at_last_indexable_segment (GtkTextRealIter *real)
+{
+ GtkTextLineSegment *seg;
+
+ /* Return TRUE if there are no indexable segments after
+ * this iterator.
+ */
+
+ seg = real->segment->next;
+ while (seg)
+ {
+ if (seg->char_count > 0)
+ return FALSE;
+ seg = seg->next;
+ }
+ return TRUE;
+}
+
+/* Goes back to the start of the next segment, even if
+ * we're not at the start of the current segment (always
+ * ends up on a different segment if it returns TRUE)
+ */
gboolean
-gtk_text_iter_backward_indexable_segment(GtkTextIter *iter)
+gtk_text_iter_backward_indexable_segment (GtkTextIter *iter)
{
- g_warning("FIXME");
+ /* Move to the start of the previous segment; if no previous
+ * segment, to the last segment in the previous line. This is
+ * inherently a bit inefficient due to the singly-linked list and
+ * tree nodes, but we can't afford the RAM for doubly-linked.
+ */
+ GtkTextRealIter *real;
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *any_seg;
+ GtkTextLineSegment *prev_seg;
+ GtkTextLineSegment *prev_any_seg;
+ gint bytes_skipped;
+ gint chars_skipped;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ real = gtk_text_iter_make_real(iter);
- return FALSE;
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants (iter);
+
+ /* Find first segments in line */
+ any_seg = real->line->segments;
+ seg = any_seg;
+ while (seg->char_count == 0)
+ seg = seg->next;
+
+ if (seg == real->segment)
+ {
+ /* Could probably do this case faster by hand-coding the
+ * iteration.
+ */
+
+ /* We were already at the start of a line;
+ * go back to the previous line.
+ */
+ if (gtk_text_iter_backward_line (iter))
+ {
+ /* Go forward to last indexable segment in line. */
+ while (!at_last_indexable_segment (real))
+ gtk_text_iter_forward_indexable_segment (iter);
+
+ check_invariants (iter);
+
+ return TRUE;
+ }
+ else
+ return FALSE; /* We were at the start of the first line. */
+ }
+
+ /* We must be in the middle of a line; so find the indexable
+ * segment just before our current segment.
+ */
+ g_assert (seg != real->segment);
+ while (seg != real->segment)
+ {
+ prev_seg = seg;
+ prev_any_seg = any_seg;
+
+ any_seg = seg->next;
+ seg = any_seg;
+ while (seg->char_count == 0)
+ seg = seg->next;
+ }
+
+ g_assert (prev_seg != NULL);
+ g_assert (prev_any_seg != NULL);
+ g_assert (prev_seg->char_count > 0);
+
+ /* We skipped the entire previous segment, plus any
+ * chars we were into the current segment.
+ */
+ if (real->segment_byte_offset >= 0)
+ bytes_skipped = prev_seg->byte_count + real->segment_byte_offset;
+ else
+ bytes_skipped = -1;
+
+ if (real->segment_char_offset >= 0)
+ chars_skipped = prev_seg->char_count + real->segment_char_offset;
+ else
+ chars_skipped = -1;
+
+ real->segment = prev_seg;
+ real->any_segment = prev_any_seg;
+ real->segment_byte_offset = 0;
+ real->segment_char_offset = 0;
+
+ if (bytes_skipped >= 0)
+ {
+ if (real->line_byte_offset >= 0)
+ {
+ real->line_byte_offset -= bytes_skipped;
+ g_assert (real->line_byte_offset >= 0);
+ }
+ }
+ else
+ real->line_byte_offset = -1;
+
+ if (chars_skipped >= 0)
+ {
+ if (real->line_char_offset >= 0)
+ {
+ real->line_char_offset -= chars_skipped;
+ g_assert (real->line_char_offset >= 0);
+ }
+
+ if (real->cached_char_index >= 0)
+ {
+ real->cached_char_index -= chars_skipped;
+ g_assert (real->cached_char_index >= 0);
+ }
+ }
+ else
+ {
+ real->line_char_offset = -1;
+ real->cached_char_index = -1;
+ }
+
+ /* line number is unchanged. */
+
+ check_invariants (iter);
+
+ return TRUE;
}
/**
@@ -1677,7 +1872,7 @@ gtk_text_iter_prev_char(GtkTextIter *iter)
I guess you'd have to profile the various approaches.
*/
-#define MAX_LINEAR_SCAN 300
+#define MAX_LINEAR_SCAN 150
/**
@@ -1822,23 +2017,34 @@ gtk_text_iter_backward_chars(GtkTextIter *iter, gint count)
else
{
/* We need to go back into previous segments. For now,
- just keep this really simple. */
- gint current_char_index;
- gint new_char_index;
+ * just keep this really simple. FIXME
+ * use backward_indexable_segment.
+ */
+ if (TRUE || count > MAX_LINEAR_SCAN)
+ {
+ gint current_char_index;
+ gint new_char_index;
- current_char_index = gtk_text_iter_get_offset(iter);
+ current_char_index = gtk_text_iter_get_offset (iter);
- if (current_char_index == 0)
- return FALSE; /* can't move backward */
+ if (current_char_index == 0)
+ return FALSE; /* can't move backward */
- new_char_index = current_char_index - count;
- if (new_char_index < 0)
- new_char_index = 0;
- gtk_text_iter_set_offset(iter, new_char_index);
+ new_char_index = current_char_index - count;
+ if (new_char_index < 0)
+ new_char_index = 0;
+ gtk_text_iter_set_offset (iter, new_char_index);
- check_invariants(iter);
+ check_invariants(iter);
- return TRUE;
+ return TRUE;
+ }
+ else
+ {
+ /* FIXME backward_indexable_segment here */
+
+ return FALSE;
+ }
}
}
@@ -1935,7 +2141,7 @@ gtk_text_iter_backward_line(GtkTextIter *iter)
return FALSE;
}
- invalidate_char_index(real);
+ invalidate_char_index (real);
real->line_byte_offset = 0;
real->line_char_offset = 0;
@@ -1946,7 +2152,7 @@ gtk_text_iter_backward_line(GtkTextIter *iter)
/* Find first segment in line */
real->any_segment = real->line->segments;
real->segment = gtk_text_line_byte_to_segment(real->line,
- 0, &offset);
+ 0, &offset);
g_assert(offset == 0);
@@ -2207,18 +2413,18 @@ gtk_text_iter_set_line_offset(GtkTextIter *iter,
{
GtkTextRealIter *real;
- g_return_if_fail(iter != NULL);
+ g_return_if_fail (iter != NULL);
- real = gtk_text_iter_make_surreal(iter);
+ real = gtk_text_iter_make_surreal (iter);
if (real == NULL)
return;
- check_invariants(iter);
+ check_invariants (iter);
- iter_set_from_char_offset(real, real->line, char_on_line);
+ iter_set_from_char_offset (real, real->line, char_on_line);
- check_invariants(iter);
+ check_invariants (iter);
}
void
@@ -2228,23 +2434,23 @@ gtk_text_iter_set_line(GtkTextIter *iter, gint line_number)
gint real_line;
GtkTextRealIter *real;
- g_return_if_fail(iter != NULL);
+ g_return_if_fail (iter != NULL);
- real = gtk_text_iter_make_surreal(iter);
+ real = gtk_text_iter_make_surreal (iter);
if (real == NULL)
return;
- check_invariants(iter);
+ check_invariants (iter);
- line = gtk_text_btree_get_line(real->tree, line_number, &real_line);
+ line = gtk_text_btree_get_line (real->tree, line_number, &real_line);
- iter_set_from_char_offset(real, line, 0);
+ iter_set_from_char_offset (real, line, 0);
/* We might as well cache this, since we know it. */
real->cached_line_number = real_line;
- check_invariants(iter);
+ check_invariants (iter);
}
void
@@ -2360,10 +2566,10 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
check_invariants(iter);
current_line = real->line;
- next_line = gtk_text_line_next_could_contain_tag(current_line,
- real->tree, tag);
+ next_line = gtk_text_line_next_could_contain_tag (current_line,
+ real->tree, tag);
- while (gtk_text_iter_forward_indexable_segment(iter))
+ while (gtk_text_iter_forward_indexable_segment (iter))
{
/* If we went forward to a line that couldn't contain a toggle
for the tag, then skip forward to a line that could contain
@@ -2374,15 +2580,15 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
if (next_line == NULL)
{
/* End of search. Set to end of buffer. */
- gtk_text_btree_get_last_iter(real->tree, iter);
+ gtk_text_btree_get_last_iter (real->tree, iter);
return FALSE;
}
if (real->line != next_line)
- iter_set_from_byte_offset(real, next_line, 0);
+ iter_set_from_byte_offset (real, next_line, 0);
current_line = real->line;
- next_line = gtk_text_line_next_could_contain_tag(current_line,
+ next_line = gtk_text_line_next_could_contain_tag (current_line,
real->tree,
tag);
}
@@ -2397,7 +2603,7 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
}
/* Check end iterator for tags */
- if (gtk_text_iter_toggles_tag(iter, tag))
+ if (gtk_text_iter_toggles_tag (iter, tag))
{
/* If there's a toggle here, it isn't indexable so
any_segment can't be the indexable segment. */
@@ -2409,12 +2615,103 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
return FALSE;
}
+/**
+ * gtk_text_iter_backward_to_tag_toggle:
+ * @iter: a #GtkTextIter
+ * @tag: a #GtkTextTag, or NULL
+ *
+ * Moves backward to the next toggle (on or off) of the
+ * #GtkTextTag @tag, or to the next toggle of any tag if
+ * @tag is NULL. If no matching tag toggles are found,
+ * returns FALSE, otherwise TRUE. Does not return toggles
+ * located at @iter, only toggles before @iter.
+ *
+ * Return value: whether we found a tag toggle before @iter
+ **/
gboolean
gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
GtkTextTag *tag)
{
+ GtkTextLine *prev_line;
+ GtkTextLine *current_line;
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
- g_warning("FIXME");
+ check_invariants(iter);
+
+ current_line = real->line;
+ prev_line = gtk_text_line_previous_could_contain_tag (current_line,
+ real->tree, tag);
+
+
+ /* If we're at segment start, go to the previous segment;
+ * if mid-segment, snap to start of current segment.
+ */
+ if (is_segment_start (real))
+ {
+ if (!gtk_text_iter_backward_indexable_segment (iter))
+ return FALSE;
+ }
+ else
+ {
+ ensure_char_offsets (real);
+
+ if (!gtk_text_iter_backward_chars (iter, real->segment_char_offset))
+ return FALSE;
+ }
+
+ do
+ {
+ /* If we went backward to a line that couldn't contain a toggle
+ * for the tag, then skip backward further to a line that
+ * could contain it. This potentially skips huge hunks of the
+ * tree, so we aren't a purely linear search.
+ */
+ if (real->line != current_line)
+ {
+ if (prev_line == NULL)
+ {
+ /* End of search. Set to start of buffer. */
+ gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
+ return FALSE;
+ }
+
+ if (real->line != prev_line)
+ {
+ /* Set to last segment in prev_line (could do this
+ * more quickly)
+ */
+ iter_set_from_byte_offset (real, prev_line, 0);
+
+ while (!at_last_indexable_segment (real))
+ gtk_text_iter_forward_indexable_segment (iter);
+ }
+
+ current_line = real->line;
+ prev_line = gtk_text_line_previous_could_contain_tag (current_line,
+ real->tree,
+ tag);
+ }
+
+ if (gtk_text_iter_toggles_tag (iter, tag))
+ {
+ /* If there's a toggle here, it isn't indexable so
+ * any_segment can't be the indexable segment.
+ */
+ g_assert (real->any_segment != real->segment);
+ return TRUE;
+ }
+ }
+ while (gtk_text_iter_backward_indexable_segment (iter));
+
+ /* Reached front of buffer */
+ return FALSE;
}
static gboolean
@@ -2437,9 +2734,9 @@ gtk_text_iter_forward_find_char (GtkTextIter *iter,
g_return_val_if_fail(iter != NULL, FALSE);
g_return_val_if_fail(pred != NULL, FALSE);
- while (gtk_text_iter_next_char(iter))
+ while (gtk_text_iter_next_char (iter))
{
- if (matches_pred(iter, pred, user_data))
+ if (matches_pred (iter, pred, user_data))
return TRUE;
}
@@ -2454,9 +2751,9 @@ gtk_text_iter_backward_find_char (GtkTextIter *iter,
g_return_val_if_fail(iter != NULL, FALSE);
g_return_val_if_fail(pred != NULL, FALSE);
- while (gtk_text_iter_prev_char(iter))
+ while (gtk_text_iter_prev_char (iter))
{
- if (matches_pred(iter, pred, user_data))
+ if (matches_pred (iter, pred, user_data))
return TRUE;
}
@@ -2640,7 +2937,8 @@ gtk_text_iter_forward_search (GtkTextIter *iter,
* gtk_text_iter_get_text() is called repeatedly on
* a single line.
*/
- if (lines_match (&search, (const gchar**)lines, visible_only, slice, &match))
+ if (lines_match (&search, (const gchar**)lines,
+ visible_only, slice, &match))
{
retval = TRUE;
@@ -3062,8 +3360,8 @@ gtk_text_iter_check (const GtkTextIter *iter)
if (real->line_byte_offset >= 0)
{
gtk_text_line_byte_locate(real->line, real->line_byte_offset,
- &byte_segment, &byte_any_segment,
- &seg_byte_offset, &line_byte_offset);
+ &byte_segment, &byte_any_segment,
+ &seg_byte_offset, &line_byte_offset);
if (line_byte_offset != real->line_byte_offset)
g_error("wrong byte offset was stored in iterator");
@@ -3084,8 +3382,8 @@ gtk_text_iter_check (const GtkTextIter *iter)
if (real->line_char_offset >= 0)
{
gtk_text_line_char_locate(real->line, real->line_char_offset,
- &char_segment, &char_any_segment,
- &seg_char_offset, &line_char_offset);
+ &char_segment, &char_any_segment,
+ &seg_char_offset, &line_char_offset);
if (line_char_offset != real->line_char_offset)
g_error("wrong char offset was stored in iterator");
diff --git a/gtk/gtktextiterprivate.h b/gtk/gtktextiterprivate.h
index 0d9104d3e1..bd6835a987 100644
--- a/gtk/gtktextiterprivate.h
+++ b/gtk/gtktextiterprivate.h
@@ -10,18 +10,15 @@ extern "C" {
#include <gtk/gtktextiter.h>
#include <gtk/gtktextbtree.h>
-GtkTextLineSegment *gtk_text_iter_get_indexable_segment(const GtkTextIter *iter);
-GtkTextLineSegment *gtk_text_iter_get_any_segment(const GtkTextIter *iter);
+GtkTextLineSegment *gtk_text_iter_get_indexable_segment (const GtkTextIter *iter);
+GtkTextLineSegment *gtk_text_iter_get_any_segment (const GtkTextIter *iter);
+GtkTextLine * gtk_text_iter_get_text_line (const GtkTextIter *iter);
+GtkTextBTree * gtk_text_iter_get_btree (const GtkTextIter *iter);
+gboolean gtk_text_iter_forward_indexable_segment (GtkTextIter *iter);
+gboolean gtk_text_iter_backward_indexable_segment (GtkTextIter *iter);
+gint gtk_text_iter_get_segment_byte (const GtkTextIter *iter);
+gint gtk_text_iter_get_segment_char (const GtkTextIter *iter);
-GtkTextLine *gtk_text_iter_get_text_line(const GtkTextIter *iter);
-
-GtkTextBTree *gtk_text_iter_get_btree(const GtkTextIter *iter);
-
-gboolean gtk_text_iter_forward_indexable_segment(GtkTextIter *iter);
-gboolean gtk_text_iter_backward_indexable_segment(GtkTextIter *iter);
-
-gint gtk_text_iter_get_segment_byte(const GtkTextIter *iter);
-gint gtk_text_iter_get_segment_char(const GtkTextIter *iter);
/* debug */
void gtk_text_iter_check(const GtkTextIter *iter);
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c
index ff7da5e3b0..d4b57f973a 100644
--- a/gtk/gtktextlayout.c
+++ b/gtk/gtktextlayout.c
@@ -1204,8 +1204,6 @@ add_cursor (GtkTextLayout *layout,
GtkTextLineSegment *seg,
gint start)
{
- GtkTextIter selection_start, selection_end;
-
PangoRectangle strong_pos, weak_pos;
GtkTextCursorDisplay *cursor;
@@ -1214,7 +1212,8 @@ add_cursor (GtkTextLayout *layout,
*/
if (gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
(GtkTextMark*)seg) &&
- (!layout->cursor_visible || gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end)))
+ (!layout->cursor_visible ||
+ gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
return;
pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
@@ -1429,7 +1428,8 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
tmp_list2 = cursor_segs;
while (tmp_list1)
{
- add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
+ add_cursor (layout, display, tmp_list2->data,
+ GPOINTER_TO_INT (tmp_list1->data));
tmp_list1 = tmp_list1->next;
tmp_list2 = tmp_list2->next;
}
diff --git a/gtk/gtktextlayout.h b/gtk/gtktextlayout.h
index 5d812f1a99..61ad83dbf7 100644
--- a/gtk/gtktextlayout.h
+++ b/gtk/gtktextlayout.h
@@ -146,7 +146,7 @@ struct _GtkTextLineDisplay
gint bottom_margin;
gboolean size_only;
- GtkTextLine *line;
+ GtkTextLine *line;
};
extern PangoAttrType gtk_text_attr_appearance_type;
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 4bbd655b64..6e5b661084 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -530,7 +530,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
"delete", 2,
GTK_TYPE_ENUM, GTK_DELETE_CHARS,
GTK_TYPE_INT, 1);
-
+
gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
"delete", 2,
GTK_TYPE_ENUM, GTK_DELETE_CHARS,
@@ -2590,6 +2590,16 @@ gtk_text_view_delete (GtkTextView *text_view,
break;
case GTK_DELETE_PARAGRAPH_ENDS:
+ /* If we're already at a newline, we need to
+ * simply delete that newline, instead of
+ * moving to the next one.
+ */
+ if (gtk_text_iter_get_char (&end) == '\n')
+ {
+ gtk_text_iter_next_char (&end);
+ --count;
+ }
+
while (count > 0)
{
if (!gtk_text_iter_forward_to_newline (&end))
@@ -3058,9 +3068,9 @@ gtk_text_view_destroy_layout (GtkTextView *text_view)
*/
static void
-gtk_text_view_start_selection_dnd (GtkTextView *text_view,
+gtk_text_view_start_selection_dnd (GtkTextView *text_view,
const GtkTextIter *iter,
- GdkEventMotion *event)
+ GdkEventMotion *event)
{
GdkDragContext *context;
GtkTargetList *target_list;
@@ -3079,7 +3089,8 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view,
gtk_drag_set_icon_default (context);
/* We're inside the selection, so start without being able
- to accept the drag. */
+ * to accept the drag.
+ */
gdk_drag_status (context, 0, event->time);
gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
}
@@ -3201,7 +3212,8 @@ gtk_text_view_drag_motion (GtkWidget *widget,
{
if (gtk_text_iter_editable (&newplace, text_view->editable))
{
- gtk_text_mark_set_visible (text_view->dnd_mark, text_view->cursor_visible);
+ gtk_text_mark_set_visible (text_view->dnd_mark,
+ text_view->cursor_visible);
gdk_drag_status (context, context->suggested_action, time);
}
diff --git a/gtk/testtextbuffer.c b/gtk/testtextbuffer.c
index 286f9b5fd7..437be8832f 100644
--- a/gtk/testtextbuffer.c
+++ b/gtk/testtextbuffer.c
@@ -16,14 +16,14 @@ main (int argc, char** argv)
int n;
gunichar ch;
GtkTextIter start, end;
-
+
gtk_init (&argc, &argv);
/* Check UTF8 unknown char thing */
g_assert (g_utf8_strlen (gtk_text_unknown_char_utf8, 3) == 1);
ch = g_utf8_get_char (gtk_text_unknown_char_utf8);
g_assert (ch == gtk_text_unknown_char);
-
+
/* First, we turn on btree debugging. */
gtk_debug_flags |= GTK_DEBUG_TEXT;
@@ -31,18 +31,18 @@ main (int argc, char** argv)
buffer = gtk_text_buffer_new (NULL);
/* Check that buffer starts with one empty line and zero chars */
-
+
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 != 1)
g_error ("%d chars, expected 1", n);
-
+
/* Run gruesome alien test suite on buffer */
run_tests (buffer);
-
+
/* Put stuff in the buffer */
fill_buffer (buffer);
@@ -59,7 +59,7 @@ main (int argc, char** argv)
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 != 1)
g_error ("%d chars, expected 1", n);
@@ -67,10 +67,165 @@ main (int argc, char** argv)
run_tests (buffer);
g_print ("All tests passed.\n");
-
+
return 0;
}
+static gint
+count_toggles_at_iter (GtkTextIter *iter,
+ GtkTextTag *of_tag)
+{
+ GSList *tags;
+ GSList *tmp;
+ gint count = 0;
+
+ /* get toggle-ons and toggle-offs */
+ tags = gtk_text_iter_get_toggled_tags (iter, TRUE);
+ tags = g_slist_concat (tags,
+ gtk_text_iter_get_toggled_tags (iter, FALSE));
+
+ tmp = tags;
+ while (tmp != NULL)
+ {
+ if (of_tag == NULL)
+ ++count;
+ else if (of_tag == tmp->data)
+ ++count;
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (tags);
+
+ return count;
+}
+
+static gint
+count_toggles_in_buffer (GtkTextBuffer *buffer,
+ GtkTextTag *of_tag)
+{
+ GtkTextIter iter;
+ gint count = 0;
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
+ do
+ {
+ count += count_toggles_at_iter (&iter, of_tag);
+ }
+ while (gtk_text_iter_next_char (&iter));
+
+ /* Do the end iterator, because forward_char won't return TRUE
+ * on it.
+ */
+ count += count_toggles_at_iter (&iter, of_tag);
+
+ return count;
+}
+
+static void
+check_specific_tag (GtkTextBuffer *buffer,
+ const gchar *tag_name)
+{
+ GtkTextIter iter;
+ GtkTextTag *tag;
+ gboolean state;
+ gint count;
+ gint buffer_count;
+ gint last_offset;
+
+ tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer),
+ tag_name);
+
+ buffer_count = count_toggles_in_buffer (buffer, tag);
+
+ state = FALSE;
+ count = 0;
+
+ last_offset = -1;
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
+ if (gtk_text_iter_toggles_tag (&iter, tag) ||
+ gtk_text_iter_forward_to_tag_toggle (&iter, tag))
+ {
+ do
+ {
+ gint this_offset;
+
+ ++count;
+
+ this_offset = gtk_text_iter_get_offset (&iter);
+
+ if (this_offset <= last_offset)
+ g_error ("forward_to_tag_toggle moved in wrong direction");
+
+ last_offset = this_offset;
+
+ if (gtk_text_iter_begins_tag (&iter, tag))
+ {
+ if (state)
+ g_error ("Tag %p is already on, and was toggled on?", tag);
+ state = TRUE;
+ }
+ else if (gtk_text_iter_ends_tag (&iter, tag))
+ {
+ if (!state)
+ g_error ("Tag %p toggled off, but wasn't toggled on?", tag);
+ state = FALSE;
+ }
+ else
+ g_error ("forward_to_tag_toggle went to a location without a toggle");
+ }
+ while (gtk_text_iter_forward_to_tag_toggle (&iter, tag));
+ }
+
+ if (count != buffer_count)
+ g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
+ buffer_count, count);
+
+ state = FALSE;
+ count = 0;
+
+ gtk_text_buffer_get_last_iter (buffer, &iter);
+ last_offset = gtk_text_iter_get_offset (&iter);
+ if (gtk_text_iter_toggles_tag (&iter, tag) ||
+ gtk_text_iter_backward_to_tag_toggle (&iter, tag))
+ {
+ do
+ {
+ gint this_offset;
+
+ ++count;
+
+ this_offset = gtk_text_iter_get_offset (&iter);
+
+ if (this_offset >= last_offset)
+ g_error ("backward_to_tag_toggle moved in wrong direction");
+
+ last_offset = this_offset;
+
+ if (gtk_text_iter_begins_tag (&iter, tag))
+ {
+ if (!state)
+ g_error ("Tag %p wasn't on when we got to the on toggle going backward?", tag);
+ state = FALSE;
+ }
+ else if (gtk_text_iter_ends_tag (&iter, tag))
+ {
+ if (state)
+ g_error ("Tag %p off toggle, but we were already inside a tag?", tag);
+ state = TRUE;
+ }
+ else
+ g_error ("backward_to_tag_toggle went to a location without a toggle");
+ }
+ while (gtk_text_iter_backward_to_tag_toggle (&iter, tag));
+ }
+
+ if (count != buffer_count)
+ g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
+ buffer_count, count);
+
+}
+
static void
run_tests (GtkTextBuffer *buffer)
{
@@ -81,9 +236,13 @@ run_tests (GtkTextBuffer *buffer)
gint i, j;
gint num_chars;
GtkTextMark *bar_mark;
+ GtkTextTag *tag;
+ GHashTable *tag_states;
+ gint count;
+ gint buffer_count;
gtk_text_buffer_get_bounds (buffer, &start, &end);
-
+
/* Check that walking the tree via chars and via iterators produces
* the same number of indexable locations.
*/
@@ -95,7 +254,7 @@ run_tests (GtkTextBuffer *buffer)
{
GtkTextIter current;
GtkTextMark *foo_mark;
-
+
gtk_text_buffer_get_iter_at_offset (buffer, &current, i);
if (!gtk_text_iter_equal (&iter, &current))
@@ -119,11 +278,11 @@ run_tests (GtkTextBuffer *buffer)
gtk_text_iter_spew (&mark, "mark");
g_error ("Mark not moved to the right place.");
}
-
+
foo_mark = gtk_text_buffer_create_mark (buffer, "foo", &iter, FALSE);
gtk_text_buffer_get_iter_at_mark (buffer, &mark, foo_mark);
gtk_text_buffer_delete_mark (buffer, foo_mark);
-
+
if (!gtk_text_iter_equal (&iter, &mark))
{
gtk_text_iter_spew (&iter, "iter");
@@ -134,34 +293,34 @@ run_tests (GtkTextBuffer *buffer)
if (gtk_text_iter_is_last (&iter))
g_error ("iterators ran out before chars (offset %d of %d)",
i, num_chars);
-
+
gtk_text_iter_next_char (&iter);
gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
-
+
++i;
}
if (!gtk_text_iter_equal (&iter, &end))
g_error ("Iterating over all chars didn't end with the end iter");
- /* Do the tree-walk backward
+ /* Do the tree-walk backward
*/
num_chars = gtk_text_buffer_get_char_count (buffer);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, -1);
gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
-
+
i = num_chars;
if (!gtk_text_iter_equal (&iter, &end))
g_error ("iter at char -1 is not equal to the end iterator");
-
+
while (i >= 0)
{
GtkTextIter current;
GtkTextMark *foo_mark;
-
+
gtk_text_buffer_get_iter_at_offset (buffer, &current, i);
if (!gtk_text_iter_equal (&iter, &current))
@@ -184,18 +343,18 @@ run_tests (GtkTextBuffer *buffer)
gtk_text_iter_spew (&mark, "mark");
g_error ("Mark not moved to the right place.");
}
-
+
foo_mark = gtk_text_buffer_create_mark (buffer, "foo", &iter, FALSE);
gtk_text_buffer_get_iter_at_mark (buffer, &mark, foo_mark);
gtk_text_buffer_delete_mark (buffer, foo_mark);
-
+
if (!gtk_text_iter_equal (&iter, &mark))
{
gtk_text_iter_spew (&iter, "iter");
gtk_text_iter_spew (&mark, "mark");
g_error ("Mark not created in the right place.");
}
-
+
if (i > 0)
{
if (!gtk_text_iter_prev_char (&iter))
@@ -208,10 +367,10 @@ run_tests (GtkTextBuffer *buffer)
if (gtk_text_iter_prev_char (&iter))
g_error ("went backward from 0?");
}
-
+
--i;
}
-
+
if (!gtk_text_iter_equal (&iter, &start))
g_error ("Iterating backward over all chars didn't end with the start iter");
@@ -223,10 +382,169 @@ run_tests (GtkTextBuffer *buffer)
gtk_text_buffer_get_iter_at_line (buffer, &iter, 0);
while (gtk_text_iter_forward_line (&iter))
++i;
-
+
if (i != gtk_text_buffer_get_line_count (buffer))
g_error ("Counted %d lines, buffer has %d", i,
gtk_text_buffer_get_line_count (buffer));
+
+ /*
+ * Check that moving over tag toggles thinks about working.
+ */
+
+ buffer_count = count_toggles_in_buffer (buffer, NULL);
+
+ tag_states = g_hash_table_new (NULL, NULL);
+ count = 0;
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
+ if (gtk_text_iter_toggles_tag (&iter, NULL) ||
+ gtk_text_iter_forward_to_tag_toggle (&iter, NULL))
+ {
+ do
+ {
+ GSList *tags;
+ GSList *tmp;
+ gboolean found_some = FALSE;
+
+ /* get toggled-on tags */
+ tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
+
+ if (tags)
+ found_some = TRUE;
+
+ tmp = tags;
+ while (tmp != NULL)
+ {
+ ++count;
+
+ tag = tmp->data;
+
+ if (g_hash_table_lookup (tag_states, tag))
+ g_error ("Tag %p is already on, and was toggled on?", tag);
+
+ g_hash_table_insert (tag_states, tag, GINT_TO_POINTER (TRUE));
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (tags);
+
+ /* get toggled-off tags */
+ tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
+
+ if (tags)
+ found_some = TRUE;
+
+ tmp = tags;
+ while (tmp != NULL)
+ {
+ ++count;
+
+ tag = tmp->data;
+
+ if (!g_hash_table_lookup (tag_states, tag))
+ g_error ("Tag %p is already off, and was toggled off?", tag);
+
+ g_hash_table_remove (tag_states, tag);
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (tags);
+
+ if (!found_some)
+ g_error ("No tags found going forward to tag toggle.");
+
+ }
+ while (gtk_text_iter_forward_to_tag_toggle (&iter, NULL));
+ }
+
+ g_hash_table_destroy (tag_states);
+
+ if (count != buffer_count)
+ g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
+ buffer_count, count);
+
+ /* Go backward; here TRUE in the hash means we saw
+ * an off toggle last.
+ */
+
+ tag_states = g_hash_table_new (NULL, NULL);
+ count = 0;
+
+ gtk_text_buffer_get_last_iter (buffer, &iter);
+ if (gtk_text_iter_toggles_tag (&iter, NULL) ||
+ gtk_text_iter_backward_to_tag_toggle (&iter, NULL))
+ {
+ do
+ {
+ GSList *tags;
+ GSList *tmp;
+ gboolean found_some = FALSE;
+
+ /* get toggled-off tags */
+ tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
+
+ if (tags)
+ found_some = TRUE;
+
+ tmp = tags;
+ while (tmp != NULL)
+ {
+ ++count;
+
+ tag = tmp->data;
+
+ if (g_hash_table_lookup (tag_states, tag))
+ g_error ("Tag %p has two off-toggles in a row?", tag);
+
+ g_hash_table_insert (tag_states, tag, GINT_TO_POINTER (TRUE));
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (tags);
+
+ /* get toggled-on tags */
+ tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
+
+ if (tags)
+ found_some = TRUE;
+
+ tmp = tags;
+ while (tmp != NULL)
+ {
+ ++count;
+
+ tag = tmp->data;
+
+ if (!g_hash_table_lookup (tag_states, tag))
+ g_error ("Tag %p was toggled on, but saw no off-toggle?", tag);
+
+ g_hash_table_remove (tag_states, tag);
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (tags);
+
+ if (!found_some)
+ g_error ("No tags found going backward to tag toggle.");
+ }
+ while (gtk_text_iter_backward_to_tag_toggle (&iter, NULL));
+ }
+
+ g_hash_table_destroy (tag_states);
+
+ if (count != buffer_count)
+ g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
+ buffer_count, count);
+
+ check_specific_tag (buffer, "fg_red");
+ check_specific_tag (buffer, "bg_green");
+ check_specific_tag (buffer, "front_tag");
+ check_specific_tag (buffer, "center_tag");
+ check_specific_tag (buffer, "end_tag");
}
@@ -264,7 +582,7 @@ fill_buffer (GtkTextBuffer *buffer)
GtkTextIter iter2;
GdkPixbuf *pixbuf;
int i;
-
+
tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
color.red = color.green = 0;
@@ -297,49 +615,49 @@ fill_buffer (GtkTextBuffer *buffer)
NULL);
pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
-
+
g_assert (pixbuf != NULL);
-
+
i = 0;
while (i < 10)
{
gchar *str;
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
-
+
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 1);
-
+
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
-
+
str = g_strdup_printf ("%d Hello World!\nwoo woo woo woo woo woo woo woo\n",
i);
-
+
gtk_text_buffer_insert (buffer, &iter, str, -1);
g_free (str);
-
+
gtk_text_buffer_insert (buffer, &iter,
"(Hello World!)\nfoo foo Hello this is some text we are using to text word wrap. It has punctuation! gee; blah - hmm, great.\nnew line\n\n"
/* This is UTF8 stuff, Emacs doesn't
really know how to display it */
"Spanish (Español) ¡Hola! / French (Français) Bonjour, Salut / German (Deutsch Süd) Grüß Gott (testing Latin-1 chars encoded in UTF8)\nThai (we can't display this, just making sure we don't crash) (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ\n",
- -1);
-
+ -1);
+
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
-
+
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 4);
-
+
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 7);
-
+
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 8);
-
+
gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 8);
@@ -350,40 +668,66 @@ fill_buffer (GtkTextBuffer *buffer)
gtk_text_iter_forward_chars (&iter, 7);
gtk_text_iter_forward_chars (&iter2, 10);
-
+
gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
gtk_text_iter_forward_chars (&iter, 12);
gtk_text_iter_forward_chars (&iter2, 10);
-
+
gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
gtk_text_iter_forward_chars (&iter, 10);
gtk_text_iter_forward_chars (&iter2, 15);
-
+
gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
- gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
+ gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
gtk_text_iter_forward_chars (&iter, 20);
gtk_text_iter_forward_chars (&iter2, 20);
-
+
gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
- gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
+ gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
gtk_text_iter_backward_chars (&iter, 25);
gtk_text_iter_forward_chars (&iter2, 5);
-
+
gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
- gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
+ gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
gtk_text_iter_forward_chars (&iter, 15);
gtk_text_iter_backward_chars (&iter2, 10);
gtk_text_buffer_remove_tag_by_name (buffer, "fg_red", &iter, &iter2);
- gtk_text_buffer_remove_tag_by_name (buffer, "fg_blue", &iter, &iter2);
-
+ gtk_text_buffer_remove_tag_by_name (buffer, "fg_blue", &iter, &iter2);
+
++i;
}
+ /* Put in tags that are just at the beginning, and just near the end,
+ * and just near the middle.
+ */
+ tag = gtk_text_buffer_create_tag (buffer, "front_tag");
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3);
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter2, 300);
+
+ gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
+
+ tag = gtk_text_buffer_create_tag (buffer, "end_tag");
+ gtk_text_buffer_get_last_iter (buffer, &iter2);
+ gtk_text_iter_backward_chars (&iter2, 12);
+ iter = iter2;
+ gtk_text_iter_backward_chars (&iter, 157);
+
+ gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
+
+ tag = gtk_text_buffer_create_tag (buffer, "center_tag");
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter,
+ gtk_text_buffer_get_char_count (buffer)/2);
+ gtk_text_iter_backward_chars (&iter, 37);
+ iter2 = iter;
+ gtk_text_iter_forward_chars (&iter2, 57);
+
+ gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
+
gdk_pixbuf_unref (pixbuf);
}