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