[gtksourceview] Add tooltips to GtkSourceMarks.



commit 049e05b813238c73836987d31e0dbafa6fc01054
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Mon May 25 22:42:14 2009 +0200

    Add tooltips to GtkSourceMarks.
    
    Add tooltips to GtkSourceMarks to show aditional information by Yevgen Muntyan.
---
 docs/reference/gtksourceview-2.0-sections.txt |    3 +
 docs/reference/tmpl/view.sgml                 |   34 +++
 gtksourceview/gtksourceview.c                 |  315 ++++++++++++++++++++++++-
 gtksourceview/gtksourceview.h                 |   15 ++
 tests/test-widget.c                           |   39 +++
 5 files changed, 396 insertions(+), 10 deletions(-)

diff --git a/docs/reference/gtksourceview-2.0-sections.txt b/docs/reference/gtksourceview-2.0-sections.txt
index 00fbf68..341efc1 100644
--- a/docs/reference/gtksourceview-2.0-sections.txt
+++ b/docs/reference/gtksourceview-2.0-sections.txt
@@ -46,6 +46,7 @@ gtk_source_buffer_get_type
 <INCLUDE>gtksourceview/gtksourceview.h</INCLUDE>
 GtkSourceView
 GtkSourceViewClass
+GtkSourceViewMarkTooltipFunc
 GtkSourceSmartHomeEndType
 GtkSourceDrawSpacesFlags
 gtk_source_view_new
@@ -66,6 +67,8 @@ gtk_source_view_set_mark_category_pixbuf
 gtk_source_view_get_mark_category_pixbuf
 gtk_source_view_get_mark_category_background
 gtk_source_view_set_mark_category_background
+gtk_source_view_set_mark_category_tooltip_func
+gtk_source_view_set_mark_category_tooltip_markup_func
 gtk_source_view_set_highlight_current_line
 gtk_source_view_get_highlight_current_line
 gtk_source_view_set_show_line_marks
diff --git a/docs/reference/tmpl/view.sgml b/docs/reference/tmpl/view.sgml
index 64fa772..a527c75 100644
--- a/docs/reference/tmpl/view.sgml
+++ b/docs/reference/tmpl/view.sgml
@@ -112,6 +112,16 @@ a text view which syntax highlighting, undo/redo and text marks. Use a
 @_gtk_source_reserved3: 
 @_gtk_source_reserved4: 
 
+<!-- ##### USER_FUNCTION GtkSourceViewMarkTooltipFunc ##### -->
+<para>
+Function type for setting up a tooltip for #GtkSourceMark.
+</para>
+
+ mark: the #GtkSourceMark
+ user_data: user data pointer which was passed to gtk_source_view_set_mark_category_tooltip_func()
+ Returns: a new-allocated text that is going to be shown as tooltip text.
+
+
 <!-- ##### ENUM GtkSourceSmartHomeEndType ##### -->
 <para>
 
@@ -301,6 +311,30 @@ a text view which syntax highlighting, undo/redo and text marks. Use a
 @color: 
 
 
+<!-- ##### FUNCTION gtk_source_view_set_mark_category_tooltip_func ##### -->
+<para>
+
+</para>
+
+ view: 
+ category: 
+ func: 
+ user_data: 
+ user_data_notify: 
+
+
+<!-- ##### FUNCTION gtk_source_view_set_mark_category_tooltip_markup_func ##### -->
+<para>
+
+</para>
+
+ view: 
+ category: 
+ markup_func: 
+ user_data: 
+ user_data_notify: 
+
+
 <!-- ##### FUNCTION gtk_source_view_set_highlight_current_line ##### -->
 <para>
 
diff --git a/gtksourceview/gtksourceview.c b/gtksourceview/gtksourceview.c
index 9da3e48..f73d3ea 100644
--- a/gtksourceview/gtksourceview.c
+++ b/gtksourceview/gtksourceview.c
@@ -115,6 +115,7 @@ struct _GtkSourceViewPrivate
 	gboolean	 show_right_margin;
 	guint		 right_margin_pos;
 	gint             cached_right_margin_pos;
