/*
- * gtktextbtree.c --
+ * Gtktextbtree.c --
*
* This file contains code that manages the B-tree representation
* of text for the text buffer and implements character and
*
*/
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <config.h>
#include "gtktextbtree.h"
#include <string.h>
-#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
-#include "gtksignal.h"
#include "gtktexttag.h"
#include "gtktexttagtable.h"
#include "gtktextlayout.h"
#include "gtktextiterprivate.h"
#include "gtkdebug.h"
#include "gtktextmarkprivate.h"
+#include "gtkalias.h"
/*
* Types
/* Height and width of this node */
gint height;
- gint width : 24;
+ signed int width : 24;
/* boolean indicating whether the lines below this node are in need of validation.
* However, width/height should always represent the current total width and
* width/height on the lines needs recomputing, not whether the totals
* need recomputing.
*/
- gint valid : 8;
+ guint valid : 8; /* Actually a boolean */
};
GtkTextBuffer *buffer;
BTreeView *views;
GSList *tag_infos;
- guint tag_changed_handler;
- guint tag_removed_handler;
+ gulong tag_changed_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
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);
tree->end_iter_segment_byte_index = 0;
tree->end_iter_segment_char_offset = 0;
- g_object_ref (G_OBJECT (tree->table));
+ g_object_ref (tree->table);
- tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
+ tree->tag_changed_handler = g_signal_connect (tree->table,
"tag_changed",
G_CALLBACK (tag_changed_cb),
tree);
- tree->tag_removed_handler = g_signal_connect (G_OBJECT (tree->table),
- "tag_removed",
- G_CALLBACK (tag_removed_cb),
- tree);
-
tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
tree->child_anchor_table = NULL;
seg->body.mark.not_deleteable = TRUE;
- g_object_ref (G_OBJECT (tree->insert_mark));
- g_object_ref (G_OBJECT (tree->selection_bound_mark));
+ g_object_ref (tree->insert_mark);
+ g_object_ref (tree->selection_bound_mark);
}
tree->refcount = 1;
tree->refcount -= 1;
if (tree->refcount == 0)
- {
- gtk_text_btree_node_destroy (tree, tree->root_node);
+ {
+ g_signal_handler_disconnect (tree->table,
+ tree->tag_changed_handler);
+ g_object_unref (tree->table);
+ tree->table = NULL;
+
+ gtk_text_btree_node_destroy (tree, tree->root_node);
+ tree->root_node = 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));
-
- g_signal_handler_disconnect (G_OBJECT (tree->table),
- tree->tag_changed_handler);
-
- g_signal_handler_disconnect (G_OBJECT (tree->table),
- tree->tag_removed_handler);
-
- g_object_unref (G_OBJECT (tree->table));
+ tree->mark_table = NULL;
+ if (tree->child_anchor_table != NULL)
+ {
+ g_hash_table_destroy (tree->child_anchor_table);
+ tree->child_anchor_table = NULL;
+ }
+
+ g_object_unref (tree->insert_mark);
+ tree->insert_mark = NULL;
+ g_object_unref (tree->selection_bound_mark);
+ tree->selection_bound_mark = NULL;
g_free (tree);
}
* Indexable segment mutation
*/
+/*
+ * The following function is responsible for resolving the bidi direction
+ * for the lines between start and end. But it also calculates any
+ * dependent bidi direction for surrounding lines that change as a result
+ * of the bidi direction decisions within the range. The function is
+ * trying to do as little propagation as is needed.
+ */
+static void
+gtk_text_btree_resolve_bidi (GtkTextIter *start,
+ GtkTextIter *end)
+{
+ GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
+ GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
+ PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
+
+ /* Resolve the strong bidi direction for all lines between
+ * start and end.
+ */
+ start_line = _gtk_text_iter_get_text_line (start);
+ start_line_prev = _gtk_text_line_previous (start_line);
+ end_line = _gtk_text_iter_get_text_line (end);
+ end_line_next = _gtk_text_line_next (end_line);
+
+ line = start_line;
+ while (line && line != end_line_next)
+ {
+ /* Loop through the segments and search for a strong character
+ */
+ GtkTextLineSegment *seg = line->segments;
+ line->dir_strong = PANGO_DIRECTION_NEUTRAL;
+
+ while (seg)
+ {
+ if (seg->type == >k_text_char_type && seg->byte_count > 0)
+ {
+ PangoDirection pango_dir;
+
+ pango_dir = pango_find_base_dir (seg->body.chars,
+ seg->byte_count);
+
+ if (pango_dir != PANGO_DIRECTION_NEUTRAL)
+ {
+ line->dir_strong = pango_dir;
+ break;
+ }
+ }
+ seg = seg->next;
+ }
+
+ line = _gtk_text_line_next (line);
+ }
+
+ /* Sweep forward */
+
+ /* The variable dir_above_propagated contains the forward propagated
+ * direction before start. It is neutral if start is in the beginning
+ * of the buffer.
+ */
+ dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
+ if (start_line_prev)
+ dir_above_propagated = start_line_prev->dir_propagated_forward;
+
+ /* Loop forward and propagate the direction of each paragraph
+ * to all neutral lines.
+ */
+ line = start_line;
+ last_strong = dir_above_propagated;
+ while (line != end_line_next)
+ {
+ if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
+ last_strong = line->dir_strong;
+
+ line->dir_propagated_forward = last_strong;
+
+ line = _gtk_text_line_next (line);
+ }
+
+ /* Continue propagating as long as the previous resolved forward
+ * is different from last_strong.
+ */
+ {
+ GtkTextIter end_propagate;
+
+ while (line &&
+ line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
+ line->dir_propagated_forward != last_strong)
+ {
+ GtkTextLine *prev = line;
+ line->dir_propagated_forward = last_strong;
+
+ line = _gtk_text_line_next(line);
+ if (!line)
+ {
+ line = prev;
+ break;
+ }
+ }
+
+ /* The last line to invalidate is the last line before the
+ * line with the strong character. Or in case of the end of the
+ * buffer, the last line of the buffer. (There seems to be an
+ * extra "virtual" last line in the buffer that must not be used
+ * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
+ * _gtk_text_line_previous is ok in that case as well.)
+ */
+ line = _gtk_text_line_previous (line);
+ _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
+ _gtk_text_btree_invalidate_region (tree, end, &end_propagate);
+ }
+
+ /* Sweep backward */
+
+ /* The variable dir_below_propagated contains the backward propagated
+ * direction after end. It is neutral if end is at the end of
+ * the buffer.
+ */
+ dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
+ if (end_line_next)
+ dir_below_propagated = end_line_next->dir_propagated_back;
+
+ /* Loop backward and propagate the direction of each paragraph
+ * to all neutral lines.
+ */
+ line = end_line;
+ last_strong = dir_below_propagated;
+ while (line != start_line_prev)
+ {
+ if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
+ last_strong = line->dir_strong;
+
+ line->dir_propagated_back = last_strong;
+
+ line = _gtk_text_line_previous (line);
+ }
+
+ /* Continue propagating as long as the resolved backward dir
+ * is different from last_strong.
+ */
+ {
+ GtkTextIter start_propagate;
+
+ while (line &&
+ line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
+ line->dir_propagated_back != last_strong)
+ {
+ GtkTextLine *prev = line;
+ line->dir_propagated_back = last_strong;
+
+ line = _gtk_text_line_previous (line);
+ if (!line)
+ {
+ line = prev;
+ break;
+ }
+ }
+
+ /* We only need to invalidate for backwards propagation if the
+ * line we ended up on didn't get a direction from forwards
+ * propagation.
+ */
+ if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
+ {
+ _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0);
+ _gtk_text_btree_invalidate_region(tree, &start_propagate, start);
+ }
+ }
+}
+
void
_gtk_text_btree_delete (GtkTextIter *start,
- GtkTextIter *end)
+ GtkTextIter *end)
{
GtkTextLineSegment *prev_seg; /* The segment just before the start
* of the deletion range. */
GtkTextLineSegment *last_seg; /* The segment just after the end
* of the deletion range. */
- GtkTextLineSegment *seg, *next;
+ GtkTextLineSegment *seg, *next, *next2;
GtkTextLine *curline;
GtkTextBTreeNode *curnode, *node;
GtkTextBTree *tree;
GtkTextLine *start_line;
GtkTextLine *end_line;
+ GtkTextLine *line;
GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
gint start_byte_offset;
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
- * final newline of the file (just before the dummy line) is being
- * deleted, then back up index to just before the newline. If
- * there is a newline just before the first character being deleted,
- * then back up the first index too, so that an even number of lines
- * gets deleted. Furthermore, remove any tags that are present on
- * the newline that isn't going to be deleted after all (this simulates
- * deleting the newline and then adding a "clean" one back again).
- */
-
- gint line1;
- gint line2;
-
- line1 = gtk_text_iter_get_line (start);
- line2 = gtk_text_iter_get_line (end);
-
- if (line2 == _gtk_text_btree_line_count (tree))
- {
- GtkTextTag** tags;
- int array_size;
- GtkTextIter orig_end;
-
- orig_end = *end;
- gtk_text_iter_backward_char (end);
-
- --line2;
-
- if (gtk_text_iter_get_line_offset (start) == 0 &&
- line1 != 0)
- {
- gtk_text_iter_backward_char (start);
- --line1;
- }
-
- tags = _gtk_text_btree_get_tags (end,
- &array_size);
-
- if (tags != NULL)
- {
- int i;
-
- i = 0;
- while (i < array_size)
- {
- _gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
-
- ++i;
- }
-
- g_free (tags);
- }
- }
- }
-
/* Broadcast the need for redisplay before we break the iterators */
+ DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (tree, start, end);
/* Save the byte offset so we can reset the iterators */
seg->next = start_line->segments;
start_line->segments = seg;
}
- else
- {
+ else if (prev_seg->next &&
+ prev_seg->next != last_seg &&
+ seg->type == >k_text_toggle_off_type &&
+ prev_seg->next->type == >k_text_toggle_on_type &&
+ seg->body.toggle.info == prev_seg->next->body.toggle.info)
+ {
+ /* Try to match an off toggle with the matching on toggle
+ * if it immediately follows. This is a common case, and
+ * handling it here prevents quadratic blowup in
+ * cleanup_line() below. See bug 317125.
+ */
+ next2 = prev_seg->next->next;
+ g_free ((char *)prev_seg->next);
+ prev_seg->next = next2;
+ g_free ((char *)seg);
+ seg = NULL;
+ }
+ else
+ {
seg->next = prev_seg->next;
prev_seg->next = seg;
}
- if (seg->type->leftGravity)
+
+ if (seg && seg->type->leftGravity)
{
prev_seg = seg;
}
view = tree->views;
while (view)
{
- GtkTextLine *line;
GtkTextLineData *ld;
gint deleted_width = 0;
deleted_height += ld->height;
}
- if (!view->next)
- gtk_text_line_destroy (tree, line);
-
line = next_line;
}
if (deleted_width > 0 || deleted_height > 0)
{
ld = _gtk_text_line_get_data (start_line, view->view_id);
-
- /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
- * no way to add ld without also validating the node, which would
- * be improper at this point.
- */
- /* This assertion does actually fail sometimes, must
- fix before stable release -hp */
- g_assert (ld);
-
+
+ if (ld == NULL)
+ {
+ /* This means that start_line has never been validated.
+ * We don't really want to do the validation here but
+ * we do need to store our temporary sizes. So we
+ * create the line data and assume a line w/h of 0.
+ */
+ ld = _gtk_text_line_data_new (view->layout, start_line);
+ _gtk_text_line_add_data (start_line, ld);
+ ld->width = 0;
+ ld->height = 0;
+ ld->valid = FALSE;
+ }
+
ld->width = MAX (deleted_width, ld->width);
ld->height += deleted_height;
ld->valid = FALSE;
view = view->next;
}
+ line = deleted_lines;
+ while (line)
+ {
+ GtkTextLine *next_line = line->next;
+
+ gtk_text_line_destroy (tree, line);
+
+ line = next_line;
+ }
+
/* avoid dangling pointer */
deleted_lines = NULL;
/* Re-initialize our iterators */
_gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
*end = *start;
+
+ gtk_text_btree_resolve_bidi (start, end);
}
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
/* extract iterator info */
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;
above. FIXME */
gtk_text_iter_forward_chars (&end, char_count_delta);
+ DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (tree,
&start, &end);
/* Convenience for the user */
*iter = end;
+
+ gtk_text_btree_resolve_bidi (&start, &end);
}
}
*iter = start;
gtk_text_iter_forward_char (iter); /* skip forward past the segment */
+ DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (tree, &start, iter);
}
seg = _gtk_widget_segment_new (anchor);
tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
+ seg->body.child.line = _gtk_text_iter_get_text_line (iter);
insert_pixbuf_or_widget_segment (iter, seg);
}
void
-_gtk_text_btree_invalidate_region (GtkTextBTree *tree,
- const GtkTextIter *start,
- const GtkTextIter *end)
+_gtk_text_btree_invalidate_region (GtkTextBTree *tree,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
{
BTreeView *view;
iter_stack_new (void)
{
IterStack *stack;
- stack = g_new (IterStack, 1);
+ stack = g_slice_new (IterStack);
stack->iters = NULL;
stack->count = 0;
stack->alloced = 0;
}
static void
-iter_stack_push (IterStack *stack, const GtkTextIter *iter)
+iter_stack_push (IterStack *stack,
+ const GtkTextIter *iter)
{
stack->count += 1;
if (stack->count > stack->alloced)
{
stack->alloced = stack->count*2;
stack->iters = g_realloc (stack->iters,
- stack->alloced*sizeof (GtkTextIter));
+ stack->alloced * sizeof (GtkTextIter));
}
stack->iters[stack->count-1] = *iter;
}
static gboolean
-iter_stack_pop (IterStack *stack, GtkTextIter *iter)
+iter_stack_pop (IterStack *stack,
+ GtkTextIter *iter)
{
if (stack->count == 0)
return FALSE;
iter_stack_free (IterStack *stack)
{
g_free (stack->iters);
- g_free (stack);
+ g_slice_free (IterStack, stack);
}
static void
{
if (_gtk_text_tag_affects_size (tag))
{
+ DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (tree, start, end);
}
else if (_gtk_text_tag_affects_nonsize_appearance (tag))
g_assert (seg != NULL);
g_assert (indexable_seg != NULL);
g_assert (seg != indexable_seg);
-
+
if ( (seg->type == >k_text_toggle_on_type ||
seg->type == >k_text_toggle_off_type) &&
(seg->body.toggle.info == info) )
segments_changed (tree);
+ queue_tag_redisplay (tree, tag, &start, &end);
+
if (gtk_debug_flags & GTK_DEBUG_TEXT)
_gtk_text_btree_check (tree);
}
* "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;
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,
GtkTextLineSegment *seg;
int chars_left;
int chars_in_line;
- int bytes_in_line;
node = tree->root_node;
*/
chars_in_line = 0;
- bytes_in_line = 0;
seg = NULL;
for (line = node->children.line; line != NULL; line = line->next)
{
int src, dst, index;
TagInfo tagInfo;
GtkTextLine *line;
- GtkTextBTree *tree;
gint byte_index;
#define NUM_TAG_INFOS 10
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;
GtkTextLineSegment *seg;
GtkTextLineSegment *end_seg;
GString *retval;
- GtkTextBTree *tree;
gchar *str;
GtkTextIter iter;
GtkTextIter start;
gtk_text_iter_order (&start, &end);
- retval = g_string_new ("");
-
- tree = _gtk_text_iter_get_btree (&start);
+ retval = g_string_new (NULL);
end_seg = _gtk_text_iter_get_indexable_segment (&end);
iter = start;
{
gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
- int deftagCnts[LOTSA_TAGS];
+ int deftagCnts[LOTSA_TAGS] = { 0, };
int *tagCnts = deftagCnts;
GtkTextTag *deftags[LOTSA_TAGS];
GtkTextTag **tags = deftags;
/* almost always avoid malloc, so stay out of system calls */
if (LOTSA_TAGS < numTags)
{
- tagCnts = g_new (int, numTags);
+ tagCnts = g_new0 (int, numTags);
tags = g_new (GtkTextTag*, numTags);
}
- for (i=0; i<numTags; i++)
- {
- tagCnts[i] = 0;
- }
-
/*
* Record tag toggles within the line of indexPtr but preceding
* indexPtr.
end = iter;
gtk_text_iter_forward_char (&end);
+ DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (mark->body.mark.tree,
&iter, &end);
}
GtkTextLineSegment *mark,
GtkTextIter *iter)
{
- if (gtk_text_iter_get_line (iter) ==
- _gtk_text_btree_line_count (tree))
+ if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
gtk_text_iter_backward_char (iter);
}
void
_gtk_text_btree_place_cursor (GtkTextBTree *tree,
const GtkTextIter *iter)
+{
+ _gtk_text_btree_select_range (tree, iter, iter);
+}
+
+void
+_gtk_text_btree_select_range (GtkTextBTree *tree,
+ const GtkTextIter *ins,
+ const GtkTextIter *bound)
{
GtkTextIter start, end;
/* Move insert AND selection_bound before we redisplay */
real_set_mark (tree, tree->insert_mark,
- "insert", FALSE, iter, TRUE, FALSE);
+ "insert", FALSE, ins, TRUE, FALSE);
real_set_mark (tree, tree->selection_bound_mark,
- "selection_bound", FALSE, iter, TRUE, FALSE);
+ "selection_bound", FALSE, bound, TRUE, FALSE);
+
+ redisplay_region (tree, ins, bound);
}
+
void
_gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
const gchar *name)
/* 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));
+ g_object_unref (segment->body.mark.obj);
}
void
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);
}
}
{
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);
+ gint real_line;
+
+ /* n_lines is without the magic line at the end */
+ g_assert (_gtk_text_btree_line_count (tree) >= 1);
- tree->end_iter_line = _gtk_text_btree_get_line (tree, n_lines - 1, &real_line);
+ tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
tree->end_iter_line_stamp = tree->chars_changed_stamp;
}
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');
}
}
return NULL;
}
+
+GtkTextLineData*
+_gtk_text_line_data_new (GtkTextLayout *layout,
+ GtkTextLine *line)
+{
+ GtkTextLineData *line_data;
+
+ line_data = g_new (GtkTextLineData, 1);
+
+ line_data->view_id = layout;
+ line_data->next = NULL;
+ line_data->width = 0;
+ line_data->height = 0;
+ line_data->valid = FALSE;
+
+ return line_data;
+}
+
void
_gtk_text_line_add_data (GtkTextLine *line,
- GtkTextLineData *data)
+ GtkTextLineData *data)
{
g_return_if_fail (line != NULL);
g_return_if_fail (data != NULL);
while (offset >= seg->byte_count)
{
- g_assert (seg != NULL); /* means an invalid byte index */
offset -= seg->byte_count;
seg = seg->next;
+ g_assert (seg != NULL); /* means an invalid byte index */
}
if (seg_offset)
while (offset >= seg->char_count)
{
- g_assert (seg != NULL); /* means an invalid char index */
offset -= seg->char_count;
seg = seg->next;
+ g_assert (seg != NULL); /* means an invalid char index */
}
if (seg_offset)
while (offset > 0 && offset >= seg->byte_count)
{
- g_assert (seg != NULL); /* means an invalid byte index */
offset -= seg->byte_count;
seg = seg->next;
+ g_assert (seg != NULL); /* means an invalid byte index */
}
if (seg_offset)
while (offset > 0 && offset >= seg->char_count)
{
- g_assert (seg != NULL); /* means an invalid byte index */
offset -= seg->char_count;
seg = seg->next;
+ g_assert (seg != NULL); /* means an invalid byte index */
}
if (seg_offset)
while (byte_offset >= seg->byte_count) /* while (we need to go farther than
the next segment) */
{
- g_assert (seg != NULL); /* our byte_index was bogus if this happens */
-
byte_offset -= seg->byte_count;
char_offset += seg->char_count;
-
seg = seg->next;
+ g_assert (seg != NULL); /* our byte_index was bogus if this happens */
}
g_assert (seg != NULL);
gint *line_byte_offset)
{
GtkTextLineSegment *seg;
- GtkTextLineSegment *after_prev_indexable;
GtkTextLineSegment *after_last_indexable;
GtkTextLineSegment *last_indexable;
gint offset;
last_indexable = NULL;
after_last_indexable = line->segments;
- after_prev_indexable = line->segments;
seg = line->segments;
/* The loop ends when we're inside a segment;
offset -= seg->byte_count;
bytes_in_line += seg->byte_count;
last_indexable = seg;
- after_prev_indexable = after_last_indexable;
after_last_indexable = last_indexable->next;
}
gint *line_char_offset)
{
GtkTextLineSegment *seg;
- GtkTextLineSegment *after_prev_indexable;
GtkTextLineSegment *after_last_indexable;
GtkTextLineSegment *last_indexable;
gint offset;
last_indexable = NULL;
after_last_indexable = line->segments;
- after_prev_indexable = line->segments;
seg = line->segments;
/* The loop ends when we're inside a segment;
offset -= seg->char_count;
chars_in_line += seg->char_count;
last_indexable = seg;
- after_prev_indexable = after_last_indexable;
after_last_indexable = last_indexable->next;
}
if (seg->type == >k_text_char_type)
{
- *seg_byte_offset = 0;
- while (offset > 0)
- {
- gint bytes;
- const char * start = seg->body.chars + *seg_byte_offset;
+ const char *p;
- bytes = g_utf8_next_char (start) - start;
- *seg_byte_offset += bytes;
- offset -= 1;
- }
+ /* if in the last fourth of the segment walk backwards */
+ if (seg->char_count - offset < seg->char_count / 4)
+ p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
+ offset - seg->char_count);
+ else
+ p = g_utf8_offset_to_pointer (seg->body.chars, offset);
+
+ *seg_byte_offset = p - seg->body.chars;
g_assert (*seg_byte_offset < seg->byte_count);
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)
+ while (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)
+ if (line_ancestor_parent != NULL)
+ node = line_ancestor_parent->children.node;
+ else
+ node = line_ancestor;
+
+ while (node != line_ancestor && node != NULL)
{
child_nodes = g_slist_prepend (child_nodes, node);
/* 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. */
static void
summary_list_destroy (Summary *summary)
{
- Summary *next;
- while (summary != NULL)
- {
- next = summary->next;
- summary_destroy (summary);
- summary = next;
- }
+ g_slice_free_chain (Summary, summary, next);
}
static GtkTextLine*
GtkTextLine *line;
line = g_new0(GtkTextLine, 1);
+ line->dir_strong = PANGO_DIRECTION_NEUTRAL;
+ line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
+ line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
return line;
}
while (changed)
{
changed = FALSE;
- for (prev_p = &line->segments, seg = *prev_p;
- seg != NULL;
- prev_p = &(*prev_p)->next, seg = *prev_p)
+ prev_p = &line->segments;
+ for (seg = *prev_p; seg != NULL; seg = *prev_p)
{
if (seg->type->cleanupFunc != NULL)
{
*prev_p = (*seg->type->cleanupFunc)(seg, line);
if (seg != *prev_p)
- changed = TRUE;
+ {
+ changed = TRUE;
+ continue;
+ }
}
+
+ prev_p = &(*prev_p)->next;
}
}
}
{
NodeData *nd;
- nd = g_new (NodeData, 1);
+ nd = g_slice_new (NodeData);
nd->view_id = view_id;
nd->next = NULL;
static void
node_data_destroy (NodeData *nd)
{
- g_free (nd);
+ g_slice_free (NodeData, nd);
}
static void
node_data_list_destroy (NodeData *nd)
{
- NodeData *iter;
- NodeData *next;
-
- iter = nd;
- while (iter != NULL)
- {
- next = iter->next;
- node_data_destroy (iter);
- iter = next;
- }
+ g_slice_free_chain (NodeData, nd, next);
}
static NodeData*
-node_data_find (NodeData *nd, gpointer view_id)
+node_data_find (NodeData *nd,
+ gpointer view_id)
{
while (nd != NULL)
{
summary->info = (void*)0x1;
summary->toggle_count = 567;
summary->next = (void*)0x1;
- g_free (summary);
+ g_slice_free (Summary, summary);
}
static GtkTextBTreeNode*
{
/* didn't find a summary for our tag. */
g_return_if_fail (adjust > 0);
- summary = g_new (Summary, 1);
+ summary = g_slice_new (Summary);
summary->info = info;
summary->toggle_count = adjust;
summary->next = node->summary;
{
/* Must be a last toggle if there was a first one. */
_gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
+ DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
_gtk_text_btree_invalidate_region (tree,
&start, &end);
}
}
-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 */
{
/* didn't find it, create. */
- info = g_new (GtkTextTagInfo, 1);
+ info = g_slice_new (GtkTextTagInfo);
info->tag = tag;
- g_object_ref (G_OBJECT (tag));
+ g_object_ref (tag);
info->tag_root = NULL;
info->toggle_count = 0;
tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
+
+#if 0
+ g_print ("Created tag info %p for tag %s(%p)\n",
+ info, info->tag->name ? info->tag->name : "anon",
+ info->tag);
+#endif
}
return info;
info = list->data;
if (info->tag == tag)
{
+#if 0
+ g_print ("Removing tag info %p for tag %s(%p)\n",
+ info, info->tag->name ? info->tag->name : "anon",
+ info->tag);
+#endif
+
if (prev != NULL)
{
prev->next = list->next;
list->next = NULL;
g_slist_free (list);
- g_object_unref (G_OBJECT (info->tag));
+ g_object_unref (info->tag);
- g_free (info);
+ g_slice_free (GtkTextTagInfo, info);
return;
}
+ prev = list;
list = g_slist_next (list);
}
-
- g_assert_not_reached ();
- return;
}
static void
*/
GtkTextBTreeNode *rootnode = info->tag_root;
- summary = (Summary *) g_malloc (sizeof (Summary));
+ summary = g_slice_new (Summary);
summary->info = info;
summary->toggle_count = info->toggle_count - delta;
summary->next = rootnode->summary;
rootLevel = rootnode->level;
info->tag_root = rootnode;
}
- summary = (Summary *) g_malloc (sizeof (Summary));
+ summary = g_slice_new (Summary);
summary->info = info;
summary->toggle_count = delta;
summary->next = node->summary;
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)",
GtkTextLine *line;
GtkTextLineSegment *seg;
GtkTextTag *tag;
- GSList *taglist = NULL;
+ GSList *all_tags, *taglist = NULL;
int count;
GtkTextTagInfo *info;
/*
* Make sure that the tag toggle counts and the tag root pointers are OK.
*/
- for (taglist = list_of_tags (tree->table);
- taglist != NULL ; taglist = taglist->next)
+ all_tags = list_of_tags (tree->table);
+ for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
{
tag = taglist->data;
info = gtk_text_btree_get_existing_tag_info (tree, tag);
}
else
{
- GtkTextLineSegmentClass * last = NULL;
+ const GtkTextLineSegmentClass *last = NULL;
for (line = node->children.line ; line != NULL ;
line = line->next)
}
}
- g_slist_free (taglist);
- taglist = NULL;
+ g_slist_free (all_tags);
/*
* Call a recursive procedure to do the main body of checks.
}
}
-
+#define __GTK_TEXT_BTREE_C__
+#include "gtkaliasdef.c"