Blame gtksourceview/gtksourceregion.c

Packit a7d494
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
Packit a7d494
 * gtksourceregion.c - GtkTextMark-based region utility
Packit a7d494
 * This file is part of GtkSourceView
Packit a7d494
 *
Packit a7d494
 * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
Packit a7d494
 * Copyright (C) 2016 Sébastien Wilmet <swilmet@gnome.org>
Packit a7d494
 *
Packit a7d494
 * This library is free software; you can redistribute it and/or
Packit a7d494
 * modify it under the terms of the GNU Lesser General Public
Packit a7d494
 * License as published by the Free Software Foundation; either
Packit a7d494
 * version 2.1 of the License, or (at your option) any later version.
Packit a7d494
 *
Packit a7d494
 * This library is distributed in the hope that it will be useful,
Packit a7d494
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a7d494
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a7d494
 * Lesser General Public License for more details.
Packit a7d494
 *
Packit a7d494
 * You should have received a copy of the GNU Lesser General Public
Packit a7d494
 * License along with this library; if not, write to the Free Software
Packit a7d494
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit a7d494
 */
Packit a7d494
Packit a7d494
#ifdef HAVE_CONFIG_H
Packit a7d494
#include <config.h>
Packit a7d494
#endif
Packit a7d494
Packit a7d494
#include "gtksourceregion.h"
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * SECTION:region
Packit a7d494
 * @Short_description: Region utility
Packit a7d494
 * @Title: GtkSourceRegion
Packit a7d494
 * @See_also: #GtkTextBuffer
Packit a7d494
 *
Packit a7d494
 * A #GtkSourceRegion permits to store a group of subregions of a
Packit a7d494
 * #GtkTextBuffer. #GtkSourceRegion stores the subregions with pairs of
Packit a7d494
 * #GtkTextMark's, so the region is still valid after insertions and deletions
Packit a7d494
 * in the #GtkTextBuffer.
Packit a7d494
 *
Packit a7d494
 * The #GtkTextMark for the start of a subregion has a left gravity, while the
Packit a7d494
 * #GtkTextMark for the end of a subregion has a right gravity.
Packit a7d494
 *
Packit a7d494
 * The typical use-case of #GtkSourceRegion is to scan a #GtkTextBuffer chunk by
Packit a7d494
 * chunk, not the whole buffer at once to not block the user interface. The
Packit a7d494
 * #GtkSourceRegion represents in that case the remaining region to scan. You
Packit a7d494
 * can listen to the #GtkTextBuffer::insert-text and
Packit a7d494
 * #GtkTextBuffer::delete-range signals to update the #GtkSourceRegion
Packit a7d494
 * accordingly.
Packit a7d494
 *
Packit a7d494
 * To iterate through the subregions, you need to use a #GtkSourceRegionIter,
Packit a7d494
 * for example:
Packit a7d494
 * |[
Packit a7d494
 * GtkSourceRegion *region;
Packit a7d494
 * GtkSourceRegionIter region_iter;
Packit a7d494
 *
Packit a7d494
 * gtk_source_region_get_start_region_iter (region, &region_iter);
Packit a7d494
 *
Packit a7d494
 * while (!gtk_source_region_iter_is_end (&region_iter))
Packit a7d494
 * {
Packit a7d494
 *         GtkTextIter subregion_start;
Packit a7d494
 *         GtkTextIter subregion_end;
Packit a7d494
 *
Packit a7d494
 *         if (!gtk_source_region_iter_get_subregion (&region_iter,
Packit a7d494
 *                                                    &subregion_start,
Packit a7d494
 *                                                    &subregion_end))
Packit a7d494
 *         {
Packit a7d494
 *                 break;
Packit a7d494
 *         }
Packit a7d494
 *
Packit a7d494
 *         // Do something useful with the subregion.
Packit a7d494
 *
Packit a7d494
 *         gtk_source_region_iter_next (&region_iter);
Packit a7d494
 * }
Packit a7d494
 * ]|
Packit a7d494
 */
Packit a7d494
Packit a7d494
/* With the gravities of the GtkTextMarks, it is possible for subregions to
Packit a7d494
 * become interlaced:
Packit a7d494
 * Buffer content:
Packit a7d494
 *   "hello world"
Packit a7d494
 * Add two subregions:
Packit a7d494
 *   "[hello] [world]"
Packit a7d494
 * Delete the space:
Packit a7d494
 *   "[hello][world]"
Packit a7d494
 * Undo:
Packit a7d494
 *   "[hello[ ]world]"
Packit a7d494
 *
Packit a7d494
 * FIXME: when iterating through the subregions, it should simplify them first.
Packit a7d494
 * I don't know if it's done (swilmet).
Packit a7d494
 */
