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...
This commit is contained in:
Havoc Pennington 2000-10-04 14:23:09 +00:00 committed by Havoc Pennington
parent 69ac9451a6
commit 928c069883
16 changed files with 1815 additions and 288 deletions

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -1,3 +1,34 @@
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...
2000-10-03 Tor Lillqvist <tml@iki.fi>
* config.h.win32: Guard definition of alloca(). glib.h now handles

View File

@ -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;
gint ordering;
/* 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.)
*/
ordering = node_compare (line->parent, info->tag_root);
/* Get on the same level */
node = line->parent;
while (node->level < info->tag_root->level)
node = node->parent;
common_parent = info->tag_root->parent;
/* 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;
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;
}
else
{
/* Tag root is after us, so no more lines that
* could contain the tag.
*/
return 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_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;
}
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;
}
GtkTextLine*
gtk_text_line_previous_could_contain_tag(GtkTextLine *line,
GtkTextBTree *tree,
GtkTextTag *tag)
{
g_warning("FIXME");
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)
{
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)

View File

@ -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);
}

View File

@ -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);
adjust_char_index (real, chars_skipped);
check_invariants(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));
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)
}
}
gboolean
gtk_text_iter_backward_indexable_segment(GtkTextIter *iter)
static gboolean
at_last_indexable_segment (GtkTextRealIter *real)
{
g_warning("FIXME");
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;
}
return FALSE;
/* 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)
{
/* 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);
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_warning("FIXME");
g_return_val_if_fail(iter != NULL, FALSE);
real = gtk_text_iter_make_real(iter);
if (real == NULL)
return FALSE;
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");

View File

@ -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);

View File

@ -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;
}

View File

@ -146,7 +146,7 @@ struct _GtkTextLineDisplay
gint bottom_margin;
gboolean size_only;
GtkTextLine *line;
GtkTextLine *line;
};
extern PangoAttrType gtk_text_attr_appearance_type;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}