gtksourceview3/gtksourceview/gtksourcegutterrenderermarks.c

418 lines
11 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
* gtksourcegutterrenderermarks.c
* This file is part of GtkSourceView
*
* Copyright (C) 2010 - Jesse van den Kieboom
*
* GtkSourceView is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GtkSourceView is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "gtksourcegutterrenderermarks.h"
#include "gtksourceview.h"
#include "gtksourcebuffer.h"
#include "gtksourcemarkattributes.h"
#include "gtksourcemark.h"
#define COMPOSITE_ALPHA 225
G_DEFINE_TYPE (GtkSourceGutterRendererMarks, gtk_source_gutter_renderer_marks, GTK_SOURCE_TYPE_GUTTER_RENDERER_PIXBUF)
static gint
sort_marks_by_priority (gconstpointer m1,
gconstpointer m2,
gpointer data)
{
GtkSourceMark *mark1 = GTK_SOURCE_MARK (m1);
GtkSourceMark *mark2 = GTK_SOURCE_MARK (m2);
GtkSourceView *view = GTK_SOURCE_VIEW (data);
GtkTextIter iter1, iter2;
gint line1;
gint line2;
gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark1)),
&iter1,
GTK_TEXT_MARK (mark1));
gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark2)),
&iter2,
GTK_TEXT_MARK (mark2));
line1 = gtk_text_iter_get_line (&iter1);
line2 = gtk_text_iter_get_line (&iter2);
if (line1 == line2)
{
gint priority1 = -1;
gint priority2 = -1;
gtk_source_view_get_mark_attributes (view,
gtk_source_mark_get_category (mark1),
&priority1);
gtk_source_view_get_mark_attributes (view,
gtk_source_mark_get_category (mark2),
&priority2);
return priority1 - priority2;
}
else
{
return line2 - line1;
}
}
static int
measure_line_height (GtkSourceView *view)
{
PangoLayout *layout;
gint height = 12;
layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), "QWERTY");
if (layout)
{
pango_layout_get_pixel_size (layout, NULL, &height);
g_object_unref (layout);
}
return height - 2;
}
static GdkPixbuf *
composite_marks (GtkSourceView *view,
GSList *marks,
gint size)
{
GdkPixbuf *composite;
gint mark_width;
gint mark_height;
/* Draw the mark with higher priority */
marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
composite = NULL;
mark_width = 0;
mark_height = 0;
/* composite all the pixbufs for the marks present at the line */
do
{
GtkSourceMark *mark;
GtkSourceMarkAttributes *attrs;
const GdkPixbuf *pixbuf;
mark = marks->data;
attrs = gtk_source_view_get_mark_attributes (view,
gtk_source_mark_get_category (mark),
NULL);
if (attrs == NULL)
{
continue;
}
pixbuf = gtk_source_mark_attributes_render_icon (attrs,
GTK_WIDGET (view),
size);
if (pixbuf != NULL)
{
if (composite == NULL)
{
composite = gdk_pixbuf_copy (pixbuf);
mark_width = gdk_pixbuf_get_width (composite);
mark_height = gdk_pixbuf_get_height (composite);
}
else
{
gint pixbuf_w;
gint pixbuf_h;
pixbuf_w = gdk_pixbuf_get_width (pixbuf);
pixbuf_h = gdk_pixbuf_get_height (pixbuf);
gdk_pixbuf_composite (pixbuf,
composite,
0, 0,
mark_width, mark_height,
0, 0,
(gdouble) pixbuf_w / mark_width,
(gdouble) pixbuf_h / mark_height,
GDK_INTERP_BILINEAR,
COMPOSITE_ALPHA);
}
}
marks = g_slist_next (marks);
}
while (marks);
return composite;
}
static void
gutter_renderer_query_data (GtkSourceGutterRenderer *renderer,
GtkTextIter *start,
GtkTextIter *end,
GtkSourceGutterRendererState state)
{
GSList *marks;
GdkPixbuf *pixbuf = NULL;
gint size = 0;
GtkSourceView *view;
GtkSourceBuffer *buffer;
view = GTK_SOURCE_VIEW (gtk_source_gutter_renderer_get_view (renderer));
buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
marks = gtk_source_buffer_get_source_marks_at_iter (buffer,
start,
NULL);
if (marks != NULL)
{
size = measure_line_height (view);
pixbuf = composite_marks (view, marks, size);
g_slist_free (marks);
}
g_object_set (G_OBJECT (renderer),
"pixbuf", pixbuf,
"xpad", 2,
"yalign", 0.5,
"xalign", 0.5,
"alignment-mode", GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST,
NULL);
g_clear_object (&pixbuf);
}
static gboolean
set_tooltip_widget_from_marks (GtkSourceView *view,
GtkTooltip *tooltip,
GSList *marks)
{
GtkGrid *grid = NULL;
gint row_num = 0;
gint icon_size;
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size);
for (; marks; marks = g_slist_next (marks))
{
const gchar *category;
GtkSourceMark *mark;
GtkSourceMarkAttributes *attrs;
gchar *text;
gboolean ismarkup = FALSE;
GtkWidget *label;
const GdkPixbuf *pixbuf;
mark = marks->data;
category = gtk_source_mark_get_category (mark);
attrs = gtk_source_view_get_mark_attributes (view, category, NULL);
if (attrs == NULL)
{
continue;
}
text = gtk_source_mark_attributes_get_tooltip_markup (attrs, mark);
if (text == NULL)
{
text = gtk_source_mark_attributes_get_tooltip_text (attrs, mark);
}
else
{
ismarkup = TRUE;
}
if (text == NULL)
{
continue;
}
if (grid == NULL)
{
grid = GTK_GRID (gtk_grid_new ());
gtk_grid_set_column_spacing (grid, 4);
gtk_widget_show (GTK_WIDGET (grid));
}
label = gtk_label_new (NULL);
if (ismarkup)
{
gtk_label_set_markup (GTK_LABEL (label), text);
}
else
{
gtk_label_set_text (GTK_LABEL (label), text);
}
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_widget_show (label);
pixbuf = gtk_source_mark_attributes_render_icon (attrs,
GTK_WIDGET (view),
icon_size);
if (pixbuf == NULL)
{
gtk_grid_attach (grid, label, 0, row_num, 2, 1);
}
else
{
GtkWidget *image;
GdkPixbuf *copy;
/* FIXME why a copy is needed? */
copy = gdk_pixbuf_copy (pixbuf);
image = gtk_image_new_from_pixbuf (copy);
g_object_unref (copy);
gtk_widget_set_halign (image, GTK_ALIGN_START);
gtk_widget_set_valign (image, GTK_ALIGN_START);
gtk_widget_show (image);
gtk_grid_attach (grid, image, 0, row_num, 1, 1);
gtk_grid_attach (grid, label, 1, row_num, 1, 1);
}
row_num++;
if (marks->next != NULL)
{
GtkWidget *separator;
separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
gtk_widget_show (separator);
gtk_grid_attach (grid, separator, 0, row_num, 2, 1);
row_num++;
}
g_free (text);
}
if (grid == NULL)
{
return FALSE;
}
gtk_tooltip_set_custom (tooltip, GTK_WIDGET (grid));
return TRUE;
}
static gboolean
gutter_renderer_query_tooltip (GtkSourceGutterRenderer *renderer,
GtkTextIter *iter,
GdkRectangle *area,
gint x,
gint y,
GtkTooltip *tooltip)
{
GSList *marks;
GtkSourceView *view;
GtkSourceBuffer *buffer;
gboolean ret;
view = GTK_SOURCE_VIEW (gtk_source_gutter_renderer_get_view (renderer));
buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
marks = gtk_source_buffer_get_source_marks_at_iter (buffer,
iter,
NULL);
if (marks != NULL)
{
marks = g_slist_sort_with_data (marks,
sort_marks_by_priority,
view);
marks = g_slist_reverse (marks);
ret = set_tooltip_widget_from_marks (view, tooltip, marks);
g_slist_free (marks);
return ret;
}
return FALSE;
}
static gboolean
gutter_renderer_query_activatable (GtkSourceGutterRenderer *renderer,
GtkTextIter *iter,
GdkRectangle *area,
GdkEvent *event)
{
return TRUE;
}
static void
gutter_renderer_change_view (GtkSourceGutterRenderer *renderer,
GtkTextView *old_view)
{
GtkSourceView *view;
view = GTK_SOURCE_VIEW (gtk_source_gutter_renderer_get_view (renderer));
if (view != NULL)
{
gtk_source_gutter_renderer_set_size (renderer,
measure_line_height (view));
}
if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_marks_parent_class)->change_view != NULL)
{
GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_marks_parent_class)->change_view (renderer,
old_view);
}
}
static void
gtk_source_gutter_renderer_marks_class_init (GtkSourceGutterRendererMarksClass *klass)
{
GtkSourceGutterRendererClass *renderer_class = GTK_SOURCE_GUTTER_RENDERER_CLASS (klass);
renderer_class->query_data = gutter_renderer_query_data;
renderer_class->query_tooltip = gutter_renderer_query_tooltip;
renderer_class->query_activatable = gutter_renderer_query_activatable;
renderer_class->change_view = gutter_renderer_change_view;
}
static void
gtk_source_gutter_renderer_marks_init (GtkSourceGutterRendererMarks *self)
{
}
GtkSourceGutterRenderer *
gtk_source_gutter_renderer_marks_new (void)
{
return g_object_new (GTK_SOURCE_TYPE_GUTTER_RENDERER_MARKS, NULL);
}