Packit a7d494
Packit a7d494
#undef ENABLE_DEBUG
Packit a7d494
/*
Packit a7d494
#define ENABLE_DEBUG
Packit a7d494
*/
Packit a7d494
Packit a7d494
#ifdef ENABLE_DEBUG
Packit a7d494
#define DEBUG(x) (x)
Packit a7d494
#else
Packit a7d494
#define DEBUG(x)
Packit a7d494
#endif
Packit a7d494
Packit a7d494
typedef struct _GtkSourceRegionPrivate GtkSourceRegionPrivate;
Packit a7d494
typedef struct _Subregion Subregion;
Packit a7d494
typedef struct _GtkSourceRegionIterReal GtkSourceRegionIterReal;
Packit a7d494
Packit a7d494
struct _GtkSourceRegionPrivate
Packit a7d494
{
Packit a7d494
	/* Weak pointer to the buffer. */
Packit a7d494
	GtkTextBuffer *buffer;
Packit a7d494
Packit a7d494
	/* List of sorted 'Subregion*' */
Packit a7d494
	GList *subregions;
Packit a7d494
Packit a7d494
	guint32 timestamp;
Packit a7d494
};
Packit a7d494
Packit a7d494
struct _Subregion
Packit a7d494
{
Packit a7d494
	GtkTextMark *start;
Packit a7d494
	GtkTextMark *end;
Packit a7d494
};
Packit a7d494
Packit a7d494
struct _GtkSourceRegionIterReal
Packit a7d494
{
Packit a7d494
	GtkSourceRegion *region;
Packit a7d494
	guint32 region_timestamp;
Packit a7d494
	GList *subregions;
Packit a7d494
};
Packit a7d494
Packit a7d494
enum
Packit a7d494
{
Packit a7d494
	PROP_0,
Packit a7d494
	PROP_BUFFER,
Packit a7d494
	N_PROPERTIES
Packit a7d494
};
Packit a7d494
Packit a7d494
static GParamSpec *properties[N_PROPERTIES];
Packit a7d494
Packit a7d494
G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceRegion, gtk_source_region, G_TYPE_OBJECT)
Packit a7d494
Packit a7d494
#ifdef ENABLE_DEBUG
Packit a7d494
static void
Packit a7d494
print_region (GtkSourceRegion *region)
Packit a7d494
{
Packit a7d494
	gchar *str;
Packit a7d494
Packit a7d494
	str = gtk_source_region_to_string (region);
Packit a7d494
	g_print ("%s\n", str);
Packit a7d494
	g_free (str);
Packit a7d494
}
Packit a7d494
#endif
Packit a7d494
Packit a7d494
/* Find and return a subregion node which contains the given text
Packit a7d494
 * iter.  If left_side is TRUE, return the subregion which contains
Packit a7d494
 * the text iter or which is the leftmost; else return the rightmost
Packit a7d494
 * subregion.
Packit a7d494
 */
Packit a7d494
static GList *
Packit a7d494
find_nearest_subregion (GtkSourceRegion   *region,
Packit a7d494
			const GtkTextIter *iter,
Packit a7d494
			GList             *begin,
Packit a7d494
			gboolean           leftmost,
Packit a7d494
			gboolean           include_edges)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv = gtk_source_region_get_instance_private (region);
Packit a7d494
	GList *retval;
Packit a7d494
	GList *l;
Packit a7d494
Packit a7d494
	g_assert (iter != NULL);
Packit a7d494
Packit a7d494
	if (begin == NULL)
Packit a7d494
	{
Packit a7d494
		begin = priv->subregions;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (begin != NULL)
Packit a7d494
	{
Packit a7d494
		retval = begin->prev;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		retval = NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	for (l = begin; l != NULL; l = l->next)
Packit a7d494
	{
Packit a7d494
		GtkTextIter sr_iter;
Packit a7d494
		Subregion *sr = l->data;
Packit a7d494
		gint cmp;
Packit a7d494
Packit a7d494
		if (!leftmost)
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_iter, sr->end);
Packit a7d494
			cmp = gtk_text_iter_compare (iter, &sr_iter);
Packit a7d494
			if (cmp < 0 || (cmp == 0 && include_edges))
Packit a7d494
			{
Packit a7d494
				retval = l;
Packit a7d494
				break;
Packit a7d494
			}
Packit a7d494
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_iter, sr->start);
Packit a7d494
			cmp = gtk_text_iter_compare (iter, &sr_iter);
Packit a7d494
			if (cmp > 0 || (cmp == 0 && include_edges))
Packit a7d494
			{
Packit a7d494
				retval = l;
Packit a7d494
			}
Packit a7d494
			else
Packit a7d494
			{
Packit a7d494
				break;
Packit a7d494
			}
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return retval;
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_region_get_property (GObject    *object,
Packit a7d494
				guint       prop_id,
Packit a7d494
				GValue     *value,
Packit a7d494
				GParamSpec *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceRegion *region = GTK_SOURCE_REGION (object);
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_BUFFER:
Packit a7d494
			g_value_set_object (value, gtk_source_region_get_buffer (region));
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
			break;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_region_set_property (GObject      *object,
Packit a7d494
				guint         prop_id,
Packit a7d494
				const GValue *value,
Packit a7d494
				GParamSpec   *pspec)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv = gtk_source_region_get_instance_private (GTK_SOURCE_REGION (object));
Packit a7d494
Packit a7d494
	switch (prop_id)
Packit a7d494
	{
Packit a7d494
		case PROP_BUFFER:
Packit a7d494
			g_assert (priv->buffer == NULL);
Packit a7d494
			priv->buffer = g_value_get_object (value);
Packit a7d494
			g_object_add_weak_pointer (G_OBJECT (priv->buffer),
Packit a7d494
						   (gpointer *) &priv->buffer);
Packit a7d494
			break;
Packit a7d494
Packit a7d494
		default:
Packit a7d494
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit a7d494
			break;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_region_dispose (GObject *object)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv = gtk_source_region_get_instance_private (GTK_SOURCE_REGION (object));
Packit a7d494
Packit a7d494
	while (priv->subregions != NULL)
Packit a7d494
	{
Packit a7d494
		Subregion *sr = priv->subregions->data;
Packit a7d494
Packit a7d494
		if (priv->buffer != NULL)
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->start);
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->end);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		g_slice_free (Subregion, sr);
Packit a7d494
		priv->subregions = g_list_delete_link (priv->subregions, priv->subregions);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (priv->buffer != NULL)
Packit a7d494
	{
Packit a7d494
		g_object_remove_weak_pointer (G_OBJECT (priv->buffer),
Packit a7d494
					      (gpointer *) &priv->buffer);
Packit a7d494
Packit a7d494
		priv->buffer = NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	G_OBJECT_CLASS (gtk_source_region_parent_class)->dispose (object);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_region_class_init (GtkSourceRegionClass *klass)
Packit a7d494
{
Packit a7d494
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit a7d494
Packit a7d494
	object_class->get_property = gtk_source_region_get_property;
Packit a7d494
	object_class->set_property = gtk_source_region_set_property;
Packit a7d494
	object_class->dispose = gtk_source_region_dispose;
Packit a7d494
Packit a7d494
	/**
Packit a7d494
	 * GtkSourceRegion:buffer:
Packit a7d494
	 *
Packit a7d494
	 * The #GtkTextBuffer. The #GtkSourceRegion has a weak reference to the
Packit a7d494
	 * buffer.
Packit a7d494
	 *
Packit a7d494
	 * Since: 3.22
Packit a7d494
	 */
Packit a7d494
	properties[PROP_BUFFER] =
Packit a7d494
		g_param_spec_object ("buffer",
Packit a7d494
				     "Buffer",
Packit a7d494
				     "",
Packit a7d494
				     GTK_TYPE_TEXT_BUFFER,
Packit a7d494
				     G_PARAM_READWRITE |
Packit a7d494
				     G_PARAM_CONSTRUCT_ONLY |
Packit a7d494
				     G_PARAM_STATIC_STRINGS);
Packit a7d494
Packit a7d494
	g_object_class_install_properties (object_class, N_PROPERTIES, properties);
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_region_init (GtkSourceRegion *region)
Packit a7d494
{
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_new:
Packit a7d494
 * @buffer: a #GtkTextBuffer.
Packit a7d494
 *
Packit a7d494
 * Returns: a new #GtkSourceRegion object for @buffer.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
GtkSourceRegion *
Packit a7d494
gtk_source_region_new (GtkTextBuffer *buffer)
Packit a7d494
{
Packit a7d494
	g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
Packit a7d494
Packit a7d494
	return g_object_new (GTK_SOURCE_TYPE_REGION,
Packit a7d494
			     "buffer", buffer,
Packit a7d494
			     NULL);
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_get_buffer:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 *
Packit a7d494
 * Returns: (transfer none) (nullable): the #GtkTextBuffer.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
GtkTextBuffer *
Packit a7d494
gtk_source_region_get_buffer (GtkSourceRegion *region)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_REGION (region), NULL);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
	return priv->buffer;
Packit a7d494
}
Packit a7d494
Packit a7d494
static void
Packit a7d494
gtk_source_region_clear_zero_length_subregions (GtkSourceRegion *region)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv = gtk_source_region_get_instance_private (region);
Packit a7d494
	GList *node;
Packit a7d494
Packit a7d494
	node = priv->subregions;
Packit a7d494
	while (node != NULL)
Packit a7d494
	{
Packit a7d494
		Subregion *sr = node->data;
Packit a7d494
		GtkTextIter start;
Packit a7d494
		GtkTextIter end;
Packit a7d494
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &start, sr->start);
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &end, sr->end);
Packit a7d494
Packit a7d494
		if (gtk_text_iter_equal (&start, &end))
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->start);
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->end);
Packit a7d494
			g_slice_free (Subregion, sr);
Packit a7d494
Packit a7d494
			if (node == priv->subregions)
Packit a7d494
			{
Packit a7d494
				priv->subregions = node = g_list_delete_link (node, node);
Packit a7d494
			}
Packit a7d494
			else
Packit a7d494
			{
Packit a7d494
				node = g_list_delete_link (node, node);
Packit a7d494
			}
Packit a7d494
Packit a7d494
			priv->timestamp++;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			node = node->next;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_add_subregion:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @_start: the start of the subregion.
Packit a7d494
 * @_end: the end of the subregion.
Packit a7d494
 *
Packit a7d494
 * Adds the subregion delimited by @_start and @_end to @region.
Packit a7d494
 *
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
gtk_source_region_add_subregion (GtkSourceRegion   *region,
Packit a7d494
				 const GtkTextIter *_start,
Packit a7d494
				 const GtkTextIter *_end)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
	GList *start_node;
Packit a7d494
	GList *end_node;
Packit a7d494
	GtkTextIter start;
Packit a7d494
	GtkTextIter end;
Packit a7d494
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_REGION (region));
Packit a7d494
	g_return_if_fail (_start != NULL);