+	gint		 cached_line_number_width;
 
 	gboolean	 style_scheme_applied;
 	GtkSourceStyleScheme *style_scheme;
@@ -152,8 +153,14 @@ typedef struct
 {
 	gint priority;
 	GdkPixbuf *pixbuf;
+	
+	GtkSourceViewMarkTooltipFunc tooltip_func;
+	gpointer tooltip_data;
+	GDestroyNotify tooltip_data_notify;
+	
 	GdkColor background;
-	guint background_set;
+	guint background_set : 1;
+	guint tooltip_markup : 1;
 } MarkCategory;
 
 /* Prototypes. */
@@ -217,9 +224,21 @@ static void	gtk_source_view_get_property		(GObject           *object,
 static void     gtk_source_view_style_set               (GtkWidget         *widget,
 							 GtkStyle          *previous_style);
 static void	gtk_source_view_realize			(GtkWidget         *widget);
+static gboolean gtk_source_view_query_tooltip		(GtkWidget	   *widget,
+							 gint		    x,
+							 gint               y,
+							 gboolean           keyboard_mode,
+							 GtkTooltip        *tooltip);
 static void	gtk_source_view_update_style_scheme	(GtkSourceView     *view);
 
 static MarkCategory *
+		gtk_source_view_get_mark_category	(GtkSourceView     *view,
+							 GtkSourceMark     *mark);
+
+static MarkCategory *
+		gtk_source_view_ensure_category		(GtkSourceView     *view,
+							 const gchar       *name);
+static MarkCategory *
 		mark_category_new			(gint               priority,
 							 GdkPixbuf         *pixbuf);
 static void	mark_category_free			(MarkCategory      *cat);
@@ -247,6 +266,7 @@ gtk_source_view_class_init (GtkSourceViewClass *klass)
 	widget_class->expose_event = gtk_source_view_expose;
 	widget_class->style_set = gtk_source_view_style_set;
 	widget_class->realize = gtk_source_view_realize;
+	widget_class->query_tooltip = gtk_source_view_query_tooltip;
 
 	textview_class->populate_popup = gtk_source_view_populate_popup;
 	textview_class->move_cursor = gtk_source_view_move_cursor;
@@ -711,6 +731,7 @@ gtk_source_view_init (GtkSourceView *view)
 	view->priv->smart_home_end = GTK_SOURCE_SMART_HOME_END_DISABLED;
 	view->priv->right_margin_pos = DEFAULT_RIGHT_MARGIN_POSITION;
 	view->priv->cached_right_margin_pos = -1;
+	view->priv->cached_line_number_width = 0;
 
 	gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 2);
 	gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 2);
