1 /*  gtksourceview.c
2  *
3  *  Copyright (C) 2001
4  *  Mikael Hermansson<tyan@linux.se>
5  *  Chris Phelps <chicane@reninet.com>
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU Library General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License*  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 
20 #include <gtk/gtk.h>
21 #include <gdk/gdkkeysyms.h>
22 #include <pango/pango-tabs.h>
23 
24 #include <vdk/gtksourceview.h>
25 
26 #define GUTTER_PIXMAP 16
27 #define TAB_STOP 4
28 #define MIN_NUMBER_WINDOW_WIDTH 20
29 
30 enum {
31 	UNDO,
32 	REDO,
33 	LAST_SIGNAL
34 };
35 
36 static guint signals[LAST_SIGNAL] = { 0 };
37 
38 static GObjectClass *parent_class = NULL;
39 
40 /* Prototypes. */
41 static void gtk_source_view_class_init (GtkSourceViewClass *klass);
42 static void gtk_source_view_init (GtkSourceView *view);
43 static void gtk_source_view_finalize (GObject *object);
44 
45 static void gtk_source_view_pixbuf_foreach_unref (gpointer key,
46 						  gpointer value,
47 						  gpointer user_data);
48 
49 static void gtk_source_view_undo (GtkSourceView *view);
50 static void gtk_source_view_redo (GtkSourceView *view);
51 
52 static void gtk_source_view_populate_popup (GtkTextView *view,
53 					    GtkMenu     *menu);
54 static void menuitem_activate_cb (GtkWidget   *menuitem,
55 				  GtkTextView *text_view);
56 
57 static void gtk_source_view_draw_line_markers (GtkSourceView *view,
58 					       gint           line,
59 					       gint           x,
60 					       gint           y);
61 static GdkPixbuf *gtk_source_view_get_line_marker (GtkSourceView *view,
62 						   GList *list);
63 
64 static void gtk_source_view_get_lines (GtkTextView  *text_view,
65 				       gint          first_y,
66 				       gint          last_y,
67 				       GArray       *buffer_coords,
68 				       GArray       *numbers,
69 				       gint         *countp);
70 static gint     gtk_source_view_expose (GtkWidget      *widget,
71 					GdkEventExpose *event);
72 
73 
74 static gint gtk_source_view_calculate_tab_stop_width (GtkWidget *widget,
75 						      gint       tab_stop);
76 
77 
78 /* Private functions. */
79 static void
gtk_source_view_class_init(GtkSourceViewClass * klass)80 gtk_source_view_class_init (GtkSourceViewClass *klass)
81 {
82 	GObjectClass   *object_class;
83 	GtkTextViewClass *textview_class;
84 	GtkBindingSet    *binding_set;
85 	GtkWidgetClass *widget_class;
86 
87 	object_class = (GObjectClass *) klass;
88 	textview_class = GTK_TEXT_VIEW_CLASS (klass);
89 	parent_class = g_type_class_peek_parent (klass);
90 	widget_class = GTK_WIDGET_CLASS (klass);
91 
92 	object_class->finalize = gtk_source_view_finalize;
93 	widget_class->expose_event = gtk_source_view_expose;
94 	textview_class->populate_popup = gtk_source_view_populate_popup;
95 	klass->undo = gtk_source_view_undo;
96 	klass->redo = gtk_source_view_redo;
97 
98 	signals[UNDO] =
99 		gtk_signal_new ("undo",
100 				GTK_RUN_LAST | GTK_RUN_ACTION,
101 				GTK_CLASS_TYPE (object_class),
102 				GTK_SIGNAL_OFFSET (GtkSourceViewClass, undo),
103 				gtk_marshal_VOID__VOID, GTK_TYPE_NONE, 0);
104 	signals[REDO] =
105 		gtk_signal_new ("redo",
106 				GTK_RUN_LAST | GTK_RUN_ACTION,
107 				GTK_CLASS_TYPE (object_class),
108 				GTK_SIGNAL_OFFSET (GtkSourceViewClass, redo),
109 				gtk_marshal_VOID__VOID, GTK_TYPE_NONE, 0);
110 
111 	binding_set = gtk_binding_set_by_class (klass);
112 	gtk_binding_entry_add_signal (binding_set,
113 				      GDK_z,
114 				      GDK_CONTROL_MASK,
115 				      "undo", 0);
116 	gtk_binding_entry_add_signal (binding_set,
117 				      GDK_r,
118 				      GDK_CONTROL_MASK,
119 				      "redo", 0);
120 
121 	/*
122 	  added by mario motta
123 	*/
124 	gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_CONTROL_MASK,
125 				      "copy_clipboard", 0);
126 	gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK,
127 				      "paste_clipboard", 0);
128 	gtk_binding_entry_add_signal (binding_set, GDK_Cancel, GDK_SHIFT_MASK,
129 				      "cut_clipboard", 0);
130 
131 }
132 
133 static void
gtk_source_view_init(GtkSourceView * view)134 gtk_source_view_init (GtkSourceView *view)
135 {
136 	view->pixmap_cache = g_hash_table_new (g_str_hash, g_str_equal);
137 
138 	gtk_source_view_set_show_line_numbers (view, TRUE);
139 	gtk_source_view_set_show_line_pixmaps (view, TRUE);
140 }
141 
142 static void
gtk_source_view_finalize(GObject * object)143 gtk_source_view_finalize (GObject *object)
144 {
145 	GtkSourceView *view;
146 
147 	g_return_if_fail (object != NULL);
148 	g_return_if_fail (GTK_IS_SOURCE_VIEW (object));
149 
150 	view = GTK_SOURCE_VIEW (object);
151 
152 	if (view->pixmap_cache) {
153 		g_hash_table_foreach_remove (view->pixmap_cache,
154 					     (GHRFunc) gtk_source_view_pixbuf_foreach_unref,
155 					     NULL);
156 		g_hash_table_destroy (view->pixmap_cache);
157 	}
158 }
159 
160 static void
gtk_source_view_pixbuf_foreach_unref(gpointer key,gpointer value,gpointer user_data)161 gtk_source_view_pixbuf_foreach_unref (gpointer key,
162 				      gpointer value,
163 				      gpointer user_data)
164 {
165 	g_object_unref (G_OBJECT (value));
166 }
167 
168 static void
gtk_source_view_undo(GtkSourceView * view)169 gtk_source_view_undo (GtkSourceView *view)
170 {
171 	g_return_if_fail (view != NULL);
172 	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
173 
174 	gtk_source_buffer_undo (GTK_SOURCE_BUFFER
175 				(gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))));
176 }
177 
178 static void
gtk_source_view_redo(GtkSourceView * view)179 gtk_source_view_redo (GtkSourceView *view)
180 {
181 	g_return_if_fail (view != NULL);
182 	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
183 
184 	gtk_source_buffer_redo (GTK_SOURCE_BUFFER
185 				(gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))));
186 }
187 
188 static void
gtk_source_view_populate_popup(GtkTextView * text_view,GtkMenu * menu)189 gtk_source_view_populate_popup (GtkTextView *text_view,
190 				GtkMenu     *menu)
191 {
192 	GtkTextBuffer *buffer;
193 	GtkWidget *menuitem;
194 
195 	buffer = gtk_text_view_get_buffer (text_view);
196 	if (!buffer && !GTK_IS_SOURCE_BUFFER (buffer))
197 		return;
198 
199 	/* what is this for? */
200 	menuitem = gtk_menu_item_new ();
201 	gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 3);
202 	gtk_widget_show (menuitem);
203 
204 	/* create undo menuitem. */
205 	menuitem = gtk_menu_item_new_with_label ("Undo");
206 	g_object_set_data (G_OBJECT (menuitem), "gtk-signal", "undo");
207 	g_signal_connect (G_OBJECT (menuitem),
208 			  "activate",
209 			  G_CALLBACK (menuitem_activate_cb),
210 			  text_view);
211 	gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 4);
212 	gtk_widget_set_sensitive (menuitem,
213 				  gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (buffer)));
214 	gtk_widget_show (menuitem);
215 
216 	/* create redo menuitem. */
217 	menuitem = gtk_menu_item_new_with_label ("Redo");
218 	g_object_set_data (G_OBJECT (menuitem), "gtk-signal", "redo");
219 	g_signal_connect (G_OBJECT (menuitem),
220 			  "activate",
221 			  G_CALLBACK (menuitem_activate_cb),
222 			  text_view);
223 	gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 5);
224 	gtk_widget_set_sensitive (menuitem,
225 				  gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (buffer)));
226 	gtk_widget_show (menuitem);
227 }
228 
229 static void
menuitem_activate_cb(GtkWidget * menuitem,GtkTextView * text_view)230 menuitem_activate_cb (GtkWidget   *menuitem,
231 		      GtkTextView *text_view)
232 {
233 	const gchar *signal;
234 
235 	signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
236 	g_signal_emit_by_name (G_OBJECT (text_view), signal);
237 }
238 
239 static GdkPixbuf *
gtk_source_view_get_line_marker(GtkSourceView * view,GList * list)240 gtk_source_view_get_line_marker (GtkSourceView *view,
241 				 GList         *list)
242 {
243 	GdkPixbuf *pixbuf;
244 	GdkPixbuf *composite;
245 	GList *iter;
246 
247 	pixbuf = gtk_source_view_get_pixbuf (view, (const gchar *) list->data);
248 	if (!pixbuf) {
249 		g_warning ("Unknown marker '%s' used.", (char*)list->data);
250 		return NULL;
251 	}
252 
253 	if (!list->next)
254 		g_object_ref (pixbuf);
255 	else {
256 		pixbuf = gdk_pixbuf_copy (pixbuf);
257 		for (iter = list->next; iter; iter = iter->next) {
258 			composite = gtk_source_view_get_pixbuf (view,
259 								(const gchar *) iter->data);
260 			if (composite) {
261 				gint width;
262 				gint height;
263 				gint comp_width;
264 				gint comp_height;
265 
266 				width = gdk_pixbuf_get_width (pixbuf);
267 				height = gdk_pixbuf_get_height (pixbuf);
268 				comp_width = gdk_pixbuf_get_width (composite);
269 				comp_height = gdk_pixbuf_get_height (composite);
270 				gdk_pixbuf_composite ((const GdkPixbuf *) composite,
271 						      pixbuf,
272 						      0, 0,
273 						      width, height,
274 						      0, 0,
275 						      width / comp_width,
276 						      height / comp_height,
277 						      GDK_INTERP_BILINEAR,
278 						      225);
279 			} else
280 				g_warning ("Unknown marker '%s' used", (char*)iter->data);
281 		}
282 	}
283 
284 	return pixbuf;
285 }
286 
287 static void
gtk_source_view_draw_line_markers(GtkSourceView * view,gint line,gint x,gint y)288 gtk_source_view_draw_line_markers (GtkSourceView *view,
289 				   gint           line,
290 				   gint           x,
291 				   gint           y)
292 {
293 	GList *list;
294 	GdkPixbuf *pixbuf;
295 	GdkWindow *win = gtk_text_view_get_window (GTK_TEXT_VIEW (view),
296 						   GTK_TEXT_WINDOW_LEFT);
297 
298 	list = (GList *)
299 		gtk_source_buffer_line_get_markers (GTK_SOURCE_BUFFER
300 						    (GTK_TEXT_VIEW (view)->buffer), line);
301 	if (list) {
302 		if ((pixbuf = gtk_source_view_get_line_marker (view, list))) {
303 			gdk_pixbuf_render_to_drawable_alpha (pixbuf, GDK_DRAWABLE (win), 0, 0,
304 							     x, y,
305 							     gdk_pixbuf_get_width (pixbuf),
306 							     gdk_pixbuf_get_height (pixbuf),
307 							     GDK_PIXBUF_ALPHA_BILEVEL,
308 							     127, GDK_RGB_DITHER_NORMAL, 0, 0);
309 			g_object_unref (pixbuf);
310 		}
311 	}
312 }
313 
314 static void
gtk_source_view_get_lines(GtkTextView * text_view,gint first_y,gint last_y,GArray * buffer_coords,GArray * numbers,gint * countp)315 gtk_source_view_get_lines (GtkTextView  *text_view,
316 			   gint          first_y,
317 			   gint          last_y,
318 			   GArray       *buffer_coords,
319 			   GArray       *numbers,
320 			   gint         *countp)
321 {
322 	GtkTextIter iter;
323 	gint count;
324 	gint size;
325 	gint last_line_num;
326 
327 	g_array_set_size (buffer_coords, 0);
328 	g_array_set_size (numbers, 0);
329 
330 	/* get iter at first y */
331 	gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
332 
333 	/* For each iter, get its location and add it to the arrays.
334 	 * Stop when we pass last_y. */
335 	count = 0;
336 	size = 0;
337 
338 	while (!gtk_text_iter_is_end (&iter)) {
339 		gint y, height;
340 
341 		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
342 
343 		g_array_append_val (buffer_coords, y);
344 		last_line_num = gtk_text_iter_get_line (&iter);
345 		g_array_append_val (numbers, last_line_num);
346 
347 		++count;
348 
349 		if ((y + height) > last_y)
350 			break;
351 
352 		gtk_text_iter_forward_line (&iter);
353 	}
354 
355 	if (gtk_text_iter_is_end (&iter)) {
356 		gint y, height;
357 		gint line_num;
358 
359 		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
360 
361 		line_num = gtk_text_iter_get_line (&iter);
362 
363 		if (line_num != last_line_num)
364 		{
365 			g_array_append_val (buffer_coords, y);
366 			g_array_append_val (numbers, line_num);
367 			++count;
368 		}
369 	}
370 
371 	*countp = count;
372 }
373 
374 static void
gtk_source_view_paint_margin(GtkSourceView * view,GdkEventExpose * event)375 gtk_source_view_paint_margin (GtkSourceView *view,
376 			      GdkEventExpose *event)
377 {
378 	GtkTextView *text_view;
379 	GdkWindow *win;
380 	PangoLayout *layout;
381 	GArray *numbers;
382 	GArray *pixels;
383 	gchar *str;
384 	gint y1;
385 	gint y2;
386 	gint count;
387 	gint margin_width;
388 	gint text_width;
389 	gint i;
390 
391 	text_view = GTK_TEXT_VIEW (view);
392 
393 	win = gtk_text_view_get_window (text_view,
394 					GTK_TEXT_WINDOW_LEFT);
395 
396 	y1 = event->area.y;
397 	y2 = y1 + event->area.height;
398 
399 	/* get the extents of the line printing */
400 	gtk_text_view_window_to_buffer_coords (text_view,
401 					       GTK_TEXT_WINDOW_LEFT,
402 					       0,
403 					       y1,
404 					       NULL,
405 					       &y1);
406 
407 	gtk_text_view_window_to_buffer_coords (text_view,
408 					       GTK_TEXT_WINDOW_LEFT,
409 					       0,
410 					       y2,
411 					       NULL,
412 					       &y2);
413 
414 	numbers = g_array_new (FALSE, FALSE, sizeof (gint));
415 	pixels = g_array_new (FALSE, FALSE, sizeof (gint));
416 
417 	/* get the line numbers and y coordinates. */
418 	gtk_source_view_get_lines (text_view,
419 				   y1,
420 				   y2,
421 				   pixels,
422 				   numbers,
423 				   &count);
424 
425 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), "");
426 
427 	/* set size. */
428 	str = g_strdup_printf ("%d", MAX (999,
429 					  gtk_text_buffer_get_line_count (text_view->buffer)));
430 	pango_layout_set_text (layout, str, -1);
431 	g_free (str);
432 
433 	pango_layout_get_pixel_size (layout, &text_width, NULL);
434 	pango_layout_set_width (layout, text_width);
435 	pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
436 
437 	/* determine the width of the left margin. */
438 	if (view->show_line_numbers && view->show_line_pixmaps)
439 		margin_width = text_width + 4 + GUTTER_PIXMAP;
440 	else if (view->show_line_numbers)
441 		margin_width = text_width + 4;
442 	else if (view->show_line_pixmaps)
443 		margin_width = GUTTER_PIXMAP;
444 	else
445 		margin_width = 0;
446 
447 	gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
448 					      GTK_TEXT_WINDOW_LEFT,
449 					      margin_width);
450 
451 	if (margin_width == 0)
452 		return;
453 
454 	i = 0;
455 	while (i < count) {
456 		gint pos;
457 
458 		gtk_text_view_buffer_to_window_coords (text_view,
459 						       GTK_TEXT_WINDOW_LEFT,
460 						       0,
461 						       g_array_index (pixels, gint, i),
462 						       NULL,
463 						       &pos);
464 
465 		if (view->show_line_numbers ) {
466 			str = g_strdup_printf ("%d", g_array_index (numbers, gint, i) + 1);
467 
468 			pango_layout_set_text (layout, str, -1);
469 
470 			gtk_paint_layout (GTK_WIDGET (view)->style,
471 					  win,
472 					  GTK_WIDGET_STATE (view),
473 					  FALSE,
474 					  NULL,
475 					  GTK_WIDGET (view),
476 					  NULL,
477 					  text_width + 2, pos,
478 					  layout);
479 
480 			g_free (str);
481 		}
482 
483 		if (view->show_line_pixmaps) {
484 			gint x;
485 
486 			if (view->show_line_numbers)
487 				x = text_width + 4;
488 			else
489 				x = 0;
490 
491 			gtk_source_view_draw_line_markers (view,
492 							   g_array_index (numbers, gint, i) + 1,
493 							   x,
494 							   pos);
495 		}
496 
497 		++i;
498 	}
499 
500 	g_array_free (pixels, TRUE);
501 	g_array_free (numbers, TRUE);
502 
503 	g_object_unref (G_OBJECT (layout));
504 }
505 
gtk_source_view_expose(GtkWidget * widget,GdkEventExpose * event)506 static gint
507 gtk_source_view_expose (GtkWidget      *widget,
508 			GdkEventExpose *event)
509 {
510 	GtkSourceView *view;
511 	GtkTextView *text_view;
512 	gboolean event_handled;
513 
514 	view = GTK_SOURCE_VIEW (widget);
515 	text_view = GTK_TEXT_VIEW (widget);
516 
517 	event_handled = FALSE;
518 
519 	/* check if the expose event is for the text window first, and
520 	 * highlight the exposed region */
521 	if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)) {
522 		GtkTextIter iter1, iter2;
523 		gint y;
524 
525 		/* make sure the exposed area is highlighted */
526 		gtk_text_view_window_to_buffer_coords (text_view,
527 						       GTK_TEXT_WINDOW_TEXT,
528 						       0,
529 						       event->area.y,
530 						       NULL,
531 						       &y);
532 		gtk_text_view_get_line_at_y (text_view, &iter1, y, NULL);
533 		gtk_text_iter_backward_line (&iter1);
534 
535 		gtk_text_view_window_to_buffer_coords (text_view,
536 						       GTK_TEXT_WINDOW_TEXT,
537 						       0,
538 						       event->area.y + event->area.height,
539 						       NULL,
540 						       &y);
541 		gtk_text_view_get_line_at_y (text_view, &iter2, y, NULL);
542 		gtk_text_iter_forward_line (&iter2);
543 
544 		gtk_source_buffer_highlight_region (GTK_SOURCE_BUFFER (text_view->buffer),
545 						    &iter1, &iter2);
546 	}
547 
548 	/* now check for the left window, which contains the margin */
549 	if (event->window == gtk_text_view_get_window (text_view,
550 						       GTK_TEXT_WINDOW_LEFT)) {
551 		gtk_source_view_paint_margin (view, event);
552 		event_handled = TRUE;
553 	} else {
554 		if (GTK_WIDGET_CLASS (parent_class)->expose_event)
555 			event_handled =
556 				(* GTK_WIDGET_CLASS (parent_class)->expose_event)
557 				(widget, event);
558 	}
559 
560 	return event_handled;
561 }
562 
563 /*
564  *This is a pretty important function...we call it when the tab_stop is changed,
565  *And when the font is changed.
566  *NOTE: You must change this with the default font for now...
567  *It may be a good idea to set the tab_width for each GtkTextTag as well
568  *based on the font that we set at creation time
569  *something like style_cache_set_tabs_from_font or the like.
570  *Now, this *may* not be necessary because most tabs wont be inside of another highlight,
571  *except for things like multi-line comments (which will usually have an italic font which
572  *may or may not be a different size than the standard one), or if some random language
573  *definition decides that it would be spiffy to have a bg color for "start of line" whitespace
574  *"^\(\t\| \)+" would probably do the trick for that.
575  */
gtk_source_view_calculate_tab_stop_width(GtkWidget * widget,gint tab_stop)576 static gint
577 gtk_source_view_calculate_tab_stop_width (GtkWidget *widget,
578 					  gint       tab_stop)
579 {
580 	PangoLayout *layout;
581 	gchar *tab_string;
582 	int counter = 0;
583 	int tab_width = 0;
584 
585 	if (tab_stop == 0)
586 		return 0;
587 
588 	tab_string = g_malloc (tab_stop + 1);
589 
590 	while (counter < tab_stop) {
591 		tab_string[counter] = ' ';
592 		counter++;
593 	}
594 	tab_string[tab_stop] = '\0';
595 
596 	layout = gtk_widget_create_pango_layout (widget, tab_string);
597 	g_free (tab_string);
598 
599 	if (layout) {
600 		pango_layout_get_pixel_size (layout, &tab_width, NULL);
601 		g_object_unref (G_OBJECT (layout));
602 	} else
603 		tab_width = tab_stop * 8;
604 
605 	return tab_width;
606 }
607 
608 /* ----------------------------------------------------------------------
609  * Public interface
610  * ---------------------------------------------------------------------- */
611 
gtk_source_view_new()612 GtkWidget *
613 gtk_source_view_new ()
614 {
615 	GtkWidget *widget;
616 	GtkSourceBuffer *buffer;
617 
618 	buffer = gtk_source_buffer_new (NULL);
619 	widget = gtk_source_view_new_with_buffer (buffer);
620 	return widget;
621 }
622 
gtk_source_view_new_with_buffer(GtkSourceBuffer * buffer)623 GtkWidget *
624 gtk_source_view_new_with_buffer (GtkSourceBuffer *buffer)
625 {
626 	GtkWidget *view;
627 
628 	view = g_object_new (GTK_TYPE_SOURCE_VIEW, NULL);
629 	gtk_text_view_set_buffer (GTK_TEXT_VIEW (view), GTK_TEXT_BUFFER (buffer));
630 
631 	return view;
632 }
633 
gtk_source_view_get_type(void)634 GType
635 gtk_source_view_get_type (void)
636 {
637 	static GType our_type = 0;
638 
639 	if (our_type == 0) {
640 		static const GTypeInfo our_info = {
641 			sizeof (GtkSourceViewClass),
642 			(GBaseInitFunc) NULL,
643 			(GBaseFinalizeFunc) NULL,
644 			(GClassInitFunc) gtk_source_view_class_init,
645 			NULL,	/* class_finalize */
646 			NULL,	/* class_data */
647 			sizeof (GtkSourceView),
648 			0,	/* n_preallocs */
649 			(GInstanceInitFunc) gtk_source_view_init
650 		};
651 
652 		our_type = g_type_register_static (GTK_TYPE_TEXT_VIEW,
653 						   "GtkSourceView",
654 						   &our_info, 0);
655 	}
656 
657 	return our_type;
658 }
659 
gtk_source_view_get_show_line_numbers(GtkSourceView * view)660 gboolean
661 gtk_source_view_get_show_line_numbers (GtkSourceView *view)
662 {
663 	g_return_val_if_fail (view != NULL, FALSE);
664 	g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), FALSE);
665 
666 	return view->show_line_numbers;
667 }
668 
gtk_source_view_set_show_line_numbers(GtkSourceView * view,gboolean visible)669 void
670 gtk_source_view_set_show_line_numbers (GtkSourceView *view,
671 				       gboolean       visible)
672 {
673 	g_return_if_fail (view != NULL);
674 	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
675 
676 	if (visible) {
677 		if (!view->show_line_numbers) {
678 			/* Set left margin to minimum width if no margin is
679 			   visible yet. Otherwise, just queue a redraw, so the
680 			   expose handler will automatically adjust the margin. */
681 			if (!view->show_line_pixmaps)
682 				gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view),
683 								      GTK_TEXT_WINDOW_LEFT,
684 								      MIN_NUMBER_WINDOW_WIDTH);
685 			else
686 				gtk_widget_queue_draw (GTK_WIDGET (view));
687 
688 			view->show_line_numbers = visible;
689 		}
690 	} else {
691 		if (view->show_line_numbers) {
692 			view->show_line_numbers = visible;
693 
694 			/* force expose event, which will adjust margin. */
695 			gtk_widget_queue_draw (GTK_WIDGET (view));
696 		}
697 	}
698 }
699 
gtk_source_view_get_show_line_pixmaps(GtkSourceView * view)700 gboolean
701 gtk_source_view_get_show_line_pixmaps (GtkSourceView *view)
702 {
703 	g_return_val_if_fail (view != NULL, FALSE);
704 	g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), FALSE);
705 
706 	return view->show_line_pixmaps;
707 }
708 
gtk_source_view_set_show_line_pixmaps(GtkSourceView * view,gboolean visible)709 void
710 gtk_source_view_set_show_line_pixmaps (GtkSourceView *view,
711 				       gboolean       visible)
712 {
713 	g_return_if_fail (view != NULL);
714 	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
715 
716 	if (visible) {
717 		if (!view->show_line_pixmaps) {
718 			/* Set left margin to minimum width if no margin is
719 			   visible yet. Otherwise, just queue a redraw, so the
720 			   expose handler will automatically adjust the margin. */
721 			if (!view->show_line_numbers)
722 				gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view),
723 								      GTK_TEXT_WINDOW_LEFT,
724 								      MIN_NUMBER_WINDOW_WIDTH);
725 			else
726 				gtk_widget_queue_draw (GTK_WIDGET (view));
727 
728 			view->show_line_pixmaps = visible;
729 		}
730 	} else {
731 		if (view->show_line_pixmaps) {
732 			view->show_line_pixmaps = visible;
733 
734 			/* force expose event, which will adjust margin. */
735 			gtk_widget_queue_draw (GTK_WIDGET (view));
736 		}
737 	}
738 }
739 
gtk_source_view_get_tab_stop(GtkSourceView * view)740 gint
741 gtk_source_view_get_tab_stop (GtkSourceView *view)
742 {
743 	g_return_val_if_fail (view != NULL, FALSE);
744 	g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), FALSE);
745 
746 	return view->tab_stop;
747 }
748 
gtk_source_view_set_tab_stop(GtkSourceView * view,gint tab_stop)749 void
750 gtk_source_view_set_tab_stop (GtkSourceView *view,
751 			      gint           tab_stop)
752 {
753 	PangoTabArray *tabs;
754 
755 	g_return_if_fail (view != NULL);
756 	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
757 
758 	view->tab_stop = tab_stop;
759 	tabs = pango_tab_array_new (1, TRUE);
760 	pango_tab_array_set_tab (tabs, 0, PANGO_TAB_LEFT,
761 				 gtk_source_view_calculate_tab_stop_width (GTK_WIDGET (view),
762 									   tab_stop));
763 	gtk_text_view_set_tabs (GTK_TEXT_VIEW (view), tabs);
764 	pango_tab_array_free (tabs);
765 }
766 
gtk_source_view_get_tab_stop_width(GtkSourceView * view)767 gint
768 gtk_source_view_get_tab_stop_width (GtkSourceView *view)
769 {
770 	PangoTabArray *tabs;
771 	PangoTabAlign alignment;
772 	gint tabstop;
773 
774 	g_return_val_if_fail (view != NULL, FALSE);
775 	g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), FALSE);
776 
777 	tabs = gtk_text_view_get_tabs (GTK_TEXT_VIEW (view));
778 	pango_tab_array_get_tab (tabs, 0, &alignment, &tabstop);
779 	return tabstop;
780 }
781 
gtk_source_view_add_pixbuf(GtkSourceView * view,const gchar * key,GdkPixbuf * pixbuf,gboolean overwrite)782 gboolean
783 gtk_source_view_add_pixbuf (GtkSourceView *view,
784 			    const gchar   *key,
785 			    GdkPixbuf     *pixbuf,
786 			    gboolean       overwrite)
787 {
788 	gpointer data = NULL;
789 	gboolean replaced = FALSE;
790 
791 	g_return_val_if_fail (view != NULL, FALSE);
792 	g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), FALSE);
793 
794 	data = g_hash_table_lookup (view->pixmap_cache, key);
795 	if (data && !overwrite)
796 		return (FALSE);
797 
798 	if (data) {
799 		g_hash_table_remove (view->pixmap_cache, key);
800 		g_object_unref (G_OBJECT (data));
801 		replaced = TRUE;
802 	}
803 	if (pixbuf && GDK_IS_PIXBUF (pixbuf)) {
804 		gint width;
805 		gint height;
806 
807 		width = gdk_pixbuf_get_width (pixbuf);
808 		height = gdk_pixbuf_get_height (pixbuf);
809 		if (width > GUTTER_PIXMAP || height > GUTTER_PIXMAP) {
810 			if (width > GUTTER_PIXMAP)
811 				width = GUTTER_PIXMAP;
812 			if (height > GUTTER_PIXMAP)
813 				height = GUTTER_PIXMAP;
814 			pixbuf = gdk_pixbuf_scale_simple (pixbuf, width, height,
815 							  GDK_INTERP_BILINEAR);
816 		}
817 		g_object_ref (G_OBJECT (pixbuf));
818 		g_hash_table_insert (view->pixmap_cache,
819 				     (gchar *) key,
820 				     (gpointer) pixbuf);
821 	}
822 
823 	return replaced;
824 }
825 
gtk_source_view_get_pixbuf(GtkSourceView * view,const gchar * key)826 GdkPixbuf *
827 gtk_source_view_get_pixbuf (GtkSourceView *view,
828 			    const gchar   *key)
829 {
830 	return g_hash_table_lookup (view->pixmap_cache, key);
831 }
832