Packit a7d494
	g_return_if_fail (_end != NULL);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
Packit a7d494
	if (priv->buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	start = *_start;
Packit a7d494
	end = *_end;
Packit a7d494
Packit a7d494
	DEBUG (g_print ("---\n"));
Packit a7d494
	DEBUG (print_region (region));
Packit a7d494
	DEBUG (g_message ("region_add (%d, %d)",
Packit a7d494
			  gtk_text_iter_get_offset (&start),
Packit a7d494
			  gtk_text_iter_get_offset (&end)));
Packit a7d494
Packit a7d494
	gtk_text_iter_order (&start, &end;;
Packit a7d494
Packit a7d494
	/* Don't add zero-length regions. */
Packit a7d494
	if (gtk_text_iter_equal (&start, &end))
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Find bounding subregions. */
Packit a7d494
	start_node = find_nearest_subregion (region, &start, NULL, FALSE, TRUE);
Packit a7d494
	end_node = find_nearest_subregion (region, &end, start_node, TRUE, TRUE);
Packit a7d494
Packit a7d494
	if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
Packit a7d494
	{
Packit a7d494
		/* Create the new subregion. */
Packit a7d494
		Subregion *sr = g_slice_new0 (Subregion);
Packit a7d494
		sr->start = gtk_text_buffer_create_mark (priv->buffer, NULL, &start, TRUE);
Packit a7d494
		sr->end = gtk_text_buffer_create_mark (priv->buffer, NULL, &end, FALSE);
Packit a7d494
Packit a7d494
		if (start_node == NULL)
Packit a7d494
		{
Packit a7d494
			/* Append the new region. */
Packit a7d494
			priv->subregions = g_list_append (priv->subregions, sr);
Packit a7d494
		}
Packit a7d494
		else if (end_node == NULL)
Packit a7d494
		{
Packit a7d494
			/* Prepend the new region. */
Packit a7d494
			priv->subregions = g_list_prepend (priv->subregions, sr);
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			/* We are in the middle of two subregions. */
Packit a7d494
			priv->subregions = g_list_insert_before (priv->subregions, start_node, sr);
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		GtkTextIter iter;
Packit a7d494
		Subregion *sr = start_node->data;
Packit a7d494
Packit a7d494
		if (start_node != end_node)
Packit a7d494
		{
Packit a7d494
			/* We need to merge some subregions. */
Packit a7d494
			GList *l = start_node->next;
Packit a7d494
			Subregion *q;
Packit a7d494
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->end);
Packit a7d494
Packit a7d494
			while (l != end_node)
Packit a7d494
			{
Packit a7d494
				q = l->data;
Packit a7d494
				gtk_text_buffer_delete_mark (priv->buffer, q->start);
Packit a7d494
				gtk_text_buffer_delete_mark (priv->buffer, q->end);
Packit a7d494
				g_slice_free (Subregion, q);
Packit a7d494
				l = g_list_delete_link (l, l);
Packit a7d494
			}
Packit a7d494
Packit a7d494
			q = l->data;
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, q->start);
Packit a7d494
			sr->end = q->end;
Packit a7d494
			g_slice_free (Subregion, q);
Packit a7d494
			l = g_list_delete_link (l, l);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* Now move marks if that action expands the region. */
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &iter, sr->start);
Packit a7d494
		if (gtk_text_iter_compare (&iter, &start) > 0)
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_move_mark (priv->buffer, sr->start, &start;;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &iter, sr->end);
Packit a7d494
		if (gtk_text_iter_compare (&iter, &end) < 0)
Packit a7d494
		{
Packit a7d494
			gtk_text_buffer_move_mark (priv->buffer, sr->end, &end;;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	priv->timestamp++;
Packit a7d494
Packit a7d494
	DEBUG (print_region (region));
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_add_region:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @region_to_add: (nullable): the #GtkSourceRegion to add to @region, or %NULL.
Packit a7d494
 *
Packit a7d494
 * Adds @region_to_add to @region. @region_to_add is not modified.
Packit a7d494
 *
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
gtk_source_region_add_region (GtkSourceRegion *region,
Packit a7d494
			      GtkSourceRegion *region_to_add)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionIter iter;
Packit a7d494
	GtkTextBuffer *region_buffer;
Packit a7d494
	GtkTextBuffer *region_to_add_buffer;
Packit a7d494
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_REGION (region));
Packit a7d494
	g_return_if_fail (region_to_add == NULL || GTK_SOURCE_IS_REGION (region_to_add));
Packit a7d494
Packit a7d494
	if (region_to_add == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	region_buffer = gtk_source_region_get_buffer (region);
Packit a7d494
	region_to_add_buffer = gtk_source_region_get_buffer (region_to_add);
Packit a7d494
	g_return_if_fail (region_buffer == region_to_add_buffer);
Packit a7d494
Packit a7d494
	if (region_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_source_region_get_start_region_iter (region_to_add, &iter);
Packit a7d494
Packit a7d494
	while (!gtk_source_region_iter_is_end (&iter))
Packit a7d494
	{
Packit a7d494
		GtkTextIter subregion_start;
Packit a7d494
		GtkTextIter subregion_end;
Packit a7d494
Packit a7d494
		if (!gtk_source_region_iter_get_subregion (&iter,
Packit a7d494
							   &subregion_start,
Packit a7d494
							   &subregion_end))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_source_region_add_subregion (region,
Packit a7d494
						 &subregion_start,
Packit a7d494
						 &subregion_end);
Packit a7d494
Packit a7d494
		gtk_source_region_iter_next (&iter);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_subtract_subregion:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @_start: the start of the subregion.
Packit a7d494
 * @_end: the end of the subregion.
Packit a7d494
 *
Packit a7d494
 * Subtracts the subregion delimited by @_start and @_end from @region.
Packit a7d494
 *
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
gtk_source_region_subtract_subregion (GtkSourceRegion   *region,
Packit a7d494
				      const GtkTextIter *_start,
Packit a7d494
				      const GtkTextIter *_end)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
	GList *start_node;
Packit a7d494
	GList *end_node;
Packit a7d494
	GList *node;
Packit a7d494
	GtkTextIter sr_start_iter;
Packit a7d494
	GtkTextIter sr_end_iter;
Packit a7d494
	gboolean done;
Packit a7d494
	gboolean start_is_outside;
Packit a7d494
	gboolean end_is_outside;
Packit a7d494
	Subregion *sr;
Packit a7d494
	GtkTextIter start;
Packit a7d494
	GtkTextIter end;
Packit a7d494
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_REGION (region));
Packit a7d494
	g_return_if_fail (_start != NULL);
Packit a7d494
	g_return_if_fail (_end != NULL);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
Packit a7d494
	if (priv->buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	start = *_start;
Packit a7d494
	end = *_end;
Packit a7d494
Packit a7d494
	DEBUG (g_print ("---\n"));
Packit a7d494
	DEBUG (print_region (region));
Packit a7d494
	DEBUG (g_message ("region_substract (%d, %d)",
Packit a7d494
			  gtk_text_iter_get_offset (&start),
Packit a7d494
			  gtk_text_iter_get_offset (&end)));
Packit a7d494
Packit a7d494
	gtk_text_iter_order (&start, &end;;
Packit a7d494
Packit a7d494
	/* Find bounding subregions. */
Packit a7d494
	start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE);
Packit a7d494
	end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE);
Packit a7d494
Packit a7d494
	/* Easy case first. */
Packit a7d494
	if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Deal with the start point. */
Packit a7d494
	start_is_outside = end_is_outside = FALSE;
Packit a7d494
Packit a7d494
	sr = start_node->data;
Packit a7d494
	gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_start_iter, sr->start);
Packit a7d494
	gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_end_iter, sr->end);
Packit a7d494
Packit a7d494
	if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter) &&
Packit a7d494
	    !gtk_text_iter_equal (&start, &sr_start_iter))
Packit a7d494
	{
Packit a7d494
		/* The starting point is inside the first subregion. */
Packit a7d494
		if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) &&
Packit a7d494
		    !gtk_text_iter_equal (&end, &sr_end_iter))
