*
*/
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include "gtktextbtree.h"
#include <string.h>
#include <ctype.h>
gint height;
gint width : 24;
- /* boolean indicating whether the height/width need to be recomputed */
+ /* boolean indicating whether the lines below this node are in need of validation.
+ * However, width/height should always represent the current total width and
+ * max height for lines below this node; the valid flag indicates whether the
+ * width/height on the lines needs recomputing, not whether the totals
+ * need recomputing.
+ */
gint valid : 8;
};
BTreeView *views;
GSList *tag_infos;
guint tag_changed_handler;
- guint tag_removed_handler;
+
/* Incremented when a segment with a byte size > 0
- is added to or removed from the tree (i.e. the
- length of a line may have changed, and lines may
- have been added or removed). This invalidates
- all outstanding iterators.
- */
+ * is added to or removed from the tree (i.e. the
+ * length of a line may have changed, and lines may
+ * have been added or removed). This invalidates
+ * all outstanding iterators.
+ */
guint chars_changed_stamp;
/* Incremented when any segments are added or deleted;
- this makes outstanding iterators recalculate their
- pointed-to segment and segment offset.
- */
+ * this makes outstanding iterators recalculate their
+ * pointed-to segment and segment offset.
+ */
guint segments_changed_stamp;
- GtkTextLine *end_iter_line;
+ /* Cache the last line in the buffer */
+ GtkTextLine *last_line;
+ guint last_line_stamp;
+ /* Cache the next-to-last line in the buffer,
+ * containing the end iterator
+ */
+ GtkTextLine *end_iter_line;
+ GtkTextLineSegment *end_iter_segment;
+ int end_iter_segment_byte_index;
+ int end_iter_segment_char_offset;
guint end_iter_line_stamp;
-
+ guint end_iter_segment_stamp;
+
GHashTable *child_anchor_table;
};
gpointer view_id);
static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
GtkTextBTreeNode *node);
+static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
+ GtkTextBTreeNode *node);
static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
gpointer view_id);
static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
GtkTextTag *tag,
gboolean size_changed,
GtkTextBTree *tree);
-static void tag_removed_cb (GtkTextTagTable *table,
- GtkTextTag *tag,
- GtkTextBTree *tree);
static void cleanup_line (GtkTextLine *line);
static void recompute_node_counts (GtkTextBTree *tree,
GtkTextBTreeNode *node);
GtkTextBTree*
_gtk_text_btree_new (GtkTextTagTable *table,
- GtkTextBuffer *buffer)
+ GtkTextBuffer *buffer)
{
GtkTextBTree *tree;
GtkTextBTreeNode *root_node;
tree->chars_changed_stamp = g_random_int ();
tree->segments_changed_stamp = g_random_int ();
+ tree->last_line_stamp = tree->chars_changed_stamp - 1;
+ tree->last_line = NULL;
+
tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
+ tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
tree->end_iter_line = NULL;
+ tree->end_iter_segment_byte_index = 0;
+ tree->end_iter_segment_char_offset = 0;
+
+ g_object_ref (G_OBJECT (tree->table));
- gtk_object_ref (GTK_OBJECT (tree->table));
- gtk_object_sink (GTK_OBJECT (tree->table));
-
- tree->tag_changed_handler = gtk_signal_connect (GTK_OBJECT (tree->table),
- "tag_changed",
- GTK_SIGNAL_FUNC (tag_changed_cb),
- tree);
-
- tree->tag_removed_handler = gtk_signal_connect (GTK_OBJECT (tree->table),
- "tag_removed",
- GTK_SIGNAL_FUNC (tag_removed_cb),
- tree);
+ tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
+ "tag_changed",
+ G_CALLBACK (tag_changed_cb),
+ tree);
tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
tree->child_anchor_table = NULL;
GtkTextIter start;
GtkTextLineSegment *seg;
- __gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
+ _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
tree->insert_mark = _gtk_text_btree_set_mark (tree,
tree->refcount += 1;
}
-static void
-mark_destroy_foreach (gpointer key, gpointer value, gpointer user_data)
-{
- GtkTextLineSegment *seg = value;
-
- g_return_if_fail (seg->body.mark.tree == NULL);
-
- g_object_unref (G_OBJECT (seg->body.mark.obj));
-}
-
void
_gtk_text_btree_unref (GtkTextBTree *tree)
{
{
gtk_text_btree_node_destroy (tree, tree->root_node);
- g_hash_table_foreach (tree->mark_table,
- mark_destroy_foreach,
- NULL);
+ g_assert (g_hash_table_size (tree->mark_table) == 0);
g_hash_table_destroy (tree->mark_table);
g_object_unref (G_OBJECT (tree->insert_mark));
g_object_unref (G_OBJECT (tree->selection_bound_mark));
- gtk_signal_disconnect (GTK_OBJECT (tree->table),
- tree->tag_changed_handler);
-
- gtk_signal_disconnect (GTK_OBJECT (tree->table),
- tree->tag_removed_handler);
+ g_signal_handler_disconnect (G_OBJECT (tree->table),
+ tree->tag_changed_handler);
- gtk_object_unref (GTK_OBJECT (tree->table));
+ g_object_unref (G_OBJECT (tree->table));
g_free (tree);
}
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
- g_return_if_fail (gtk_text_iter_get_btree (start) ==
- gtk_text_iter_get_btree (end));
+ g_return_if_fail (_gtk_text_iter_get_btree (start) ==
+ _gtk_text_iter_get_btree (end));
- gtk_text_iter_reorder (start, end);
-
- tree = gtk_text_iter_get_btree (start);
+ gtk_text_iter_order (start, end);
+ tree = _gtk_text_iter_get_btree (start);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ _gtk_text_btree_check (tree);
+
{
+ /* FIXME this code should no longer be required */
/*
* The code below is ugly, but it's needed to make sure there
* is always a dummy empty line at the end of the text. If the
}
tags = _gtk_text_btree_get_tags (end,
- &array_size);
+ &array_size);
if (tags != NULL)
{
/* Save the byte offset so we can reset the iterators */
start_byte_offset = gtk_text_iter_get_line_index (start);
- start_line = gtk_text_iter_get_text_line (start);
- end_line = gtk_text_iter_get_text_line (end);
+ start_line = _gtk_text_iter_get_text_line (start);
+ end_line = _gtk_text_iter_get_text_line (end);
/*
* Split the start and end segments, so we have a place
for (node = curnode; node != NULL;
node = node->parent)
{
+ /* Don't update node->num_chars, because
+ * that was done when we deleted the segments.
+ */
node->num_lines -= 1;
}
prevnode->next = curnode->next;
}
parent->num_children--;
- g_free (curnode);
+ gtk_text_btree_node_free_empty (tree, curnode);
curnode = parent;
}
curnode = curline->parent;
next = seg->next;
char_count = seg->char_count;
- if ((*seg->type->deleteFunc)(seg, curline, 0) != 0)
+ if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
{
/*
* This segment refuses to die. Move it to prev_seg and
{
BTreeView *view;
GtkTextBTreeNode *ancestor_node;
-
GtkTextLine *prevline;
+ int chars_moved;
+ /* last_seg was appended to start_line up at the top of this function */
+ chars_moved = 0;
for (seg = last_seg; seg != NULL;
seg = seg->next)
{
+ chars_moved += seg->char_count;
if (seg->type->lineChangeFunc != NULL)
{
(*seg->type->lineChangeFunc)(seg, end_line);
}
}
+
+ for (node = start_line->parent; node != NULL;
+ node = node->parent)
+ {
+ node->num_chars += chars_moved;
+ }
+
curnode = end_line->parent;
for (node = curnode; node != NULL;
node = node->parent)
{
+ node->num_chars -= chars_moved;
node->num_lines--;
}
curnode->num_children--;
view = view->next;
}
+ /* avoid dangling pointer */
+ deleted_lines = NULL;
+
gtk_text_btree_rebalance (tree, curnode);
}
void
_gtk_text_btree_insert (GtkTextIter *iter,
- const gchar *text,
- gint len)
+ const gchar *text,
+ gint len)
{
GtkTextLineSegment *prev_seg; /* The segment just before the first
* new segment (NULL means new segment
len = strlen (text);
/* extract iterator info */
- tree = gtk_text_iter_get_btree (iter);
- line = gtk_text_iter_get_text_line (iter);
+ tree = _gtk_text_iter_get_btree (iter);
+ line = _gtk_text_iter_get_text_line (iter);
+
start_line = line;
start_byte_index = gtk_text_iter_get_line_index (iter);
- /* Get our insertion segment split */
+ /* Get our insertion segment split. Note this assumes line allows
+ * char insertions, which isn't true of the "last" line. But iter
+ * should not be on that line, as we assert here.
+ */
+ g_assert (!_gtk_text_line_is_last (line, tree));
prev_seg = gtk_text_line_segment_split (iter);
cur_seg = prev_seg;
/* Invalidate all iterators */
chars_changed (tree);
segments_changed (tree);
-
+
/*
* Chop the text up into lines and create a new segment for
* each line, plus a new line for the leftovers from the
char_count_delta = 0;
while (eol < len)
{
+ sol = eol;
+
pango_find_paragraph_boundary (text + sol,
len - sol,
&delim,
/* make these relative to the start of the text */
delim += sol;
eol += sol;
+
+ g_assert (eol >= sol);
+ g_assert (delim >= sol);
+ g_assert (eol >= delim);
+ g_assert (sol >= 0);
+ g_assert (eol <= len);
chunk_len = eol - sol;
-
+
+ g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
seg = _gtk_char_segment_new (&text[sol], chunk_len);
char_count_delta += seg->char_count;
}
if (delim == eol)
- /* chunk didn't end with a paragraph separator */
- break;
+ {
+ /* chunk didn't end with a paragraph separator */
+ g_assert (eol == len);
+ break;
+ }
/*
* The chunk ended with a newline, so create a new GtkTextLine
line = newline;
cur_seg = NULL;
line_count_delta++;
-
- sol = eol;
}
/*
_gtk_text_btree_get_iter_at_line (tree,
- &start,
- start_line,
- start_byte_index);
+ &start,
+ start_line,
+ start_byte_index);
end = start;
/* We could almost certainly be more efficient here
GtkTextBTree *tree;
gint start_byte_offset;
- line = gtk_text_iter_get_text_line (iter);
- tree = gtk_text_iter_get_btree (iter);
+ line = _gtk_text_iter_get_text_line (iter);
+ tree = _gtk_text_iter_get_btree (iter);
start_byte_offset = gtk_text_iter_get_line_index (iter);
prevPtr = gtk_text_line_segment_split (iter);
}
void
-__gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
+_gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
GdkPixbuf *pixbuf)
{
GtkTextLineSegment *seg;
insert_pixbuf_or_widget_segment (iter, seg);
}
-GtkTextChildAnchor*
-_gtk_text_btree_create_child_anchor (GtkTextIter *iter)
+void
+_gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
+ GtkTextChildAnchor *anchor)
{
GtkTextLineSegment *seg;
GtkTextBTree *tree;
+
+ if (anchor->segment != NULL)
+ {
+ g_warning (G_STRLOC": Same child anchor can't be inserted twice");
+ return;
+ }
- seg = _gtk_widget_segment_new ();
+ seg = _gtk_widget_segment_new (anchor);
- tree = seg->body.child.tree = gtk_text_iter_get_btree (iter);
+ tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
insert_pixbuf_or_widget_segment (iter, seg);
g_hash_table_insert (tree->child_anchor_table,
seg->body.child.obj,
seg->body.child.obj);
-
- return seg->body.child.obj;
}
void
GtkTextLine *
_gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
- gpointer view_id,
- gint ypixel,
- gint *line_top_out)
+ gpointer view_id,
+ gint ypixel,
+ gint *line_top_out)
{
GtkTextLine *line;
BTreeView *view;
GtkTextLineData *line_data;
g_return_if_fail (tree != NULL);
-
+
view = g_new (BTreeView, 1);
view->view_id = layout;
view->next = tree->views;
view->prev = NULL;
+ if (tree->views)
+ {
+ g_assert (tree->views->prev == NULL);
+ tree->views->prev = view;
+ }
+
tree->views = view;
/* The last line in the buffer has identity values for the per-view
void
_gtk_text_btree_remove_view (GtkTextBTree *tree,
- gpointer view_id)
+ gpointer view_id)
{
BTreeView *view;
GtkTextLine *last_line;
GtkTextLineData *line_data;
g_return_if_fail (tree != NULL);
-
+
view = tree->views;
while (view != NULL)
gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
+ view->layout = (gpointer) 0xdeadbeef;
+ view->view_id = (gpointer) 0xdeadbeef;
+
g_free (view);
}
const GtkTextIter *start,
const GtkTextIter *end)
{
- if (gtk_text_tag_affects_size (tag))
+ if (_gtk_text_tag_affects_size (tag))
{
_gtk_text_btree_invalidate_region (tree, start, end);
-
}
- else if (gtk_text_tag_affects_nonsize_appearance (tag))
+ else if (_gtk_text_tag_affects_nonsize_appearance (tag))
{
/* We only need to queue a redraw, not a relayout */
redisplay_region (tree, start, end);
void
_gtk_text_btree_tag (const GtkTextIter *start_orig,
- const GtkTextIter *end_orig,
- GtkTextTag *tag,
- gboolean add)
+ const GtkTextIter *end_orig,
+ GtkTextTag *tag,
+ gboolean add)
{
GtkTextLineSegment *seg, *prev;
GtkTextLine *cleanupline;
g_return_if_fail (start_orig != NULL);
g_return_if_fail (end_orig != NULL);
g_return_if_fail (GTK_IS_TEXT_TAG (tag));
- g_return_if_fail (gtk_text_iter_get_btree (start_orig) ==
- gtk_text_iter_get_btree (end_orig));
-
+ g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
+ _gtk_text_iter_get_btree (end_orig));
+ g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
+
#if 0
printf ("%s tag %s from %d to %d\n",
add ? "Adding" : "Removing",
start = *start_orig;
end = *end_orig;
- gtk_text_iter_reorder (&start, &end);
+ gtk_text_iter_order (&start, &end);
- tree = gtk_text_iter_get_btree (&start);
+ tree = _gtk_text_iter_get_btree (&start);
queue_tag_redisplay (tree, tag, &start, &end);
info = gtk_text_btree_get_tag_info (tree, tag);
- start_line = gtk_text_iter_get_text_line (&start);
- end_line = gtk_text_iter_get_text_line (&end);
+ start_line = _gtk_text_iter_get_text_line (&start);
+ end_line = _gtk_text_iter_get_text_line (&end);
/* Find all tag toggles in the region; we are going to delete them.
We need to find them in advance, because
with the tree. */
stack = iter_stack_new ();
iter = start;
- /* We don't want to delete a toggle that's at the start iterator. */
- gtk_text_iter_forward_char (&iter);
+
+ /* forward_to_tag_toggle() skips a toggle at the start iterator,
+ * which is deliberate - we don't want to delete a toggle at the
+ * start.
+ */
while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
{
if (gtk_text_iter_compare (&iter, &end) >= 0)
GtkTextLineSegment *indexable_seg;
GtkTextLine *line;
- line = gtk_text_iter_get_text_line (&iter);
- seg = gtk_text_iter_get_any_segment (&iter);
- indexable_seg = gtk_text_iter_get_indexable_segment (&iter);
+ line = _gtk_text_iter_get_text_line (&iter);
+ seg = _gtk_text_iter_get_any_segment (&iter);
+ indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
g_assert (seg != NULL);
g_assert (indexable_seg != NULL);
* "Getters"
*/
-GtkTextLine*
-_gtk_text_btree_get_line (GtkTextBTree *tree,
- gint line_number,
- gint *real_line_number)
+static GtkTextLine*
+get_line_internal (GtkTextBTree *tree,
+ gint line_number,
+ gint *real_line_number,
+ gboolean include_last)
{
GtkTextBTreeNode *node;
GtkTextLine *line;
int line_count;
line_count = _gtk_text_btree_line_count (tree);
-
+ if (!include_last)
+ line_count -= 1;
+
if (line_number < 0)
{
line_number = line_count;
}
GtkTextLine*
-__gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
- gint char_index,
- gint *line_start_index,
- gint *real_char_index)
+_gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
+{
+ return
+ _gtk_text_btree_get_line (tree,
+ _gtk_text_btree_line_count (tree) - 1,
+ NULL);
+}
+
+GtkTextLine*
+_gtk_text_btree_get_line (GtkTextBTree *tree,
+ gint line_number,
+ gint *real_line_number)
+{
+ return get_line_internal (tree, line_number, real_line_number, TRUE);
+}
+
+GtkTextLine*
+_gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
+ gint line_number,
+ gint *real_line_number)
+{
+ return get_line_internal (tree, line_number, real_line_number, FALSE);
+}
+
+GtkTextLine*
+_gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
+ gint char_index,
+ gint *line_start_index,
+ gint *real_char_index)
{
GtkTextBTreeNode *node;
GtkTextLine *line;
node = tree->root_node;
- /* Clamp to valid indexes (-1 is magic for "highest index") */
- if (char_index < 0 || char_index >= node->num_chars)
+ /* Clamp to valid indexes (-1 is magic for "highest index"),
+ * node->num_chars includes the two newlines that aren't really
+ * in the buffer.
+ */
+ if (char_index < 0 || char_index >= (node->num_chars - 1))
{
- char_index = node->num_chars - 1;
+ char_index = node->num_chars - 2;
}
*real_char_index = char_index;
#define NUM_TAG_INFOS 10
- line = gtk_text_iter_get_text_line (iter);
- tree = gtk_text_iter_get_btree (iter);
+ line = _gtk_text_iter_get_text_line (iter);
+ tree = _gtk_text_iter_get_btree (iter);
byte_index = gtk_text_iter_get_line_index (iter);
tagInfo.numTags = 0;
if (gtk_text_iter_equal (start, end))
return;
- seg = gtk_text_iter_get_indexable_segment (start);
- end_seg = gtk_text_iter_get_indexable_segment (end);
+ seg = _gtk_text_iter_get_indexable_segment (start);
+ end_seg = _gtk_text_iter_get_indexable_segment (end);
if (seg->type == >k_text_char_type)
{
/* printf (" <invisible>\n"); */
}
- copy_start = gtk_text_iter_get_segment_byte (start);
+ copy_start = _gtk_text_iter_get_segment_byte (start);
if (seg == end_seg)
{
/* End is in the same segment; need to copy fewer bytes. */
- gint end_byte = gtk_text_iter_get_segment_byte (end);
+ gint end_byte = _gtk_text_iter_get_segment_byte (end);
copy_bytes = end_byte - copy_start;
}
g_return_val_if_fail (start_orig != NULL, NULL);
g_return_val_if_fail (end_orig != NULL, NULL);
- g_return_val_if_fail (gtk_text_iter_get_btree (start_orig) ==
- gtk_text_iter_get_btree (end_orig), NULL);
+ g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
+ _gtk_text_iter_get_btree (end_orig), NULL);
start = *start_orig;
end = *end_orig;
- gtk_text_iter_reorder (&start, &end);
+ gtk_text_iter_order (&start, &end);
retval = g_string_new ("");
- tree = gtk_text_iter_get_btree (&start);
+ tree = _gtk_text_iter_get_btree (&start);
- end_seg = gtk_text_iter_get_indexable_segment (&end);
+ end_seg = _gtk_text_iter_get_indexable_segment (&end);
iter = start;
- seg = gtk_text_iter_get_indexable_segment (&iter);
+ seg = _gtk_text_iter_get_indexable_segment (&iter);
while (seg != end_seg)
{
copy_segment (retval, include_hidden, include_nonchars,
&iter, &end);
- gtk_text_iter_forward_indexable_segment (&iter);
+ _gtk_text_iter_forward_indexable_segment (&iter);
- seg = gtk_text_iter_get_indexable_segment (&iter);
+ seg = _gtk_text_iter_get_indexable_segment (&iter);
}
copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
gint
_gtk_text_btree_char_count (GtkTextBTree *tree)
{
- /* Exclude newline in bogus last line */
- return tree->root_node->num_chars - 1;
+ /* Exclude newline in bogus last line and the
+ * one in the last line that is after the end iterator
+ */
+ return tree->root_node->num_chars - 2;
}
#define LOTSA_TAGS 1000
GtkTextBTree *tree;
gint byte_index;
- line = gtk_text_iter_get_text_line (iter);
- tree = gtk_text_iter_get_btree (iter);
+ line = _gtk_text_iter_get_text_line (iter);
+ tree = _gtk_text_iter_get_btree (iter);
byte_index = gtk_text_iter_get_line_index (iter);
- numTags = gtk_text_tag_table_size (tree->table);
+ numTags = gtk_text_tag_table_get_size (tree->table);
/* almost always avoid malloc, so stay out of system calls */
if (LOTSA_TAGS < numTags)
end = tmp;
}
- start_line = gtk_text_iter_get_text_line (start);
- end_line = gtk_text_iter_get_text_line (end);
+ start_line = _gtk_text_iter_get_text_line (start);
+ end_line = _gtk_text_iter_get_text_line (end);
view = tree->views;
while (view != NULL)
g_return_val_if_fail (tree != NULL, NULL);
g_return_val_if_fail (where != NULL, NULL);
- g_return_val_if_fail (gtk_text_iter_get_btree (where) == tree, NULL);
+ g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
if (existing_mark)
mark = existing_mark->segment;
iter = *where;
if (gtk_debug_flags & GTK_DEBUG_TEXT)
- gtk_text_iter_check (&iter);
+ _gtk_text_iter_check (&iter);
if (mark != NULL)
{
This could hose our iterator... */
gtk_text_btree_unlink_segment (tree, mark,
mark->body.mark.line);
- mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
- g_assert (mark->body.mark.line == gtk_text_iter_get_text_line (&iter));
+ mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
+ g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
segments_changed (tree); /* make sure the iterator recomputes its
segment */
left_gravity,
name);
- mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
+ mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
if (mark->body.mark.name)
g_hash_table_insert (tree->mark_table,
}
if (gtk_debug_flags & GTK_DEBUG_TEXT)
- gtk_text_iter_check (&iter);
+ _gtk_text_iter_check (&iter);
/* Link mark into new location */
gtk_text_btree_link_segment (mark, &iter);
redisplay_mark_if_visible (mark);
if (gtk_debug_flags & GTK_DEBUG_TEXT)
- gtk_text_iter_check (&iter);
+ _gtk_text_iter_check (&iter);
if (gtk_debug_flags & GTK_DEBUG_TEXT)
_gtk_text_btree_check (tree);
}
else
{
- gtk_text_iter_reorder (&tmp_start, &tmp_end);
+ gtk_text_iter_order (&tmp_start, &tmp_end);
if (start)
*start = tmp_start;
}
void
-__gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
+_gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
const gchar *name)
{
GtkTextMark *mark;
_gtk_text_btree_remove_mark (tree, mark);
}
+void
+_gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
+ GtkTextLineSegment *segment)
+{
+
+ if (segment->body.mark.name)
+ g_hash_table_remove (tree->mark_table, segment->body.mark.name);
+
+ segment->body.mark.tree = NULL;
+ segment->body.mark.line = NULL;
+
+ /* Remove the ref on the mark, which frees segment as a side effect
+ * if this is the last reference.
+ */
+ g_object_unref (G_OBJECT (segment->body.mark.obj));
+}
+
void
_gtk_text_btree_remove_mark (GtkTextBTree *tree,
- GtkTextMark *mark)
+ GtkTextMark *mark)
{
GtkTextLineSegment *segment;
/* This calls cleanup_line and segments_changed */
gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
-
- if (segment->body.mark.name)
- g_hash_table_remove (tree->mark_table, segment->body.mark.name);
-
- /* Remove the ref on the mark that belonged to the segment. */
- g_object_unref (G_OBJECT (mark));
-
- segment->body.mark.tree = NULL;
- segment->body.mark.line = NULL;
+
+ _gtk_text_btree_release_mark_segment (tree, segment);
}
gboolean
_gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
- GtkTextMark *segment)
+ GtkTextMark *segment)
{
return segment == tree->insert_mark;
}
gboolean
_gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
- GtkTextMark *segment)
+ GtkTextMark *segment)
{
return segment == tree->selection_bound_mark;
}
GtkTextMark*
_gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
- const gchar *name)
+ const gchar *name)
{
GtkTextLineSegment *seg;
return seg ? seg->body.mark.obj : NULL;
}
+/**
+ * gtk_text_mark_set_visible:
+ * @mark: a #GtkTextMark
+ * @setting: visibility of mark
+ *
+ * Sets the visibility of @mark; the insertion point is normally
+ * visible, i.e. you can see it as a vertical bar. Also, the text
+ * widget uses a visible mark to indicate where a drop will occur when
+ * dragging-and-dropping text. Most other marks are not visible.
+ * Marks are not visible by default.
+ *
+ **/
void
gtk_text_mark_set_visible (GtkTextMark *mark,
gboolean setting)
at least not without complexity.
So, we just return the last line.
*/
- return _gtk_text_btree_get_line (tree, -1, NULL);
+ return _gtk_text_btree_get_end_iter_line (tree);
}
}
/* FIXME this function is far too slow, for no good reason. */
gboolean
_gtk_text_line_char_has_tag (GtkTextLine *line,
- GtkTextBTree *tree,
- gint char_in_line,
- GtkTextTag *tag)
+ GtkTextBTree *tree,
+ gint char_in_line,
+ GtkTextTag *tag)
{
GtkTextLineSegment *toggle_seg;
gboolean
_gtk_text_line_byte_has_tag (GtkTextLine *line,
- GtkTextBTree *tree,
- gint byte_in_line,
- GtkTextTag *tag)
+ GtkTextBTree *tree,
+ gint byte_in_line,
+ GtkTextTag *tag)
{
GtkTextLineSegment *toggle_seg;
gboolean
_gtk_text_line_is_last (GtkTextLine *line,
- GtkTextBTree *tree)
+ GtkTextBTree *tree)
{
return line == get_last_line (tree);
}
+static void
+ensure_end_iter_line (GtkTextBTree *tree)
+{
+ if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
+ {
+ int n_lines;
+ int real_line;
+
+ /* n_lines is without the magic line at the end */
+ n_lines = _gtk_text_btree_line_count (tree);
+
+ g_assert (n_lines >= 1);
+
+ tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
+
+ tree->end_iter_line_stamp = tree->chars_changed_stamp;
+ }
+}
+
+static void
+ensure_end_iter_segment (GtkTextBTree *tree)
+{
+ if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
+ {
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *last_with_chars;
+
+ ensure_end_iter_line (tree);
+
+ last_with_chars = NULL;
+
+ seg = tree->end_iter_line->segments;
+ while (seg != NULL)
+ {
+ if (seg->char_count > 0)
+ last_with_chars = seg;
+ seg = seg->next;
+ }
+
+ tree->end_iter_segment = last_with_chars;
+
+ /* We know the last char in the last line is '\n' */
+ tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
+ tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
+
+ tree->end_iter_segment_stamp = tree->segments_changed_stamp;
+
+ g_assert (tree->end_iter_segment->type == >k_text_char_type);
+ g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
+ }
+}
+
+gboolean
+_gtk_text_line_contains_end_iter (GtkTextLine *line,
+ GtkTextBTree *tree)
+{
+ ensure_end_iter_line (tree);
+
+ return line == tree->end_iter_line;
+}
+
+gboolean
+_gtk_text_btree_is_end (GtkTextBTree *tree,
+ GtkTextLine *line,
+ GtkTextLineSegment *seg,
+ int byte_index,
+ int char_offset)
+{
+ g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
+
+ /* Do this first to avoid walking segments in most cases */
+ if (!_gtk_text_line_contains_end_iter (line, tree))
+ return FALSE;
+
+ ensure_end_iter_segment (tree);
+
+ if (seg != tree->end_iter_segment)
+ return FALSE;
+
+ if (byte_index >= 0)
+ return byte_index == tree->end_iter_segment_byte_index;
+ else
+ return char_offset == tree->end_iter_segment_char_offset;
+}
+
GtkTextLine*
_gtk_text_line_next (GtkTextLine *line)
{
}
}
+GtkTextLine*
+_gtk_text_line_next_excluding_last (GtkTextLine *line)
+{
+ GtkTextLine *next;
+
+ next = _gtk_text_line_next (line);
+
+ /* If we were on the end iter line, we can't go to
+ * the last line
+ */
+ if (next && next->next == NULL && /* these checks are optimization only */
+ _gtk_text_line_next (next) == NULL)
+ return NULL;
+
+ return next;
+}
+
GtkTextLine*
_gtk_text_line_previous (GtkTextLine *line)
{
gpointer
_gtk_text_line_get_data (GtkTextLine *line,
- gpointer view_id)
+ gpointer view_id)
{
GtkTextLineData *iter;
void
_gtk_text_line_invalidate_wrap (GtkTextLine *line,
- GtkTextLineData *ld)
+ GtkTextLineData *ld)
{
/* For now this is totally unoptimized. FIXME?
is less than the max width for the parent node,
and the case where the height is unchanged when we re-wrap.
*/
-
+
g_return_if_fail (ld != NULL);
-
+
ld->valid = FALSE;
gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
}
/* FIXME sync with char_locate (or figure out a clean
way to merge the two functions) */
-void
+gboolean
_gtk_text_line_byte_locate (GtkTextLine *line,
- gint byte_offset,
- GtkTextLineSegment **segment,
- GtkTextLineSegment **any_segment,
- gint *seg_byte_offset,
- gint *line_byte_offset)
+ gint byte_offset,
+ GtkTextLineSegment **segment,
+ GtkTextLineSegment **any_segment,
+ gint *seg_byte_offset,
+ gint *line_byte_offset)
{
GtkTextLineSegment *seg;
GtkTextLineSegment *after_prev_indexable;
gint offset;
gint bytes_in_line;
- g_return_if_fail (line != NULL);
-
- if (byte_offset < 0)
- {
- /* -1 means end of line; we here assume no line is
- longer than 1 bazillion bytes, of course we assumed
- that anyway since we'd wrap around... */
-
- byte_offset = G_MAXINT;
- }
+ g_return_val_if_fail (line != NULL, FALSE);
+ g_return_val_if_fail (byte_offset >= 0, FALSE);
*segment = NULL;
*any_segment = NULL;
if (seg == NULL)
{
/* We went off the end of the line */
- *segment = last_indexable;
- *any_segment = after_prev_indexable;
- /* subtracting 1 is OK, we know it's a newline at the end. */
- offset = (*segment)->byte_count - 1;
- bytes_in_line -= (*segment)->byte_count;
+ if (offset != 0)
+ g_warning ("%s: byte index off the end of the line", G_STRLOC);
+
+ return FALSE;
}
else
{
g_assert (*seg_byte_offset < (*segment)->byte_count);
*line_byte_offset = bytes_in_line + *seg_byte_offset;
+
+ return TRUE;
}
/* FIXME sync with byte_locate (or figure out a clean
way to merge the two functions) */
-void
+gboolean
_gtk_text_line_char_locate (GtkTextLine *line,
- gint char_offset,
- GtkTextLineSegment **segment,
- GtkTextLineSegment **any_segment,
- gint *seg_char_offset,
- gint *line_char_offset)
+ gint char_offset,
+ GtkTextLineSegment **segment,
+ GtkTextLineSegment **any_segment,
+ gint *seg_char_offset,
+ gint *line_char_offset)
{
GtkTextLineSegment *seg;
GtkTextLineSegment *after_prev_indexable;
gint offset;
gint chars_in_line;
- g_return_if_fail (line != NULL);
-
- if (char_offset < 0)
- {
- /* -1 means end of line; we here assume no line is
- longer than 1 bazillion chars, of course we assumed
- that anyway since we'd wrap around... */
-
- char_offset = G_MAXINT;
- }
-
+ g_return_val_if_fail (line != NULL, FALSE);
+ g_return_val_if_fail (char_offset >= 0, FALSE);
+
*segment = NULL;
*any_segment = NULL;
chars_in_line = 0;
if (seg == NULL)
{
- /* We went off the end of the line */
- *segment = last_indexable;
- *any_segment = after_prev_indexable;
- /* subtracting 1 is OK, we know it's a newline at the end. */
- offset = (*segment)->char_count - 1;
- chars_in_line -= (*segment)->char_count;
+ /* end of the line */
+ if (offset != 0)
+ g_warning ("%s: char offset off the end of the line", G_STRLOC);
+
+ return FALSE;
}
else
{
g_assert (*seg_char_offset < (*segment)->char_count);
*line_char_offset = chars_in_line + *seg_char_offset;
+
+ return TRUE;
}
void
-__gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
+_gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
gint byte_offset,
gint *line_char_offset,
gint *seg_char_offset)
}
void
-__gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
+_gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
gint char_offset,
gint *line_byte_offset,
gint *seg_byte_offset)
/* remember that tag == NULL means "any tag" */
GtkTextLine*
-__gtk_text_line_next_could_contain_tag (GtkTextLine *line,
- GtkTextBTree *tree,
- GtkTextTag *tag)
+_gtk_text_line_next_could_contain_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag)
{
GtkTextBTreeNode *node;
GtkTextTagInfo *info;
/* Right now we can only offer linear-search if the user wants
* to know about any tag toggle at all.
*/
- return _gtk_text_line_next (line);
+ return _gtk_text_line_next_excluding_last (line);
}
/* Our tag summaries only have node precision, not line
- precision. This means that if any line under a node could contain a
- tag, then any of the others could also contain a tag.
-
- In the future we could have some mechanism to keep track of how
- many toggles we've found under a node so far, since we have a
- count of toggles under the node. But for now I'm going with KISS.
- */
+ * precision. This means that if any line under a node could contain a
+ * tag, then any of the others could also contain a tag.
+ *
+ * In the future we could have some mechanism to keep track of how
+ * many toggles we've found under a node so far, since we have a
+ * count of toggles under the node. But for now I'm going with KISS.
+ */
/* return same-node line, if any. */
if (line->next)
}
GtkTextLine*
-__gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
+_gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
GtkTextBTree *tree,
GtkTextTag *tag)
{
static GtkTextLine*
get_last_line (GtkTextBTree *tree)
{
- if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
+ if (tree->last_line_stamp != tree->chars_changed_stamp)
{
gint n_lines;
GtkTextLine *line;
line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
- tree->end_iter_line_stamp = tree->chars_changed_stamp;
- tree->end_iter_line = line;
+ tree->last_line_stamp = tree->chars_changed_stamp;
+ tree->last_line = line;
}
- return tree->end_iter_line;
+ return tree->last_line;
}
/*
node_data_new (gpointer view_id)
{
NodeData *nd;
-
+
nd = g_new (NodeData, 1);
nd->view_id = view_id;
static void
node_data_destroy (NodeData *nd)
{
-
g_free (nd);
}
/**
- * __gtk_text_btree_validate_line:
+ * _gtk_text_btree_validate_line:
* @tree: a #GtkTextBTree
* @line: line to validate
* @view_id: view ID for the view to validate
* results up through the entire tree.
**/
void
-__gtk_text_btree_validate_line (GtkTextBTree *tree,
- GtkTextLine *line,
- gpointer view_id)
+_gtk_text_btree_validate_line (GtkTextBTree *tree,
+ GtkTextLine *line,
+ gpointer view_id)
{
GtkTextLineData *ld;
- GtkTextLine *last_line;
BTreeView *view;
g_return_if_fail (tree != NULL);
view = gtk_text_btree_get_view (tree, view_id);
g_return_if_fail (view != NULL);
-
+
ld = _gtk_text_line_get_data (line, view_id);
if (!ld || !ld->valid)
{
ld = gtk_text_layout_wrap (view->layout, line, ld);
- last_line = get_last_line (tree);
-
+
gtk_text_btree_node_check_valid_upward (line->parent, view_id);
}
}
seg = line->segments;
line->segments = seg->next;
- if (GTK_IS_TEXT_MARK_SEGMENT (seg))
- {
- /* Set the mark as deleted */
- seg->body.mark.tree = NULL;
- seg->body.mark.line = NULL;
- }
-
- (*seg->type->deleteFunc) (seg, line, 1);
+ (*seg->type->deleteFunc) (seg, line, TRUE);
}
gtk_text_line_destroy (tree, line);
}
}
}
+ gtk_text_btree_node_free_empty (tree, node);
+}
+
+static void
+gtk_text_btree_node_free_empty (GtkTextBTree *tree,
+ GtkTextBTreeNode *node)
+{
+ g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
+ (node->level == 0 && node->children.line == NULL));
+
summary_list_destroy (node->summary);
node_data_list_destroy (node->node_data);
g_free (node);
nd = nd->next;
}
- if (nd == NULL) {
- nd = node_data_new (view_id);
-
- if (node->node_data)
- nd->next = node->node_data;
-
- node->node_data = nd;
- }
+ if (nd == NULL)
+ {
+ nd = node_data_new (view_id);
+
+ if (node->node_data)
+ nd->next = node->node_data;
+
+ node->node_data = nd;
+ }
return nd;
}
GtkTextIter *start,
GtkTextIter *end)
{
- __gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
- _gtk_text_btree_get_last_iter (tree, end);
+ _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
+ _gtk_text_btree_get_end_iter (tree, end);
}
static void
}
}
-static void
-tag_removed_cb (GtkTextTagTable *table,
- GtkTextTag *tag,
- GtkTextBTree *tree)
+void
+_gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
+ GtkTextTag *tag)
{
/* Remove the tag from the tree */
{
tree->root_node = node->children.node;
tree->root_node->parent = NULL;
- summary_list_destroy (node->summary);
- g_free (node);
+
+ node->children.node = NULL;
+ gtk_text_btree_node_free_empty (tree, node);
}
return;
}
recompute_node_counts (tree, node);
node->next = other->next;
node->parent->num_children--;
- summary_list_destroy (other->summary);
- g_free (other);
+
+ other->children.node = NULL;
+ other->children.line = NULL;
+ gtk_text_btree_node_free_empty (tree, other);
continue;
}
info = g_new (GtkTextTagInfo, 1);
info->tag = tag;
- gtk_object_ref (GTK_OBJECT (tag));
+ g_object_ref (G_OBJECT (tag));
info->tag_root = NULL;
info->toggle_count = 0;
list->next = NULL;
g_slist_free (list);
- gtk_object_unref (GTK_OBJECT (info->tag));
+ g_object_unref (G_OBJECT (info->tag));
g_free (info);
return;
list = g_slist_next (list);
}
-
- g_assert_not_reached ();
- return;
}
static void
gtk_text_btree_node_check_valid (node, view->view_id);
view = view->next;
}
-
+
/*
* Scan through the GtkTextBTreeNode's tag records again and delete any Summary
* records that still have a zero count, or that have all the toggles.
GtkTextLine *line;
GtkTextBTree *tree;
- line = gtk_text_iter_get_text_line (iter);
- tree = gtk_text_iter_get_btree (iter);
+ line = _gtk_text_iter_get_text_line (iter);
+ tree = _gtk_text_iter_get_btree (iter);
prev = gtk_text_line_segment_split (iter);
if (prev == NULL)
*/
static void
-gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node,
+gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
+ GtkTextBTreeNode *node,
NodeData *nd)
{
gint width;
gint height;
gboolean valid;
+ BTreeView *view;
+
+ view = tree->views;
+
+ while (view != NULL)
+ {
+ if (view->view_id == nd->view_id)
+ break;
+ view = view->next;
+ }
+
+ if (view == NULL)
+ g_error ("Node has data for a view %p no longer attached to the tree",
+ nd->view_id);
+
gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
&width, &height, &valid);
+
+ /* valid aggregate not checked the same as width/height, because on
+ * btree rebalance we can have invalid nodes where all lines below
+ * them are actually valid, due to moving lines around between
+ * nodes.
+ *
+ * The guarantee is that if there are invalid lines the node is
+ * invalid - we don't guarantee that if the node is invalid there
+ * are invalid lines.
+ */
+
if (nd->width != width ||
nd->height != height ||
- !nd->valid != !valid)
+ (nd->valid && !valid))
{
g_error ("Node aggregates for view %p are invalid:\n"
"Are (%d,%d,%s), should be (%d,%d,%s)",
}
static void
-gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
+gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
+ GtkTextBTreeNode *node)
{
GtkTextBTreeNode *childnode;
Summary *summary, *summary2;
nd = node->node_data;
while (nd != NULL)
{
- gtk_text_btree_node_view_check_consistency (node, nd);
+ gtk_text_btree_node_view_check_consistency (tree, node, nd);
nd = nd->next;
}
g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
node->level, childnode->level);
}
- gtk_text_btree_node_check_consistency (childnode);
+ gtk_text_btree_node_check_consistency (tree, childnode);
for (summary = childnode->summary; summary != NULL;
summary = summary->next)
{
*/
node = tree->root_node;
- gtk_text_btree_node_check_consistency (tree->root_node);
+ gtk_text_btree_node_check_consistency (tree, tree->root_node);
/*
* Make sure that there are at least two lines in the text and