*/
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <config.h>
#include "gtktextbtree.h"
#include <string.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 */
};
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);
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;
if (tree->refcount == 0)
{
- g_signal_handler_disconnect (G_OBJECT (tree->table),
+ g_signal_handler_disconnect (tree->table,
tree->tag_changed_handler);
- g_object_unref (G_OBJECT (tree->table));
+ g_object_unref (tree->table);
tree->table = NULL;
gtk_text_btree_node_destroy (tree, tree->root_node);
tree->child_anchor_table = NULL;
}
- g_object_unref (G_OBJECT (tree->insert_mark));
+ g_object_unref (tree->insert_mark);
tree->insert_mark = NULL;
- g_object_unref (G_OBJECT (tree->selection_bound_mark));
+ 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->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)
/* 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
/* Convenience for the user */
*iter = end;
+
+ gtk_text_btree_resolve_bidi (&start, &end);
}
}
gtk_text_iter_order (&start, &end);
- retval = g_string_new ("");
+ retval = g_string_new (NULL);
tree = _gtk_text_iter_get_btree (&start);
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
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. */
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;
}
info = g_new (GtkTextTagInfo, 1);
info->tag = tag;
- g_object_ref (G_OBJECT (tag));
+ g_object_ref (tag);
info->tag_root = NULL;
info->toggle_count = 0;
list->next = NULL;
g_slist_free (list);
- g_object_unref (G_OBJECT (info->tag));
+ g_object_unref (info->tag);
g_free (info);
return;
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);
}
}
- 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"