Packit a7d494
		{
Packit a7d494
			/* The ending point is also inside the first
Packit a7d494
			 * subregion: we need to split.
Packit a7d494
			 */
Packit a7d494
			Subregion *new_sr = g_slice_new0 (Subregion);
Packit a7d494
			new_sr->end = sr->end;
Packit a7d494
			new_sr->start = gtk_text_buffer_create_mark (priv->buffer,
Packit a7d494
								     NULL,
Packit a7d494
								     &end,
Packit a7d494
								     TRUE);
Packit a7d494
Packit a7d494
			start_node = g_list_insert_before (start_node, start_node->next, new_sr);
Packit a7d494
Packit a7d494
			sr->end = gtk_text_buffer_create_mark (priv->buffer,
Packit a7d494
							       NULL,
Packit a7d494
							       &start,
Packit a7d494
							       FALSE);
Packit a7d494
Packit a7d494
			/* No further processing needed. */
Packit a7d494
			DEBUG (g_message ("subregion splitted"));
Packit a7d494
Packit a7d494
			return;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			/* The ending point is outside, so just move
Packit a7d494
			 * the end of the subregion to the starting point.
Packit a7d494
			 */
Packit a7d494
			gtk_text_buffer_move_mark (priv->buffer, sr->end, &start;;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		/* The starting point is outside (and so to the left)
Packit a7d494
		 * of the first subregion.
Packit a7d494
		 */
Packit a7d494
		DEBUG (g_message ("start is outside"));
Packit a7d494
Packit a7d494
		start_is_outside = TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Deal with the end point. */
Packit a7d494
	if (start_node != end_node)
Packit a7d494
	{
Packit a7d494
		sr = end_node->data;
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_start_iter, sr->start);
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_end_iter, sr->end);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) &&
Packit a7d494
	    !gtk_text_iter_equal (&end, &sr_end_iter))
