+ gtk_text_buffer_get_iter_at_mark (r->buffer,
+ r->range_start,
+ r->start_mark);
+
+ gtk_text_buffer_get_iter_at_mark (r->buffer,
+ r->range_end,
+ r->end_mark);
+
+ gtk_text_buffer_get_iter_at_mark (r->buffer,
+ r->whole_end,
+ r->whole_end_mark);
+
+ gtk_text_buffer_delete_mark (r->buffer, r->start_mark);
+ gtk_text_buffer_delete_mark (r->buffer, r->end_mark);
+ gtk_text_buffer_delete_mark (r->buffer, r->whole_end_mark);
+
+ /* Due to the gravities on the marks, the ordering could have
+ * gotten mangled; we switch to an empty range in that
+ * case
+ */
+
+ if (gtk_text_iter_compare (r->range_start, r->range_end) > 0)
+ *r->range_start = *r->range_end;
+
+ if (gtk_text_iter_compare (r->range_end, r->whole_end) > 0)
+ *r->range_end = *r->whole_end;
+
+ g_object_unref (r->buffer);
+ g_free (r);
+}
+
+static void
+insert_range_untagged (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const GtkTextIter *orig_start,
+ const GtkTextIter *orig_end,
+ gboolean interactive)
+{
+ GtkTextIter range_start;
+ GtkTextIter range_end;
+ GtkTextIter start, end;
+ Range *r;
+
+ if (gtk_text_iter_equal (orig_start, orig_end))
+ return;
+
+ start = *orig_start;
+ end = *orig_end;
+
+ range_start = start;
+ range_end = start;
+
+ while (TRUE)
+ {
+ if (gtk_text_iter_equal (&range_start, &range_end))
+ {
+ /* Figure out how to move forward */
+
+ g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
+
+ if (gtk_text_iter_equal (&range_end, &end))
+ {
+ /* nothing left to do */
+ break;
+ }
+ else if (gtk_text_iter_get_char (&range_end) == GTK_TEXT_UNKNOWN_CHAR)
+ {
+ GdkPixbuf *pixbuf = NULL;
+ GtkTextChildAnchor *anchor = NULL;
+ pixbuf = gtk_text_iter_get_pixbuf (&range_end);
+ anchor = gtk_text_iter_get_child_anchor (&range_end);
+
+ if (pixbuf)
+ {
+ r = save_range (&range_start,
+ &range_end,
+ &end);
+
+ gtk_text_buffer_insert_pixbuf (buffer,
+ iter,
+ pixbuf);
+
+ restore_range (r);
+ r = NULL;
+
+ gtk_text_iter_forward_char (&range_end);
+
+ range_start = range_end;
+ }
+ else if (anchor)
+ {
+ /* Just skip anchors */
+
+ gtk_text_iter_forward_char (&range_end);
+ range_start = range_end;
+ }
+ else
+ {
+ /* The GTK_TEXT_UNKNOWN_CHAR was in a text segment, so
+ * keep going.
+ */
+ gtk_text_iter_forward_find_char (&range_end,
+ possibly_not_text, NULL,
+ &end);
+
+ g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
+ }
+ }
+ else
+ {
+ /* Text segment starts here, so forward search to
+ * find its possible endpoint
+ */
+ gtk_text_iter_forward_find_char (&range_end,
+ possibly_not_text, NULL,
+ &end);
+
+ g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
+ }
+ }
+ else
+ {
+ r = save_range (&range_start,
+ &range_end,
+ &end);
+
+ insert_text_range (buffer,
+ iter,
+ &range_start,
+ &range_end,
+ interactive);
+
+ restore_range (r);
+ r = NULL;
+
+ range_start = range_end;
+ }
+ }
+}
+
+static void
+insert_range_not_inside_self (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const GtkTextIter *orig_start,
+ const GtkTextIter *orig_end,
+ gboolean interactive)
+{
+ /* Find each range of uniformly-tagged text, insert it,
+ * then apply the tags.
+ */
+ GtkTextIter start = *orig_start;
+ GtkTextIter end = *orig_end;
+ GtkTextIter range_start;
+ GtkTextIter range_end;
+
+ if (gtk_text_iter_equal (orig_start, orig_end))
+ return;
+
+ gtk_text_iter_order (&start, &end);
+
+ range_start = start;
+ range_end = start;
+
+ while (TRUE)
+ {
+ gint start_offset;
+ GtkTextIter start_iter;
+ GSList *tags;
+ GSList *tmp_list;
+ Range *r;
+
+ if (gtk_text_iter_equal (&range_start, &end))
+ break; /* All done */
+
+ g_assert (gtk_text_iter_compare (&range_start, &end) < 0);
+
+ gtk_text_iter_forward_to_tag_toggle (&range_end, NULL);
+
+ g_assert (!gtk_text_iter_equal (&range_start, &range_end));
+
+ /* Clamp to the end iterator */
+ if (gtk_text_iter_compare (&range_end, &end) > 0)
+ range_end = end;
+
+ /* We have a range with unique tags; insert it, and
+ * apply all tags.
+ */
+ start_offset = gtk_text_iter_get_offset (iter);
+
+ r = save_range (&range_start, &range_end, &end);
+
+ insert_range_untagged (buffer, iter, &range_start, &range_end, interactive);
+
+ restore_range (r);
+ r = NULL;
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
+
+ tags = gtk_text_iter_get_tags (&range_start);
+ tmp_list = tags;
+ while (tmp_list != NULL)
+ {
+ gtk_text_buffer_apply_tag (buffer,
+ tmp_list->data,
+ &start_iter,
+ iter);
+
+ tmp_list = g_slist_next (tmp_list);
+ }
+ g_slist_free (tags);
+
+ range_start = range_end;
+ }
+}
+
+static void
+gtk_text_buffer_real_insert_range (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const GtkTextIter *orig_start,
+ const GtkTextIter *orig_end,
+ gboolean interactive)
+{
+ GtkTextBuffer *src_buffer;
+
+ /* Find each range of uniformly-tagged text, insert it,
+ * then apply the tags.
+ */
+ if (gtk_text_iter_equal (orig_start, orig_end))
+ return;
+
+ if (interactive)
+ gtk_text_buffer_begin_user_action (buffer);
+
+ src_buffer = gtk_text_iter_get_buffer (orig_start);
+
+ if (gtk_text_iter_get_buffer (iter) != src_buffer ||
+ !gtk_text_iter_in_range (iter, orig_start, orig_end))
+ {
+ insert_range_not_inside_self (buffer, iter, orig_start, orig_end, interactive);
+ }
+ else
+ {
+ /* If you insert a range into itself, it could loop infinitely
+ * because the region being copied keeps growing as we insert. So
+ * we have to separately copy the range before and after
+ * the insertion point.
+ */
+ GtkTextIter start = *orig_start;
+ GtkTextIter end = *orig_end;
+ GtkTextIter range_start;
+ GtkTextIter range_end;
+ Range *first_half;
+ Range *second_half;
+
+ gtk_text_iter_order (&start, &end);
+
+ range_start = start;
+ range_end = *iter;
+ first_half = save_range (&range_start, &range_end, &end);
+
+ range_start = *iter;
+ range_end = end;
+ second_half = save_range (&range_start, &range_end, &end);
+
+ restore_range (first_half);
+ insert_range_not_inside_self (buffer, iter, &range_start, &range_end, interactive);
+
+ restore_range (second_half);
+ insert_range_not_inside_self (buffer, iter, &range_start, &range_end, interactive);
+ }
+
+ if (interactive)
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+/**
+ * gtk_text_buffer_insert_range:
+ * @buffer: a #GtkTextBuffer
+ * @iter: a position in @buffer
+ * @start: a position in a #GtkTextBuffer
+ * @end: another position in the same buffer as @start
+ *
+ * Copies text, tags, and pixbufs between @start and @end (the order
+ * of @start and @end doesn't matter) and inserts the copy at @iter.
+ * Used instead of simply getting/inserting text because it preserves
+ * images and tags. If @start and @end are in a different buffer from
+ * @buffer, the two buffers must share the same tag table.
+ *
+ * Implemented via emissions of the insert_text and apply_tag signals,
+ * so expect those.
+ **/
+void
+gtk_text_buffer_insert_range (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (start != NULL);
+ g_return_if_fail (end != NULL);
+ g_return_if_fail (gtk_text_iter_get_buffer (start) ==
+ gtk_text_iter_get_buffer (end));
+ g_return_if_fail (gtk_text_iter_get_buffer (start)->priv->tag_table ==
+ buffer->priv->tag_table);
+ g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
+
+ gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE);
+}
+
+/**
+ * gtk_text_buffer_insert_range_interactive:
+ * @buffer: a #GtkTextBuffer
+ * @iter: a position in @buffer
+ * @start: a position in a #GtkTextBuffer
+ * @end: another position in the same buffer as @start
+ * @default_editable: default editability of the buffer
+ *
+ * Same as gtk_text_buffer_insert_range(), but does nothing if the
+ * insertion point isn't editable. The @default_editable parameter
+ * indicates whether the text is editable at @iter if no tags
+ * enclosing @iter affect editability. Typically the result of
+ * gtk_text_view_get_editable() is appropriate here.
+ *
+ * Returns: whether an insertion was possible at @iter
+ **/
+gboolean
+gtk_text_buffer_insert_range_interactive (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ gboolean default_editable)
+{
+ g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+ g_return_val_if_fail (gtk_text_iter_get_buffer (start) ==
+ gtk_text_iter_get_buffer (end), FALSE);
+ g_return_val_if_fail (gtk_text_iter_get_buffer (start)->priv->tag_table ==
+ buffer->priv->tag_table, FALSE);
+
+ if (gtk_text_iter_can_insert (iter, default_editable))
+ {
+ gtk_text_buffer_real_insert_range (buffer, iter, start, end, TRUE);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * gtk_text_buffer_insert_with_tags:
+ * @buffer: a #GtkTextBuffer
+ * @iter: an iterator in @buffer
+ * @text: UTF-8 text
+ * @len: length of @text, or -1
+ * @first_tag: first tag to apply to @text
+ * @...: %NULL-terminated list of tags to apply
+ *
+ * Inserts @text into @buffer at @iter, applying the list of tags to
+ * the newly-inserted text. The last tag specified must be %NULL to
+ * terminate the list. Equivalent to calling gtk_text_buffer_insert(),
+ * then gtk_text_buffer_apply_tag() on the inserted text;
+ * gtk_text_buffer_insert_with_tags() is just a convenience function.
+ **/
+void
+gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len,
+ GtkTextTag *first_tag,
+ ...)
+{
+ gint start_offset;
+ GtkTextIter start;