static void
recalculate_size (GtkSourceGutterRendererLines *renderer)
{
	gint num_lines;
	gint num_digits = 0;
	GtkTextBuffer *buffer;

	buffer = get_buffer (renderer);

	num_lines = gtk_text_buffer_get_line_count (buffer);

	num_digits = count_num_digits (num_lines);

	if (num_digits != renderer->priv->num_line_digits)
	{
		gchar markup[24];
		gint size;

		renderer->priv->num_line_digits = num_digits;

		num_lines = MAX (num_lines, 99);

		g_snprintf (markup, sizeof markup, "<b>%d</b>", num_lines);
		gtk_source_gutter_renderer_text_measure_markup (GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer),
		                                                markup,
		                                                &size,
		                                                NULL);

		gtk_source_gutter_renderer_set_size (GTK_SOURCE_GUTTER_RENDERER (renderer),
		                                     size);
	}
}
static void
gutter_renderer_text_begin (GtkSourceGutterRenderer *renderer,
			    cairo_t                 *cr,
			    GdkRectangle            *background_area,
			    GdkRectangle            *cell_area,
			    GtkTextIter             *start,
			    GtkTextIter             *end)
{
	GtkSourceGutterRendererText *text = GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer);
	GtkTextView *view;

	view = gtk_source_gutter_renderer_get_view (renderer);

	g_clear_object (&text->priv->cached_layout);
	text->priv->cached_layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), NULL);

	if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->begin != NULL)
	{
		GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->begin (renderer,
													cr,
													background_area,
													cell_area,
													start,
													end);
	}
}
static void
gutter_renderer_query_data (GtkSourceGutterRenderer      *renderer,
                            GtkTextIter                  *start,
                            GtkTextIter                  *end,
                            GtkSourceGutterRendererState  state)
{
	GtkSourceGutterRendererLines *lines = GTK_SOURCE_GUTTER_RENDERER_LINES (renderer);
	gchar text[24];
	gint line;
	gint len;
	gboolean current_line;

	line = gtk_text_iter_get_line (start) + 1;

	current_line = (state & GTK_SOURCE_GUTTER_RENDERER_STATE_CURSOR) &&
	               lines->priv->cursor_visible;

	if (current_line)
	{
		len = g_snprintf (text, sizeof text, "<b>%d</b>", line);
	}
	else
	{
		len = g_snprintf (text, sizeof text, "%d", line);
	}

	gtk_source_gutter_renderer_text_set_markup (GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer),
	                                            text,
	                                            len);
}
static void
gtk_source_gutter_renderer_text_finalize (GObject *object)
{
	GtkSourceGutterRendererText *renderer = GTK_SOURCE_GUTTER_RENDERER_TEXT (object);

	g_free (renderer->priv->text);

	G_OBJECT_CLASS (gtk_source_gutter_renderer_text_parent_class)->finalize (object);
}
static void
gutter_renderer_text_end (GtkSourceGutterRenderer *renderer)
{
	GtkSourceGutterRendererText *text = GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer);

	g_clear_object (&text->priv->cached_layout);

	if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->end != NULL)
	{
		GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->end (renderer);
	}
}
static void
gutter_renderer_text_begin (GtkSourceGutterRenderer      *renderer,
                            cairo_t                      *cr,
                            GdkRectangle                 *background_area,
                            GdkRectangle                 *cell_area,
                            GtkTextIter                  *start,
                            GtkTextIter                  *end)
{
	GtkSourceGutterRendererText *text = GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer);

	create_layout (text, GTK_WIDGET (gtk_source_gutter_renderer_get_view (renderer)));

	if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->begin != NULL)
	{
		GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->begin (renderer,
													cr,
													background_area,
													cell_area,
													start,
													end);
	}
}
static void
gtk_source_gutter_renderer_text_get_property (GObject    *object,
                                              guint       prop_id,
                                              GValue     *value,
                                              GParamSpec *pspec)
{
	GtkSourceGutterRendererText *renderer;

	renderer = GTK_SOURCE_GUTTER_RENDERER_TEXT (object);

	switch (prop_id)
	{
		case PROP_MARKUP:
			g_value_set_string (value, renderer->priv->is_markup ? renderer->priv->text : NULL);
			break;
		case PROP_TEXT:
			g_value_set_string (value, !renderer->priv->is_markup ? renderer->priv->text : NULL);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}
static void
gtk_source_gutter_renderer_text_set_property (GObject      *object,
                                              guint         prop_id,
                                              const GValue *value,
                                              GParamSpec   *pspec)
{
	GtkSourceGutterRendererText *renderer;

	renderer = GTK_SOURCE_GUTTER_RENDERER_TEXT (object);

	switch (prop_id)
	{
		case PROP_MARKUP:
			set_text (renderer, g_value_get_string (value), -1, TRUE);
			break;
		case PROP_TEXT:
			set_text (renderer, g_value_get_string (value), -1, FALSE);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
			break;
	}
}
static void
gutter_renderer_text_draw (GtkSourceGutterRenderer      *renderer,
			   cairo_t                      *cr,
			   GdkRectangle                 *background_area,
			   GdkRectangle                 *cell_area,
			   GtkTextIter                  *start,
			   GtkTextIter                  *end,
			   GtkSourceGutterRendererState  state)
{
	GtkSourceGutterRendererText *text = GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer);
	GtkTextView *view;
	gint width;
	gint height;
	gfloat xalign;
	gfloat yalign;
	GtkSourceGutterRendererAlignmentMode mode;
	gint x = 0;
	gint y = 0;
	GtkStyleContext *context;

	/* Chain up to draw background */
	if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->draw != NULL)
	{
		GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->draw (renderer,
												       cr,
												       background_area,
												       cell_area,
												       start,
												       end,
												       state);
	}

	view = gtk_source_gutter_renderer_get_view (renderer);

	if (text->priv->is_markup)
	{
		pango_layout_set_markup (text->priv->cached_layout,
		                         text->priv->text,
		                         -1);
	}
	else
	{
		pango_layout_set_text (text->priv->cached_layout,
		                       text->priv->text,
		                       -1);
	}

	pango_layout_get_pixel_size (text->priv->cached_layout, &width, &height);

	gtk_source_gutter_renderer_get_alignment (renderer,
	                                          &xalign,
	                                          &yalign);

	/* Avoid calculations if we don't wrap text */
	if (gtk_text_view_get_wrap_mode (view) == GTK_WRAP_NONE)
	{
		mode = GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_CELL;
	}
	else
	{
		mode = gtk_source_gutter_renderer_get_alignment_mode (renderer);
	}

	switch (mode)
	{
		case GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_CELL:
			x = cell_area->x + (cell_area->width - width) * xalign;
			y = cell_area->y + (cell_area->height - height) * yalign;
			break;

		case GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST:
			center_on (view,
			           cell_area,
			           start,
			           width,
			           height,
			           xalign,
			           yalign,
			           &x,
			           &y);
			break;

		case GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_LAST:
			center_on (view,
			           cell_area,
			           end,
			           width,
			           height,
			           xalign,
			           yalign,
			           &x,
			           &y);
			break;

		default:
			g_assert_not_reached ();
	}

	context = gtk_widget_get_style_context (GTK_WIDGET (view));
	gtk_render_layout (context, cr, x, y, text->priv->cached_layout);
}
static void
gutter_renderer_text_draw (GtkSourceGutterRenderer      *renderer,
                           cairo_t                      *cr,
                           GdkRectangle                 *background_area,
                           GdkRectangle                 *cell_area,
                           GtkTextIter                  *start,
                           GtkTextIter                  *end,
                           GtkSourceGutterRendererState  state)
{
	GtkSourceGutterRendererText *text = GTK_SOURCE_GUTTER_RENDERER_TEXT (renderer);
	gint width;
	gint height;
	PangoAttrList *attr_list;
	gfloat xalign;
	gfloat yalign;
	GtkSourceGutterRendererAlignmentMode mode;
	GtkTextView *view;
	gint x = 0;
	gint y = 0;
	GtkStyleContext *context;

	/* Chain up to draw background */
	if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->draw != NULL)
	{
		GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_text_parent_class)->draw (renderer,
												       cr,
												       background_area,
												       cell_area,
												       start,
												       end,
												       state);
	}

	view = gtk_source_gutter_renderer_get_view (renderer);

	if (text->priv->is_markup)
	{
		pango_layout_set_markup (text->priv->cached_layout,
		                         text->priv->text,
		                         -1);
	}
	else
	{
		pango_layout_set_text (text->priv->cached_layout,
		                       text->priv->text,
		                       -1);
	}

	attr_list = pango_layout_get_attributes (text->priv->cached_layout);

	if (!attr_list)
	{
		pango_layout_set_attributes (text->priv->cached_layout,
		                             pango_attr_list_copy (text->priv->cached_attr_list));
	}
	else
	{
		pango_attr_list_insert (attr_list,
		                        pango_attribute_copy (text->priv->fg_attr));
	}

	pango_layout_get_pixel_size (text->priv->cached_layout, &width, &height);

	gtk_source_gutter_renderer_get_alignment (renderer,
	                                          &xalign,
	                                          &yalign);

	mode = gtk_source_gutter_renderer_get_alignment_mode (renderer);

	switch (mode)
	{
		case GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_CELL:
			x = cell_area->x + (cell_area->width - width) * xalign;
			y = cell_area->y + (cell_area->height - height) * yalign;
		break;
		case GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST:
			center_on (renderer,
			           cell_area,
			           start,
			           width,
			           height,
			           xalign,
			           yalign,
			           &x,
			           &y);
		break;
		case GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_LAST:
			center_on (renderer,
			           cell_area,
			           end,
			           width,
			           height,
			           xalign,
			           yalign,
			           &x,
			           &y);
		break;
	}

	context = gtk_widget_get_style_context (GTK_WIDGET (view));
	gtk_render_layout (context, cr, x, y, text->priv->cached_layout);
}