Packit a7d494
	{
Packit a7d494
		/* Ending point is inside, move the start mark. */
Packit a7d494
		gtk_text_buffer_move_mark (priv->buffer, sr->start, &end;;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		end_is_outside = TRUE;
Packit a7d494
		DEBUG (g_message ("end is outside"));
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Finally remove any intermediate subregions. */
Packit a7d494
	done = FALSE;
Packit a7d494
	node = start_node;
Packit a7d494
Packit a7d494
	while (!done)
Packit a7d494
	{
Packit a7d494
		if (node == end_node)
Packit a7d494
		{
Packit a7d494
			/* We are done, exit in the next iteration. */
Packit a7d494
			done = TRUE;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if ((node == start_node && !start_is_outside) ||
Packit a7d494
		    (node == end_node && !end_is_outside))
Packit a7d494
		{
Packit a7d494
			/* Skip starting or ending node. */
Packit a7d494
			node = node->next;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			GList *l = node->next;
Packit a7d494
			sr = node->data;
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->start);
Packit a7d494
			gtk_text_buffer_delete_mark (priv->buffer, sr->end);
Packit a7d494
			g_slice_free (Subregion, sr);
Packit a7d494
			priv->subregions = g_list_delete_link (priv->subregions, node);
Packit a7d494
			node = l;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	priv->timestamp++;
Packit a7d494
Packit a7d494
	DEBUG (print_region (region));
Packit a7d494
Packit a7d494
	/* Now get rid of empty subregions. */
Packit a7d494
	gtk_source_region_clear_zero_length_subregions (region);
Packit a7d494
Packit a7d494
	DEBUG (print_region (region));
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_subtract_region:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @region_to_subtract: (nullable): the #GtkSourceRegion to subtract from
Packit a7d494
 *   @region, or %NULL.
Packit a7d494
 *
Packit a7d494
 * Subtracts @region_to_subtract from @region. @region_to_subtract is not
Packit a7d494
 * modified.
Packit a7d494
 *
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
gtk_source_region_subtract_region (GtkSourceRegion *region,
Packit a7d494
				   GtkSourceRegion *region_to_subtract)
Packit a7d494
{
Packit a7d494
	GtkTextBuffer *region_buffer;
Packit a7d494
	GtkTextBuffer *region_to_subtract_buffer;
Packit a7d494
	GtkSourceRegionIter iter;
Packit a7d494
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_REGION (region));
Packit a7d494
	g_return_if_fail (region_to_subtract == NULL || GTK_SOURCE_IS_REGION (region_to_subtract));
Packit a7d494
Packit a7d494
	region_buffer = gtk_source_region_get_buffer (region);
Packit a7d494
	region_to_subtract_buffer = gtk_source_region_get_buffer (region_to_subtract);
Packit a7d494
	g_return_if_fail (region_buffer == region_to_subtract_buffer);
Packit a7d494
Packit a7d494
	if (region_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_source_region_get_start_region_iter (region_to_subtract, &iter);
Packit a7d494
Packit a7d494
	while (!gtk_source_region_iter_is_end (&iter))
Packit a7d494
	{
Packit a7d494
		GtkTextIter subregion_start;
Packit a7d494
		GtkTextIter subregion_end;
Packit a7d494
Packit a7d494
		if (!gtk_source_region_iter_get_subregion (&iter,
Packit a7d494
							   &subregion_start,
Packit a7d494
							   &subregion_end))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_source_region_subtract_subregion (region,
Packit a7d494
						      &subregion_start,
Packit a7d494
						      &subregion_end);
Packit a7d494
Packit a7d494
		gtk_source_region_iter_next (&iter);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_is_empty:
Packit a7d494
 * @region: (nullable): a #GtkSourceRegion, or %NULL.
Packit a7d494
 *
Packit a7d494
 * Returns whether the @region is empty. A %NULL @region is considered empty.
Packit a7d494
 *
Packit a7d494
 * Returns: whether the @region is empty.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
gtk_source_region_is_empty (GtkSourceRegion *region)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionIter region_iter;
Packit a7d494
Packit a7d494
	if (region == NULL)
Packit a7d494
	{
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* A #GtkSourceRegion can contain empty subregions. So checking the
Packit a7d494
	 * number of subregions is not sufficient.
Packit a7d494
	 * When calling gtk_source_region_add_subregion() with equal iters, the
Packit a7d494
	 * subregion is not added. But when a subregion becomes empty, due to
Packit a7d494
	 * text deletion, the subregion is not removed from the
Packit a7d494
	 * #GtkSourceRegion.
Packit a7d494
	 */
Packit a7d494
Packit a7d494
	gtk_source_region_get_start_region_iter (region, &region_iter);
Packit a7d494
Packit a7d494
	while (!gtk_source_region_iter_is_end (&region_iter))
Packit a7d494
	{
Packit a7d494
		GtkTextIter subregion_start;
Packit a7d494
		GtkTextIter subregion_end;
Packit a7d494
Packit a7d494
		if (!gtk_source_region_iter_get_subregion (&region_iter,
Packit a7d494
							   &subregion_start,
Packit a7d494
							   &subregion_end))
Packit a7d494
		{
Packit a7d494
			return TRUE;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (!gtk_text_iter_equal (&subregion_start, &subregion_end))
Packit a7d494
		{
Packit a7d494
			return FALSE;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_source_region_iter_next (&region_iter);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_get_bounds:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @start: (out) (optional): iterator to initialize with the start of @region,
Packit a7d494
 *   or %NULL.
Packit a7d494
 * @end: (out) (optional): iterator to initialize with the end of @region,
Packit a7d494
 *   or %NULL.
Packit a7d494
 *
Packit a7d494
 * Gets the @start and @end bounds of the @region.
Packit a7d494
 *
Packit a7d494
 * Returns: %TRUE if @start and @end have been set successfully (if non-%NULL),
Packit a7d494
 *   or %FALSE if the @region is empty.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
gtk_source_region_get_bounds (GtkSourceRegion *region,
Packit a7d494
			      GtkTextIter     *start,
Packit a7d494
			      GtkTextIter     *end)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_REGION (region), FALSE);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
Packit a7d494
	if (priv->buffer == NULL ||
Packit a7d494
	    gtk_source_region_is_empty (region))
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	g_assert (priv->subregions != NULL);
Packit a7d494
Packit a7d494
	if (start != NULL)
Packit a7d494
	{
Packit a7d494
		Subregion *first_subregion = priv->subregions->data;
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, start, first_subregion->start);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (end != NULL)
Packit a7d494
	{
Packit a7d494
		Subregion *last_subregion = g_list_last (priv->subregions)->data;
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, end, last_subregion->end);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_intersect_subregion:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @_start: the start of the subregion.
Packit a7d494
 * @_end: the end of the subregion.
Packit a7d494
 *
Packit a7d494
 * Returns the intersection between @region and the subregion delimited by
Packit a7d494
 * @_start and @_end. @region is not modified.
Packit a7d494
 *
Packit a7d494
 * Returns: (transfer full) (nullable): the intersection as a new
Packit a7d494
 *   #GtkSourceRegion.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
GtkSourceRegion *
Packit a7d494
gtk_source_region_intersect_subregion (GtkSourceRegion   *region,
Packit a7d494
				       const GtkTextIter *_start,
Packit a7d494
				       const GtkTextIter *_end)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
	GtkSourceRegion *new_region;
Packit a7d494
	GtkSourceRegionPrivate *new_priv;
Packit a7d494
	GList *start_node;
Packit a7d494
	GList *end_node;
Packit a7d494
	GList *node;
Packit a7d494
	GtkTextIter sr_start_iter;
Packit a7d494
	GtkTextIter sr_end_iter;
Packit a7d494
	Subregion *sr;
Packit a7d494
	Subregion *new_sr;
Packit a7d494
	gboolean done;
Packit a7d494
	GtkTextIter start;
Packit a7d494
	GtkTextIter end;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_REGION (region), NULL);
Packit a7d494
	g_return_val_if_fail (_start != NULL, NULL);
Packit a7d494
	g_return_val_if_fail (_end != NULL, NULL);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
Packit a7d494
	if (priv->buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	start = *_start;
Packit a7d494
	end = *_end;
Packit a7d494
Packit a7d494
	gtk_text_iter_order (&start, &end;;
Packit a7d494
Packit a7d494
	/* Find bounding subregions. */
Packit a7d494
	start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE);
Packit a7d494
	end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE);
Packit a7d494
Packit a7d494
	/* Easy case first. */
Packit a7d494
	if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
Packit a7d494
	{
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	new_region = gtk_source_region_new (priv->buffer);
Packit a7d494
	new_priv = gtk_source_region_get_instance_private (new_region);
Packit a7d494
	done = FALSE;
Packit a7d494
Packit a7d494
	sr = start_node->data;
Packit a7d494
	gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_start_iter, sr->start);
Packit a7d494
	gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_end_iter, sr->end);
Packit a7d494
Packit a7d494
	/* Starting node. */
Packit a7d494
	if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter))
Packit a7d494
	{
Packit a7d494
		new_sr = g_slice_new0 (Subregion);
Packit a7d494
		new_priv->subregions = g_list_prepend (new_priv->subregions, new_sr);
Packit a7d494
Packit a7d494
		new_sr->start = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
							     NULL,
Packit a7d494
							     &start,
Packit a7d494
							     TRUE);
Packit a7d494
Packit a7d494
		if (start_node == end_node)
Packit a7d494
		{
Packit a7d494
			/* Things will finish shortly. */
Packit a7d494
			done = TRUE;
Packit a7d494
			if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter))
Packit a7d494
			{
Packit a7d494
				new_sr->end = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
									   NULL,
Packit a7d494
									   &end,
Packit a7d494
									   FALSE);
Packit a7d494
			}
Packit a7d494
			else
Packit a7d494
			{
Packit a7d494
				new_sr->end = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
									   NULL,
Packit a7d494
									   &sr_end_iter,
Packit a7d494
									   FALSE);
Packit a7d494
			}
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			new_sr->end = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
								   NULL,
Packit a7d494
								   &sr_end_iter,
Packit a7d494
								   FALSE);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		node = start_node->next;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		/* start should be the same as the subregion, so copy it in the
Packit a7d494
		 * loop.
Packit a7d494
		 */
Packit a7d494
		node = start_node;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (!done)
Packit a7d494
	{
Packit a7d494
		while (node != end_node)
Packit a7d494
		{
Packit a7d494
			/* Copy intermediate subregions verbatim. */
Packit a7d494
			sr = node->data;
Packit a7d494
			gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_start_iter, sr->start);
Packit a7d494
			gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_end_iter, sr->end);
Packit a7d494
Packit a7d494
			new_sr = g_slice_new0 (Subregion);
Packit a7d494
			new_priv->subregions = g_list_prepend (new_priv->subregions, new_sr);
Packit a7d494
Packit a7d494
			new_sr->start = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
								     NULL,
Packit a7d494
								     &sr_start_iter,
Packit a7d494
								     TRUE);