@@ -1282,7 +1303,7 @@ draw_line_marks (GtkSourceView *view,
 
 	/* Draw the mark with higher priority */
 	marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
-
+	
 	composite = NULL;
 	mark_width = mark_height = 0;
 
@@ -1291,11 +1312,13 @@ draw_line_marks (GtkSourceView *view,
 	{
 		GtkSourceMark *mark;
 		GdkPixbuf *pixbuf;
+		MarkCategory *cat;
 
 		mark = marks->data;
 
-		pixbuf = gtk_source_view_get_mark_category_pixbuf (view,
-					gtk_source_mark_get_category (mark));
+		cat = gtk_source_view_get_mark_category (view, mark);
+		if (cat != NULL && cat->pixbuf != NULL)
+			pixbuf = g_object_ref (cat->pixbuf);
 
 		if (pixbuf != NULL)
 		{
@@ -1439,6 +1462,7 @@ gtk_source_view_paint_margin (GtkSourceView *view,
 		margin_width = text_width + 4;
 	else
 		margin_width = 0;
+	view->priv->cached_line_number_width = margin_width;
 
 	x_pixmap = margin_width;
 
@@ -1671,16 +1695,16 @@ gtk_source_view_paint_marks_background (GtkSourceView  *view,
 
 		while (marks != NULL)
 		{
-			const gchar *category;
 			MarkCategory *cat = NULL;
-			category = gtk_source_mark_get_category (marks->data);
-			if (category != NULL)
-				cat = g_hash_table_lookup (view->priv->mark_categories, category);
+
+			cat = gtk_source_view_get_mark_category (view, marks->data);
+
 			if (cat != NULL && cat->background_set && cat->priority > priority)
 			{
 				background = &cat->background;
 				priority = cat->priority;
 			}
+
 			marks = g_slist_delete_link (marks, marks);
 		}
 
@@ -2496,10 +2520,11 @@ mark_category_new (gint priority, GdkPixbuf *pixbuf)
 
 	cat = g_slice_new0 (MarkCategory);
 	cat->priority = priority;
+
 	if (pixbuf != NULL)
+	{
 		cat->pixbuf = g_object_ref (pixbuf);
-	else
-		cat->pixbuf = NULL;
+	}
 
 	return cat;
 }
@@ -2507,11 +2532,49 @@ mark_category_new (gint priority, GdkPixbuf *pixbuf)
 static void
 mark_category_free (MarkCategory *cat)
 {
+	if (cat->tooltip_data_notify != NULL)
+	{
+		cat->tooltip_data_notify (cat->tooltip_data);
+	}
 	if (cat->pixbuf)
+	{
 		g_object_unref (cat->pixbuf);
+	}
 	g_slice_free (MarkCategory, cat);
 }
 
+static MarkCategory *
+gtk_source_view_get_mark_category (GtkSourceView *view,
+				   GtkSourceMark *mark)
+{
+	const gchar *category;
+	category = gtk_source_mark_get_category (mark);
+	if (category)
+		return g_hash_table_lookup (view->priv->mark_categories,
+					    category);
+	else
+		return NULL;
+}
+
+static MarkCategory *
+gtk_source_view_ensure_category (GtkSourceView *view,
+				 const gchar   *name)
+{
+	MarkCategory *cat;
+
+	cat = g_hash_table_lookup (view->priv->mark_categories, name);
+
+	if (cat == NULL)
+	{
+		cat = mark_category_new (0, NULL);
+		g_hash_table_insert (view->priv->mark_categories,
+				     g_strdup (name),
+				     cat);
+	}
+
+	return cat;
+}
+
 /**
  * gtk_source_view_set_mark_category_pixbuf:
  * @view: a #GtkSourceView.
@@ -2561,7 +2624,9 @@ gtk_source_view_set_mark_category_pixbuf (GtkSourceView *view,
 		if (cat != NULL)
 		{
 			if (cat->pixbuf != NULL)
+			{
 				g_object_unref (cat->pixbuf);
+			}
 			cat->pixbuf = g_object_ref (pixbuf);
 		}
 		else
@@ -2614,6 +2679,112 @@ gtk_source_view_get_mark_category_pixbuf (GtkSourceView *view,
 		return NULL;
 }
 
+static void
+set_mark_category_tooltip_func (GtkSourceView   *view,
+				const gchar     *category,
+				GtkSourceViewMarkTooltipFunc func,
+				gpointer	 user_data,
+				GDestroyNotify   user_data_notify,
+				gboolean markup)
+{
+	MarkCategory *cat;
+
+	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
+	g_return_if_fail (category != NULL);
+
+	cat = gtk_source_view_ensure_category (view, category);
+
+	if (cat->tooltip_data_notify)
+		cat->tooltip_data_notify (cat->tooltip_data);
+
+	cat->tooltip_func = func;
+	cat->tooltip_markup = markup;
+	cat->tooltip_data = user_data;
+	cat->tooltip_data_notify = user_data_notify;
+
+	if (func != NULL)
+	{
+		gtk_widget_set_has_tooltip (GTK_WIDGET (view), TRUE);
+		if (GTK_WIDGET_REALIZED (view))
+			gtk_widget_trigger_tooltip_query (GTK_WIDGET (view));
+	}
+}
+
+/**
+ * gtk_source_view_set_mark_category_tooltip_func:
+ * @view: a #GtkSourceView.
+ * @category: a mark category.
+ * @func: a #GtkSourceViewMarkTooltipFunc or %NULL.
+ * @user_data: user data which will be passed to @func.
+ * @user_data_notify: if not %NULL, it will be called for @user_data when the
+ * @view is destroyed or when gtk_source_view_set_mark_category_tooltip_func() or
+ * gtk_source_view_set_mark_category_tooltip_markup_func() is called for @category again.
+ *
+ * Set a #GtkSourceViewMarkTooltipFunc used to set tooltip on marks from the
+ * given mark @category. If @func is %NULL and @markup_func is %NULL then tooltips
+ * will not be shown for marks from @category. If @markup_func is not %NULL 
+ * @markup_func is going to be used instead of @func.
+ *
+ * <informalexample><programlisting>
+ * static gchar *
+ * tooltip_func (GtkSourceMark *mark,
+ *               gpointer       user_data)
+ * {
+ *   gchar *text;
+ *
+ *   text = get_tooltip_for_mark (mark, user_data);
+ *
+ *   return text;
+ * }
+ *
+ * ...
+ *
+ * GtkSourceView *view;
+ *
+ * gtk_source_view_set_mark_category_tooltip_func (view, "my-mark", NULL, tooltip_func,
+ *                                                 g_strdup ("<b>my string</b>"), g_free);
+ * gtk_source_view_set_mark_category_tooltip_func (view, "other-mark", tooltip_func, NULL,
+ *                                                 NULL, NULL);
+ * </programlisting></informalexample>
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_view_set_mark_category_tooltip_func (GtkSourceView   *view,
+						const gchar     *category,
+						GtkSourceViewMarkTooltipFunc func,
+						gpointer	 user_data,
+						GDestroyNotify   user_data_notify)
+{
+	set_mark_category_tooltip_func (view, category, func, user_data,
+					user_data_notify, FALSE);
+}
+
+/**
+ * gtk_source_view_set_mark_category_tooltip_markup_func:
+ * @view: a #GtkSourceView.
+ * @category: a mark category.
+ * @markup_func: a #GtkSourceViewMarkTooltipFunc or %NULL.
+ * @user_data: user data which will be passed to @func.
+ * @user_data_notify: if not %NULL, it will be called for @user_data when the
+ * @view is destroyed or when gtk_source_view_set_mark_category_tooltip_func() or
+ * gtk_source_view_set_mark_category_tooltip_markup_func() is called for @category again.
+ *
+ * See gtk_source_view_set_mark_category_tooltip_func() for more information.
+ *
+ * Since: 2.8
+ */
+void
+gtk_source_view_set_mark_category_tooltip_markup_func (GtkSourceView   *view,
+						       const gchar     *category,
+						       GtkSourceViewMarkTooltipFunc markup_func,
+						       gpointer         user_data,
+						       GDestroyNotify   user_data_notify)
+{
+	set_mark_category_tooltip_func (view, category, markup_func, user_data,
+					user_data_notify, TRUE);
+}
+
 /**
  * gtk_source_view_set_mark_category_background:
  * @view: a #GtkSourceView.
@@ -3394,6 +3565,130 @@ gtk_source_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
 	return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->button_press_event (widget, event);
 }
 
+static void
+set_tooltip_widget_from_marks (GtkSourceView *view,
+			       GtkTooltip *tooltip,
+			       GSList *marks)
+{
+	GtkWidget *vbox;
+	
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_widget_show (vbox);
+
+	while (marks != NULL)
+	{
+		MarkCategory *cat;
+		GtkSourceMark *mark;
+
+		mark = marks->data;
+		cat = gtk_source_view_get_mark_category (view, mark);
+
+		if (cat != NULL)
+		{
+			GtkWidget *image;
+			GtkWidget *label;
+			GtkWidget *hbox;
+			GtkWidget *separator;
+			gchar *text;
+	
+			hbox = gtk_hbox_new (FALSE, 4);
+			gtk_widget_show (hbox);
+			gtk_box_pack_start (GTK_BOX (vbox), hbox,
+					    FALSE, FALSE, 0);
+			
+			text = cat->tooltip_func (mark, cat->tooltip_data);
+		
+			if (text != NULL)
+			{
+				label = gtk_label_new (NULL);
+				if (cat->tooltip_markup)
+					gtk_label_set_markup (GTK_LABEL (label), text);
+				else
+					gtk_label_set_text (GTK_LABEL (label), text);
+				
+				gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+				gtk_widget_show (label);
+			
+				gtk_box_pack_end (GTK_BOX (hbox), label,
+						  TRUE, TRUE, 0);
+			
+				if (cat->pixbuf != NULL)
+				{
+					image = gtk_image_new_from_pixbuf (cat->pixbuf);
+					gtk_widget_show (image);
+			
+					gtk_box_pack_start (GTK_BOX (hbox), image,
+							    FALSE, FALSE, 0);
+				}
+				
+				if (g_slist_length (marks) != 1)
+				{
+					separator = gtk_hseparator_new ();
+					gtk_widget_show (separator);
+					gtk_box_pack_start (GTK_BOX (vbox), separator,
+							    FALSE, FALSE, 0);
+				}
+				
+				g_free (text);
+			}
+		}
+
+		marks = g_slist_delete_link (marks, marks);
+	}
+	
+	gtk_tooltip_set_custom (tooltip, vbox);
+}
+
+static gboolean
+gtk_source_view_query_tooltip (GtkWidget  *widget,
+			       gint        x,
+			       gint        y,
+			       gboolean    keyboard_mode,
+			       GtkTooltip *tooltip)
+{
+	GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+	GtkSourceView *view = GTK_SOURCE_VIEW (widget);
+
+	/* Check if we are inside the marker area */
+	/* TODO: when folding is in, correct this */
+	if (x < gtk_text_view_get_border_window_size (text_view, GTK_TEXT_WINDOW_LEFT) &&
+	    x > view->priv->cached_line_number_width)
+	{
+		GtkTextIter line_iter;
+		GSList *marks;
+		GtkSourceBuffer *buffer;
+		gint line;
+
+		buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (text_view));
+		gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (widget),
+					     &line_iter,
+					     y,
+					     NULL);
+		line = gtk_text_iter_get_line (&line_iter);
+		marks = gtk_source_buffer_get_source_marks_at_line (buffer,
+								    line,
+								    NULL);
+
+		if (marks != NULL)
+		{
+			marks = g_slist_sort_with_data (marks, sort_marks_by_priority, view);
+			
+			set_tooltip_widget_from_marks (view, tooltip, marks);
+			
+			return TRUE;
+		}
+	}
+
+	if (GTK_WIDGET_CLASS (gtk_source_view_parent_class)->query_tooltip != NULL)
+		return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->query_tooltip (widget,
+										       x,
+										       y,
+										       keyboard_mode,
+										       tooltip);
+	else
+		return FALSE;
+}
+
 /**
  * gtk_source_view_get_auto_indent:
  * @view: a #GtkSourceView.
diff --git a/gtksourceview/gtksourceview.h b/gtksourceview/gtksourceview.h
index 22f55cf..3eae9af 100644
--- a/gtksourceview/gtksourceview.h
+++ b/gtksourceview/gtksourceview.h
@@ -180,6 +180,21 @@ gboolean         gtk_source_view_get_mark_category_background
 							 const gchar     *category,
 							 GdkColor        *dest);
 
+typedef gchar *  (*GtkSourceViewMarkTooltipFunc)	(GtkSourceMark	*mark,
+							 gpointer	 user_data);
+void             gtk_source_view_set_mark_category_tooltip_func
+							(GtkSourceView   *view,
+							 const gchar     *category,
+							 GtkSourceViewMarkTooltipFunc func,
+							 gpointer	  user_data,
+							 GDestroyNotify   user_data_notify);
+void		 gtk_source_view_set_mark_category_tooltip_markup_func
+							(GtkSourceView   *view,
+							const gchar     *category,
+							GtkSourceViewMarkTooltipFunc markup_func,
+							gpointer         user_data,
+							GDestroyNotify   user_data_notify);
+
 void             gtk_source_view_set_mark_category_priority
 							(GtkSourceView   *view,
 							 const gchar     *category,
diff --git a/tests/test-widget.c b/tests/test-widget.c
index 7cbb85e..a8bc50b 100644
--- a/tests/test-widget.c
+++ b/tests/test-widget.c
@@ -1161,6 +1161,9 @@ button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer user_data)
 			/* just take the first and delete it */
 			gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer),
 						     GTK_TEXT_MARK (mark_list->data));