Packit a7d494
Packit a7d494
			new_sr->end = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
								   NULL,
Packit a7d494
								   &sr_end_iter,
Packit a7d494
								   FALSE);
Packit a7d494
Packit a7d494
			/* Next node. */
Packit a7d494
			node = node->next;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		/* Ending node. */
Packit a7d494
		sr = node->data;
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_start_iter, sr->start);
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &sr_end_iter, sr->end);
Packit a7d494
Packit a7d494
		new_sr = g_slice_new0 (Subregion);
Packit a7d494
		new_priv->subregions = g_list_prepend (new_priv->subregions, new_sr);
Packit a7d494
Packit a7d494
		new_sr->start = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
							     NULL,
Packit a7d494
							     &sr_start_iter,
Packit a7d494
							     TRUE);
Packit a7d494
Packit a7d494
		if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter))
Packit a7d494
		{
Packit a7d494
			new_sr->end = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
								   NULL,
Packit a7d494
								   &end,
Packit a7d494
								   FALSE);
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			new_sr->end = gtk_text_buffer_create_mark (new_priv->buffer,
Packit a7d494
								   NULL,
Packit a7d494
								   &sr_end_iter,
Packit a7d494
								   FALSE);
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	new_priv->subregions = g_list_reverse (new_priv->subregions);
Packit a7d494
	return new_region;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_intersect_region:
Packit a7d494
 * @region1: (nullable): a #GtkSourceRegion, or %NULL.
Packit a7d494
 * @region2: (nullable): a #GtkSourceRegion, or %NULL.
Packit a7d494
 *
Packit a7d494
 * Returns the intersection between @region1 and @region2. @region1 and
Packit a7d494
 * @region2 are not modified.
Packit a7d494
 *
Packit a7d494
 * Returns: (transfer full) (nullable): the intersection as a #GtkSourceRegion
Packit a7d494
 *   object.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
GtkSourceRegion *
Packit a7d494
gtk_source_region_intersect_region (GtkSourceRegion *region1,
Packit a7d494
				    GtkSourceRegion *region2)
Packit a7d494
{
Packit a7d494
	GtkTextBuffer *region1_buffer;
Packit a7d494
	GtkTextBuffer *region2_buffer;
Packit a7d494
	GtkSourceRegion *full_intersect = NULL;
Packit a7d494
	GtkSourceRegionIter region2_iter;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (region1 == NULL || GTK_SOURCE_IS_REGION (region1), NULL);
Packit a7d494
	g_return_val_if_fail (region2 == NULL || GTK_SOURCE_IS_REGION (region2), NULL);
Packit a7d494
Packit a7d494
	if (region1 == NULL && region2 == NULL)
Packit a7d494
	{
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
	if (region1 == NULL)
Packit a7d494
	{
Packit a7d494
		return g_object_ref (region2);
Packit a7d494
	}
Packit a7d494
	if (region2 == NULL)
Packit a7d494
	{
Packit a7d494
		return g_object_ref (region1);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	region1_buffer = gtk_source_region_get_buffer (region1);
Packit a7d494
	region2_buffer = gtk_source_region_get_buffer (region2);
Packit a7d494
	g_return_val_if_fail (region1_buffer == region2_buffer, NULL);
Packit a7d494
Packit a7d494
	if (region1_buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	gtk_source_region_get_start_region_iter (region2, &region2_iter);
Packit a7d494
Packit a7d494
	while (!gtk_source_region_iter_is_end (&region2_iter))
Packit a7d494
	{
Packit a7d494
		GtkTextIter subregion2_start;
Packit a7d494
		GtkTextIter subregion2_end;
Packit a7d494
		GtkSourceRegion *sub_intersect;
Packit a7d494
Packit a7d494
		if (!gtk_source_region_iter_get_subregion (&region2_iter,
Packit a7d494
							   &subregion2_start,
Packit a7d494
							   &subregion2_end))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		sub_intersect = gtk_source_region_intersect_subregion (region1,
Packit a7d494
								       &subregion2_start,
Packit a7d494
								       &subregion2_end);
Packit a7d494
Packit a7d494
		if (full_intersect == NULL)
Packit a7d494
		{
Packit a7d494
			full_intersect = sub_intersect;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			gtk_source_region_add_region (full_intersect, sub_intersect);
Packit a7d494
			g_clear_object (&sub_intersect);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_source_region_iter_next (&region2_iter);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return full_intersect;
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
check_iterator (GtkSourceRegionIterReal *real)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
Packit a7d494
	if (real->region == NULL)
Packit a7d494
	{
Packit a7d494
		goto invalid;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (real->region);
Packit a7d494
Packit a7d494
	if (real->region_timestamp == priv->timestamp)
Packit a7d494
	{
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
invalid:
Packit a7d494
	g_warning ("Invalid GtkSourceRegionIter: either the iterator is "
Packit a7d494
		   "uninitialized, or the region has been modified since the "
Packit a7d494
		   "iterator was created.");
Packit a7d494
Packit a7d494
	return FALSE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_get_start_region_iter:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 * @iter: (out): iterator to initialize to the first subregion.
Packit a7d494
 *
Packit a7d494
 * Initializes a #GtkSourceRegionIter to the first subregion of @region. If
Packit a7d494
 * @region is empty, @iter will be initialized to the end iterator.
Packit a7d494
 *
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
gtk_source_region_get_start_region_iter (GtkSourceRegion     *region,
Packit a7d494
					 GtkSourceRegionIter *iter)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
	GtkSourceRegionIterReal *real;
Packit a7d494
Packit a7d494
	g_return_if_fail (GTK_SOURCE_IS_REGION (region));
Packit a7d494
	g_return_if_fail (iter != NULL);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
	real = (GtkSourceRegionIterReal *)iter;
Packit a7d494
Packit a7d494
	/* priv->subregions may be NULL, -> end iter */
Packit a7d494
Packit a7d494
	real->region = region;
Packit a7d494
	real->subregions = priv->subregions;
Packit a7d494
	real->region_timestamp = priv->timestamp;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_iter_is_end:
Packit a7d494
 * @iter: a #GtkSourceRegionIter.
Packit a7d494
 *
Packit a7d494
 * Returns: whether @iter is the end iterator.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
gtk_source_region_iter_is_end (GtkSourceRegionIter *iter)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionIterReal *real;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (iter != NULL, FALSE);
Packit a7d494
Packit a7d494
	real = (GtkSourceRegionIterReal *)iter;
Packit a7d494
	g_return_val_if_fail (check_iterator (real), FALSE);
Packit a7d494
Packit a7d494
	return real->subregions == NULL;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_iter_next:
Packit a7d494
 * @iter: a #GtkSourceRegionIter.
Packit a7d494
 *
Packit a7d494
 * Moves @iter to the next subregion.
Packit a7d494
 *
Packit a7d494
 * Returns: %TRUE if @iter moved and is dereferenceable, or %FALSE if @iter has
Packit a7d494
 *   been set to the end iterator.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
gtk_source_region_iter_next (GtkSourceRegionIter *iter)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionIterReal *real;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (iter != NULL, FALSE);
Packit a7d494
Packit a7d494
	real = (GtkSourceRegionIterReal *)iter;
Packit a7d494
	g_return_val_if_fail (check_iterator (real), FALSE);
Packit a7d494
Packit a7d494
	if (real->subregions != NULL)
Packit a7d494
	{
Packit a7d494
		real->subregions = real->subregions->next;
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return FALSE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_iter_get_subregion:
Packit a7d494
 * @iter: a #GtkSourceRegionIter.
Packit a7d494
 * @start: (out) (optional): iterator to initialize with the subregion start, or %NULL.
Packit a7d494
 * @end: (out) (optional): iterator to initialize with the subregion end, or %NULL.
Packit a7d494
 *
Packit a7d494
 * Gets the subregion at this iterator.
Packit a7d494
 *
Packit a7d494
 * Returns: %TRUE if @start and @end have been set successfully (if non-%NULL),
Packit a7d494
 *   or %FALSE if @iter is the end iterator or if the region is empty.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
gtk_source_region_iter_get_subregion (GtkSourceRegionIter *iter,
Packit a7d494
				      GtkTextIter         *start,
Packit a7d494
				      GtkTextIter         *end)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionIterReal *real;
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
	Subregion *sr;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (iter != NULL, FALSE);
Packit a7d494
Packit a7d494
	real = (GtkSourceRegionIterReal *)iter;
Packit a7d494
	g_return_val_if_fail (check_iterator (real), FALSE);
Packit a7d494
Packit a7d494
	if (real->subregions == NULL)
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (real->region);
Packit a7d494
Packit a7d494
	if (priv->buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	sr = real->subregions->data;
Packit a7d494
	g_return_val_if_fail (sr != NULL, FALSE);
Packit a7d494
Packit a7d494
	if (start != NULL)
Packit a7d494
	{
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, start, sr->start);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (end != NULL)
Packit a7d494
	{
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, end, sr->end);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return TRUE;
Packit a7d494
}
Packit a7d494
Packit a7d494
/**
Packit a7d494
 * gtk_source_region_to_string:
Packit a7d494
 * @region: a #GtkSourceRegion.
Packit a7d494
 *
Packit a7d494
 * Gets a string represention of @region, for debugging purposes.
Packit a7d494
 *
Packit a7d494
 * The returned string contains the character offsets of the subregions. It
Packit a7d494
 * doesn't include a newline character at the end of the string.
Packit a7d494
 *
Packit a7d494
 * Returns: (transfer full) (nullable): a string represention of @region. Free
Packit a7d494
 *   with g_free() when no longer needed.
Packit a7d494
 * Since: 3.22
Packit a7d494
 */
Packit a7d494
gchar *
Packit a7d494
gtk_source_region_to_string (GtkSourceRegion *region)
Packit a7d494
{
Packit a7d494
	GtkSourceRegionPrivate *priv;
Packit a7d494
	GString *string;
Packit a7d494
	GList *l;
Packit a7d494
Packit a7d494
	g_return_val_if_fail (GTK_SOURCE_IS_REGION (region), NULL);
Packit a7d494
Packit a7d494
	priv = gtk_source_region_get_instance_private (region);
Packit a7d494
Packit a7d494
	if (priv->buffer == NULL)
Packit a7d494
	{
Packit a7d494
		return NULL;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	string = g_string_new ("Subregions:");
Packit a7d494
Packit a7d494
	for (l = priv->subregions; l != NULL; l = l->next)
Packit a7d494
	{
Packit a7d494
		Subregion *sr = l->data;
Packit a7d494
		GtkTextIter start;
Packit a7d494
		GtkTextIter end;
Packit a7d494
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &start, sr->start);
Packit a7d494
		gtk_text_buffer_get_iter_at_mark (priv->buffer, &end, sr->end);
Packit a7d494
Packit a7d494
		g_string_append_printf (string,
Packit a7d494
					" %d-%d",
Packit a7d494
					gtk_text_iter_get_offset (&start),
Packit a7d494
					gtk_text_iter_get_offset (&end));
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return g_string_free (string, FALSE);
Packit a7d494
}