+			mark_list = g_slist_delete_link (mark_list, mark_list);
+			gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer),
+						     GTK_TEXT_MARK (mark_list->data));
 		}
 		else
 		{
@@ -1169,6 +1172,12 @@ button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer user_data)
 							      NULL,
 							      mark_type,
 							      &line_start);
+			gtk_text_iter_set_line_offset (&line_start,
+						       gtk_text_iter_get_chars_in_line (&line_start) - 1);
+			gtk_source_buffer_create_source_mark (buffer,
+							      NULL,
+							      mark_type,
+							      &line_start);
 		}
 
 		g_slist_free (mark_list);
@@ -1180,6 +1189,26 @@ button_press_cb (GtkWidget *widget, GdkEventButton *ev, gpointer user_data)
 
 /* Window creation functions -------------------------------------------------------- */
 
+static gchar *
+mark_tooltip_func (GtkSourceMark *mark,
+		   gpointer	  user_data)
+{
+	GtkTextBuffer *buf;
+	GtkTextIter iter;
+	gint line, column;
+	
+	buf = gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark));
+	
+	gtk_text_buffer_get_iter_at_mark (buf, &iter, GTK_TEXT_MARK (mark));
+	line = gtk_text_iter_get_line (&iter) + 1;
+	column = gtk_text_iter_get_line_offset (&iter);
+
+	if (strcmp (gtk_source_mark_get_category (mark), MARK_TYPE_1) == 0)
+		return g_strdup_printf ("Line: %d, Column: %d", line, column);
+	else
+		return g_strdup_printf ("<b>Line</b>: %d, <i>Column</i>: %d", line, column);
+}
+
 static GtkWidget *
 create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from)
 {
@@ -1329,6 +1358,11 @@ create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from)
 		gtk_source_view_set_mark_category_background (GTK_SOURCE_VIEW (view), MARK_TYPE_1, &color);
 		gtk_source_view_set_mark_category_pixbuf (GTK_SOURCE_VIEW (view), MARK_TYPE_1, pixbuf);
 		gtk_source_view_set_mark_category_priority (GTK_SOURCE_VIEW (view), MARK_TYPE_1, 1);
+		gtk_source_view_set_mark_category_tooltip_func (GTK_SOURCE_VIEW (view),
+								MARK_TYPE_1,
+								mark_tooltip_func,
+								NULL,
+								NULL);
 		g_object_unref (pixbuf);
 	}
 	else
@@ -1346,6 +1380,11 @@ create_view_window (GtkSourceBuffer *buffer, GtkSourceView *from)
 		gtk_source_view_set_mark_category_background (GTK_SOURCE_VIEW (view), MARK_TYPE_2, &color);
 		gtk_source_view_set_mark_category_pixbuf (GTK_SOURCE_VIEW (view), MARK_TYPE_2, pixbuf);
 		gtk_source_view_set_mark_category_priority (GTK_SOURCE_VIEW (view), MARK_TYPE_2, 2);
+		gtk_source_view_set_mark_category_tooltip_markup_func (GTK_SOURCE_VIEW (view),
+								       MARK_TYPE_2,
+								       mark_tooltip_func,
+								       NULL,
+								       NULL);
 		g_object_unref (pixbuf);
 	}
 	else



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]