1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
2 /* gtksourceview.c
3  * This file is part of GtkSourceView
4  *
5  * Copyright (C) 2001 - Mikael Hermansson <tyan@linux.se> and
6  *                      Chris Phelps <chicane@reninet.com>
7  * Copyright (C) 2002 - Jeroen Zwartepoorte
8  * Copyright (C) 2003 - Gustavo Giráldez and Paolo Maggi
9  *
10  * GtkSourceView is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * GtkSourceView is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include "gtksourceview.h"
30 
31 #include <string.h> /* For strlen */
32 
33 #include <gtk/gtk.h>
34 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-tabs.h>
36 
37 #include "gtksourcebuffer.h"
38 #include "gtksourcebuffer-private.h"
39 #include "gtksourcebufferinternal.h"
40 #include "gtksourceview-i18n.h"
41 #include "gtksourceview-enumtypes.h"
42 #include "gtksourcemark.h"
43 #include "gtksourcemarkattributes.h"
44 #include "gtksourcestylescheme.h"
45 #include "gtksourcecompletionprovider.h"
46 #include "gtksourcecompletion-private.h"
47 #include "gtksourcegutter.h"
48 #include "gtksourcegutter-private.h"
49 #include "gtksourcegutterrendererlines.h"
50 #include "gtksourcegutterrenderermarks.h"
51 #include "gtksourceiter.h"
52 #include "gtksourcesearchcontext.h"
53 #include "gtksourcespacedrawer.h"
54 #include "gtksourcespacedrawer-private.h"
55 
56 /**
57  * SECTION:view
58  * @Short_description: Widget that displays a GtkSourceBuffer
59  * @Title: GtkSourceView
60  * @See_also: #GtkTextView, #GtkSourceBuffer
61  *
62  * #GtkSourceView is the main class of the GtkSourceView library.
63  * Use a #GtkSourceBuffer to display text with a #GtkSourceView.
64  *
65  * This class provides:
66  *  - Show the line numbers;
67  *  - Show a right margin;
68  *  - Highlight the current line;
69  *  - Indentation settings;
70  *  - Configuration for the Home and End keyboard keys;
71  *  - Configure and show line marks;
72  *  - And a few other things.
73  *
74  * An easy way to test all these features is to use the test-widget mini-program
75  * provided in the GtkSourceView repository, in the tests/ directory.
76  *
77  * # GtkSourceView as GtkBuildable
78  *
79  * The GtkSourceView implementation of the #GtkBuildable interface exposes the
80  * #GtkSourceView:completion object with the internal-child "completion".
81  *
82  * An example of a UI definition fragment with GtkSourceView:
83  * |[
84  * <object class="GtkSourceView" id="source_view">
85  *   <property name="tab_width">4</property>
86  *   <property name="auto_indent">True</property>
87  *   <child internal-child="completion">
88  *     <object class="GtkSourceCompletion">
89  *       <property name="select_on_show">False</property>
90  *     </object>
91  *   </child>
92  * </object>
93  * ]|
94  */
95 
96 /*
97 #define ENABLE_DEBUG
98 */
99 #undef ENABLE_DEBUG
100 
101 /*
102 #define ENABLE_PROFILE
103 */
104 #undef ENABLE_PROFILE
105 
106 #ifdef ENABLE_DEBUG
107 #define DEBUG(x) (x)
108 #else
109 #define DEBUG(x)
110 #endif
111 
112 #ifdef ENABLE_PROFILE
113 #define PROFILE(x) (x)
114 #else
115 #define PROFILE(x)
116 #endif
117 
118 #define GUTTER_PIXMAP 			16
119 #define DEFAULT_TAB_WIDTH 		8
120 #define MAX_TAB_WIDTH			32
121 #define MAX_INDENT_WIDTH		32
122 
123 #define DEFAULT_RIGHT_MARGIN_POSITION	80
124 #define MAX_RIGHT_MARGIN_POSITION	1000
125 
126 #define RIGHT_MARGIN_LINE_ALPHA		40
127 #define RIGHT_MARGIN_OVERLAY_ALPHA	15
128 
129 enum
130 {
131 	UNDO,
132 	REDO,
133 	SHOW_COMPLETION,
134 	LINE_MARK_ACTIVATED,
135 	MOVE_LINES,
136 	MOVE_WORDS,
137 	SMART_HOME_END,
138 	MOVE_TO_MATCHING_BRACKET,
139 	CHANGE_NUMBER,
140 	CHANGE_CASE,
141 	JOIN_LINES,
142 	N_SIGNALS
143 };
144 
145 enum
146 {
147 	PROP_0,
148 	PROP_COMPLETION,
149 	PROP_SHOW_LINE_NUMBERS,
150 	PROP_SHOW_LINE_MARKS,
151 	PROP_TAB_WIDTH,
152 	PROP_INDENT_WIDTH,
153 	PROP_AUTO_INDENT,
154 	PROP_INSERT_SPACES,
155 	PROP_SHOW_RIGHT_MARGIN,
156 	PROP_RIGHT_MARGIN_POSITION,
157 	PROP_SMART_HOME_END,
158 	PROP_HIGHLIGHT_CURRENT_LINE,
159 	PROP_INDENT_ON_TAB,
160 	PROP_DRAW_SPACES,
161 	PROP_BACKGROUND_PATTERN,
162 	PROP_SMART_BACKSPACE,
163 	PROP_SPACE_DRAWER
164 };
165 
166 struct _GtkSourceViewPrivate
167 {
168 	GtkSourceStyleScheme *style_scheme;
169 	GdkRGBA *right_margin_line_color;
170 	GdkRGBA *right_margin_overlay_color;
171 
172 	GtkSourceSpaceDrawer *space_drawer;
173 
174 	GHashTable *mark_categories;
175 
176 	GtkSourceBuffer *source_buffer;
177 
178 	GtkSourceGutter *left_gutter;
179 	GtkSourceGutter *right_gutter;
180 
181 	GtkSourceGutterRenderer *line_renderer;
182 	GtkSourceGutterRenderer *marks_renderer;
183 
184 	GdkRGBA current_line_color;
185 
186 	GtkSourceCompletion *completion;
187 
188 	guint right_margin_pos;
189 	gint cached_right_margin_pos;
190 	guint tab_width;
191 	gint indent_width;
192 	GtkSourceSmartHomeEndType smart_home_end;
193 	GtkSourceBackgroundPatternType background_pattern;
194 	GdkRGBA background_pattern_color;
195 
196 	guint tabs_set : 1;
197 	guint show_line_numbers : 1;
198 	guint show_line_marks : 1;
199 	guint auto_indent : 1;
200 	guint insert_spaces : 1;
201 	guint highlight_current_line : 1;
202 	guint indent_on_tab : 1;
203 	guint show_right_margin  : 1;
204 	guint current_line_color_set : 1;
205 	guint background_pattern_color_set : 1;
206 	guint smart_backspace : 1;
207 };
208 
209 typedef struct _MarkCategory MarkCategory;
210 
211 struct _MarkCategory
212 {
213 	GtkSourceMarkAttributes *attributes;
214 	gint priority;
215 };
216 
217 static guint signals[N_SIGNALS];
218 
219 static void gtk_source_view_buildable_interface_init (GtkBuildableIface *iface);
220 
221 G_DEFINE_TYPE_WITH_CODE (GtkSourceView, gtk_source_view, GTK_TYPE_TEXT_VIEW,
222 			 G_ADD_PRIVATE (GtkSourceView)
223 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
224 						gtk_source_view_buildable_interface_init))
225 
226 /* Implement DnD for application/x-color drops */
227 typedef enum _GtkSourceViewDropTypes {
228 	TARGET_COLOR = 200
229 } GtkSourceViewDropTypes;
230 
231 static const GtkTargetEntry drop_types[] = {
232 	{(gchar *)"application/x-color", 0, TARGET_COLOR}
233 };
234 
235 /* Prototypes. */
236 static void 	gtk_source_view_dispose			(GObject            *object);
237 static void 	gtk_source_view_finalize		(GObject            *object);
238 static void	gtk_source_view_undo 			(GtkSourceView      *view);
239 static void	gtk_source_view_redo 			(GtkSourceView      *view);
240 static void	gtk_source_view_show_completion_real	(GtkSourceView      *view);
241 static GtkTextBuffer * gtk_source_view_create_buffer	(GtkTextView        *view);
242 static void	remove_source_buffer			(GtkSourceView      *view);
243 static void	set_source_buffer			(GtkSourceView      *view,
244 							 GtkTextBuffer      *buffer);
245 static void	gtk_source_view_populate_popup 		(GtkTextView        *view,
246 							 GtkWidget          *popup);
247 static void	gtk_source_view_move_cursor		(GtkTextView        *text_view,
248 							 GtkMovementStep     step,
249 							 gint                count,
250 							 gboolean            extend_selection);
251 static void	gtk_source_view_delete_from_cursor	(GtkTextView        *text_view,
252 							 GtkDeleteType       type,
253 							 gint                count);
254 static gboolean gtk_source_view_extend_selection	(GtkTextView            *text_view,
255 							 GtkTextExtendSelection  granularity,
256 							 const GtkTextIter      *location,
257 							 GtkTextIter            *start,
258 							 GtkTextIter            *end);
259 static void 	gtk_source_view_get_lines		(GtkTextView       *text_view,
260 							 gint               first_y,
261 							 gint               last_y,
262 							 GArray            *buffer_coords,
263 							 GArray            *line_heights,
264 							 GArray            *numbers,
265 							 gint              *countp);
266 static gboolean gtk_source_view_draw 			(GtkWidget         *widget,
267 							 cairo_t           *cr);
268 static void	gtk_source_view_move_lines		(GtkSourceView     *view,
269 							 gboolean           copy,
270 							 gint               step);
271 static void	gtk_source_view_move_words		(GtkSourceView     *view,
272 							 gint               step);
273 static gboolean	gtk_source_view_key_press_event		(GtkWidget         *widget,
274 							 GdkEventKey       *event);
275 static void	view_dnd_drop 				(GtkTextView       *view,
276 							 GdkDragContext    *context,
277 							 gint               x,
278 							 gint               y,
279 							 GtkSelectionData  *selection_data,
280 							 guint              info,
281 							 guint              timestamp,
282 							 gpointer           data);
283 static gint	calculate_real_tab_width 		(GtkSourceView     *view,
284 							 guint              tab_size,
285 							 gchar              c);
286 static void	gtk_source_view_set_property		(GObject           *object,
287 							 guint              prop_id,
288 							 const GValue      *value,
289 							 GParamSpec        *pspec);
290 static void	gtk_source_view_get_property		(GObject           *object,
291 							 guint              prop_id,
292 							 GValue            *value,
293 							 GParamSpec        *pspec);
294 static void	gtk_source_view_style_updated		(GtkWidget         *widget);
295 static void	gtk_source_view_update_style_scheme	(GtkSourceView     *view);
296 static void	gtk_source_view_draw_layer		(GtkTextView        *view,
297 							 GtkTextViewLayer   layer,
298 							 cairo_t           *cr);
299 
300 static MarkCategory *mark_category_new                  (GtkSourceMarkAttributes *attributes,
301                                                          gint priority);
302 static void          mark_category_free                 (MarkCategory *category);
303 
304 static void
gtk_source_view_constructed(GObject * object)305 gtk_source_view_constructed (GObject *object)
306 {
307 	GtkSourceView *view = GTK_SOURCE_VIEW (object);
308 
309 	set_source_buffer (view, gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
310 
311 	G_OBJECT_CLASS (gtk_source_view_parent_class)->constructed (object);
312 }
313 
314 static void
gtk_source_view_move_to_matching_bracket(GtkSourceView * view,gboolean extend_selection)315 gtk_source_view_move_to_matching_bracket (GtkSourceView *view,
316 					  gboolean       extend_selection)
317 {
318 	GtkTextView *text_view = GTK_TEXT_VIEW (view);
319 	GtkTextBuffer *buffer;
320 	GtkTextMark *insert_mark;
321 	GtkTextIter insert;
322 	GtkTextIter bracket_match;
323 	GtkSourceBracketMatchType result;
324 
325 	buffer = gtk_text_view_get_buffer (text_view);
326 	insert_mark = gtk_text_buffer_get_insert (buffer);
327 	gtk_text_buffer_get_iter_at_mark (buffer, &insert, insert_mark);
328 
329 	result = _gtk_source_buffer_find_bracket_match (GTK_SOURCE_BUFFER (buffer),
330 							&insert,
331 							NULL,
332 							&bracket_match);
333 
334 	if (result == GTK_SOURCE_BRACKET_MATCH_FOUND)
335 	{
336 		if (extend_selection)
337 		{
338 			gtk_text_buffer_move_mark (buffer, insert_mark, &bracket_match);
339 		}
340 		else
341 		{
342 			gtk_text_buffer_place_cursor (buffer, &bracket_match);
343 		}
344 
345 		gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
346 	}
347 }
348 
349 static void
gtk_source_view_change_number(GtkSourceView * view,gint count)350 gtk_source_view_change_number (GtkSourceView *view,
351 			       gint           count)
352 {
353 	GtkTextView *text_view = GTK_TEXT_VIEW (view);
354 	GtkTextBuffer *buffer;
355 	GtkTextIter start, end;
356 	gchar *str;
357 
358 	buffer = gtk_text_view_get_buffer (text_view);
359 	if (!GTK_SOURCE_IS_BUFFER (buffer))
360 	{
361 		return;
362 	}
363 
364 	if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
365 	{
366 		if (!gtk_text_iter_starts_word (&start))
367 		{
368 			gtk_text_iter_backward_word_start (&start);
369 		}
370 
371 		if (!gtk_text_iter_ends_word (&end))
372 		{
373 			gtk_text_iter_forward_word_end (&end);
374 		}
375 	}
376 
377 	str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
378 
379 	if (str != NULL && *str != '\0')
380 	{
381 		gchar *p;
382 		gint64 n;
383 		glong len;
384 
385 		len = gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&start);
386 		g_assert (len > 0);
387 
388 		n = g_ascii_strtoll (str, &p, 10);
389 
390 		/* do the action only if strtoll succeeds (p != str) and
391 		 * the whole string is the number, e.g. not 123abc
392 		 */
393 		if ((p - str) == len)
394 		{
395 			gchar *newstr;
396 
397 			newstr = g_strdup_printf ("%"G_GINT64_FORMAT, (n + count));
398 
399 			gtk_text_buffer_begin_user_action (buffer);
400 			gtk_text_buffer_delete (buffer, &start, &end);
401 			gtk_text_buffer_insert (buffer, &start, newstr, -1);
402 			gtk_text_buffer_end_user_action (buffer);
403 
404 			g_free (newstr);
405 		}
406 
407 		g_free (str);
408 	}
409 }
410 
411 static void
gtk_source_view_change_case(GtkSourceView * view,GtkSourceChangeCaseType case_type)412 gtk_source_view_change_case (GtkSourceView           *view,
413 			     GtkSourceChangeCaseType  case_type)
414 {
415 	GtkSourceBuffer *buffer;
416 	GtkTextIter start, end;
417 
418 	buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
419 
420 	gtk_text_view_reset_im_context (GTK_TEXT_VIEW (view));
421 
422 	if (!gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &start, &end))
423 	{
424 		/* if no selection, change the current char */
425 		gtk_text_iter_forward_char (&end);
426 	}
427 
428 	gtk_source_buffer_change_case (buffer, case_type, &start, &end);
429 }
430 
431 static void
gtk_source_view_join_lines(GtkSourceView * view)432 gtk_source_view_join_lines (GtkSourceView *view)
433 {
434 	GtkSourceBuffer *buffer;
435 	GtkTextIter start;
436 	GtkTextIter end;
437 
438 	buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
439 
440 	gtk_text_view_reset_im_context (GTK_TEXT_VIEW (view));
441 
442 	gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
443 
444 	gtk_source_buffer_join_lines (buffer, &start, &end);
445 }
446 
447 static void
gtk_source_view_class_init(GtkSourceViewClass * klass)448 gtk_source_view_class_init (GtkSourceViewClass *klass)
449 {
450 	GObjectClass *object_class;
451 	GtkTextViewClass *textview_class;
452 	GtkBindingSet *binding_set;
453 	GtkWidgetClass *widget_class;
454 
455 	object_class = G_OBJECT_CLASS (klass);
456 	textview_class = GTK_TEXT_VIEW_CLASS (klass);
457 	widget_class = GTK_WIDGET_CLASS (klass);
458 
459 	object_class->constructed = gtk_source_view_constructed;
460 	object_class->dispose = gtk_source_view_dispose;
461 	object_class->finalize = gtk_source_view_finalize;
462 	object_class->get_property = gtk_source_view_get_property;
463 	object_class->set_property = gtk_source_view_set_property;
464 
465 	widget_class->key_press_event = gtk_source_view_key_press_event;
466 	widget_class->draw = gtk_source_view_draw;
467 	widget_class->style_updated = gtk_source_view_style_updated;
468 
469 	textview_class->populate_popup = gtk_source_view_populate_popup;
470 	textview_class->move_cursor = gtk_source_view_move_cursor;
471 	textview_class->delete_from_cursor = gtk_source_view_delete_from_cursor;
472 	textview_class->extend_selection = gtk_source_view_extend_selection;
473 	textview_class->create_buffer = gtk_source_view_create_buffer;
474 	textview_class->draw_layer = gtk_source_view_draw_layer;
475 
476 	klass->undo = gtk_source_view_undo;
477 	klass->redo = gtk_source_view_redo;
478 	klass->show_completion = gtk_source_view_show_completion_real;
479 	klass->move_lines = gtk_source_view_move_lines;
480 	klass->move_words = gtk_source_view_move_words;
481 
482 	/**
483 	 * GtkSourceView:completion:
484 	 *
485 	 * The completion object associated with the view
486 	 */
487 	g_object_class_install_property (object_class,
488 					 PROP_COMPLETION,
489 					 g_param_spec_object ("completion",
490 							      "Completion",
491 							      "The completion object associated with the view",
492 							      GTK_SOURCE_TYPE_COMPLETION,
493 							      G_PARAM_READABLE |
494 							      G_PARAM_STATIC_STRINGS));
495 
496 	/**
497 	 * GtkSourceView:show-line-numbers:
498 	 *
499 	 * Whether to display line numbers
500 	 */
501 	g_object_class_install_property (object_class,
502 					 PROP_SHOW_LINE_NUMBERS,
503 					 g_param_spec_boolean ("show-line-numbers",
504 							       "Show Line Numbers",
505 							       "Whether to display line numbers",
506 							       FALSE,
507 							       G_PARAM_READWRITE |
508 							       G_PARAM_STATIC_STRINGS));
509 	/**
510 	 * GtkSourceView:show-line-marks:
511 	 *
512 	 * Whether to display line mark pixbufs
513 	 */
514 	g_object_class_install_property (object_class,
515 					 PROP_SHOW_LINE_MARKS,
516 					 g_param_spec_boolean ("show-line-marks",
517 							       "Show Line Marks",
518 							       "Whether to display line mark pixbufs",
519 							       FALSE,
520 							       G_PARAM_READWRITE |
521 							       G_PARAM_STATIC_STRINGS));
522 
523 	/**
524 	 * GtkSourceView:tab-width:
525 	 *
526 	 * Width of a tab character expressed in number of spaces.
527 	 */
528 	g_object_class_install_property (object_class,
529 					 PROP_TAB_WIDTH,
530 					 g_param_spec_uint ("tab-width",
531 							    "Tab Width",
532 							    "Width of a tab character expressed in spaces",
533 							    1,
534 							    MAX_TAB_WIDTH,
535 							    DEFAULT_TAB_WIDTH,
536 							    G_PARAM_READWRITE |
537 							    G_PARAM_STATIC_STRINGS));
538 
539 	/**
540 	 * GtkSourceView:indent-width:
541 	 *
542 	 * Width of an indentation step expressed in number of spaces.
543 	 */
544 	g_object_class_install_property (object_class,
545 					 PROP_INDENT_WIDTH,
546 					 g_param_spec_int ("indent-width",
547 							   "Indent Width",
548 							   "Number of spaces to use for each step of indent",
549 							   -1,
550 							   MAX_INDENT_WIDTH,
551 							   -1,
552 							   G_PARAM_READWRITE |
553 							   G_PARAM_STATIC_STRINGS));
554 
555 	g_object_class_install_property (object_class,
556 					 PROP_AUTO_INDENT,
557 					 g_param_spec_boolean ("auto-indent",
558 							       "Auto Indentation",
559 							       "Whether to enable auto indentation",
560 							       FALSE,
561 							       G_PARAM_READWRITE |
562 							       G_PARAM_STATIC_STRINGS));
563 
564 	g_object_class_install_property (object_class,
565 					 PROP_INSERT_SPACES,
566 					 g_param_spec_boolean ("insert-spaces-instead-of-tabs",
567 							       "Insert Spaces Instead of Tabs",
568 							       "Whether to insert spaces instead of tabs",
569 							       FALSE,
570 							       G_PARAM_READWRITE |
571 							       G_PARAM_STATIC_STRINGS));
572 
573 	/**
574 	 * GtkSourceView:show-right-margin:
575 	 *
576 	 * Whether to display the right margin.
577 	 */
578 	g_object_class_install_property (object_class,
579 					 PROP_SHOW_RIGHT_MARGIN,
580 					 g_param_spec_boolean ("show-right-margin",
581 							       "Show Right Margin",
582 							       "Whether to display the right margin",
583 							       FALSE,
584 							       G_PARAM_READWRITE |
585 							       G_PARAM_STATIC_STRINGS));
586 
587 	/**
588 	 * GtkSourceView:right-margin-position:
589 	 *
590 	 * Position of the right margin.
591 	 */
592 	g_object_class_install_property (object_class,
593 					 PROP_RIGHT_MARGIN_POSITION,
594 					 g_param_spec_uint ("right-margin-position",
595 							    "Right Margin Position",
596 							    "Position of the right margin",
597 							    1,
598 							    MAX_RIGHT_MARGIN_POSITION,
599 							    DEFAULT_RIGHT_MARGIN_POSITION,
600 							    G_PARAM_READWRITE |
601 							    G_PARAM_STATIC_STRINGS));
602 
603 	/**
604 	 * GtkSourceView:smart-home-end:
605 	 *
606 	 * Set the behavior of the HOME and END keys.
607 	 *
608 	 * Since: 2.0
609 	 */
610 	g_object_class_install_property (object_class,
611 					 PROP_SMART_HOME_END,
612 					 g_param_spec_enum ("smart-home-end",
613 							    "Smart Home/End",
614 							    "HOME and END keys move to first/last "
615 							    "non whitespace characters on line before going "
616 							    "to the start/end of the line",
617 							    GTK_SOURCE_TYPE_SMART_HOME_END_TYPE,
618 							    GTK_SOURCE_SMART_HOME_END_DISABLED,
619 							    G_PARAM_READWRITE |
620 							    G_PARAM_STATIC_STRINGS));
621 
622 	g_object_class_install_property (object_class,
623 					 PROP_HIGHLIGHT_CURRENT_LINE,
624 					 g_param_spec_boolean ("highlight-current-line",
625 							       "Highlight current line",
626 							       "Whether to highlight the current line",
627 							       FALSE,
628 							       G_PARAM_READWRITE |
629 							       G_PARAM_STATIC_STRINGS));
630 
631 	g_object_class_install_property (object_class,
632 					 PROP_INDENT_ON_TAB,
633 					 g_param_spec_boolean ("indent-on-tab",
634 							       "Indent on tab",
635 							       "Whether to indent the selected text when the tab key is pressed",
636 							       TRUE,
637 							       G_PARAM_READWRITE |
638 							       G_PARAM_STATIC_STRINGS));
639 
640 	/**
641 	 * GtkSourceView:draw-spaces:
642 	 *
643 	 * Set if and how the spaces should be visualized.
644 	 *
645 	 * For a finer-grained method, there is also the GtkSourceTag's
646 	 * #GtkSourceTag:draw-spaces property.
647 	 *
648 	 * Since: 2.4
649 	 * Deprecated: 3.24: Use the #GtkSourceSpaceDrawer:matrix property
650 	 * instead.
651 	 */
652 	g_object_class_install_property (object_class,
653 					 PROP_DRAW_SPACES,
654 					 g_param_spec_flags ("draw-spaces",
655 							     "Draw Spaces",
656 							     "Set if and how the spaces should be visualized",
657 							     GTK_SOURCE_TYPE_DRAW_SPACES_FLAGS,
658 							     0,
659 							     G_PARAM_READWRITE |
660 							     G_PARAM_STATIC_STRINGS |
661 							     G_PARAM_DEPRECATED));
662 
663 	/**
664 	 * GtkSourceView:background-pattern:
665 	 *
666 	 * Draw a specific background pattern on the view.
667 	 *
668 	 * Since: 3.16
669 	 */
670 	g_object_class_install_property (object_class,
671 					 PROP_BACKGROUND_PATTERN,
672 					 g_param_spec_enum ("background-pattern",
673 							    "Background pattern",
674 							    "Draw a specific background pattern on the view",
675 							    GTK_SOURCE_TYPE_BACKGROUND_PATTERN_TYPE,
676 							    GTK_SOURCE_BACKGROUND_PATTERN_TYPE_NONE,
677 							    G_PARAM_READWRITE |
678 							    G_PARAM_STATIC_STRINGS));
679 
680 	/**
681 	 * GtkSourceView:smart-backspace:
682 	 *
683 	 * Whether smart Backspace should be used.
684 	 *
685 	 * Since: 3.18
686 	 */
687 	g_object_class_install_property (object_class,
688 					 PROP_SMART_BACKSPACE,
689 					 g_param_spec_boolean ("smart-backspace",
690 							       "Smart Backspace",
691 							       "",
692 							       FALSE,
693 							       G_PARAM_READWRITE |
694 							       G_PARAM_STATIC_STRINGS));
695 
696 	/**
697 	 * GtkSourceView:space-drawer:
698 	 *
699 	 * The #GtkSourceSpaceDrawer object associated with the view.
700 	 *
701 	 * Since: 3.24
702 	 */
703 	g_object_class_install_property (object_class,
704 					 PROP_SPACE_DRAWER,
705 					 g_param_spec_object ("space-drawer",
706 							      "Space Drawer",
707 							      "",
708 							      GTK_SOURCE_TYPE_SPACE_DRAWER,
709 							      G_PARAM_READABLE |
710 							      G_PARAM_STATIC_STRINGS));
711 
712 	signals[UNDO] =
713 		g_signal_new ("undo",
714 			      G_TYPE_FROM_CLASS (klass),
715 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
716 			      G_STRUCT_OFFSET (GtkSourceViewClass, undo),
717 			      NULL, NULL, NULL,
718 			      G_TYPE_NONE, 0);
719 
720 	signals[REDO] =
721 		g_signal_new ("redo",
722 			      G_TYPE_FROM_CLASS (klass),
723 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
724 			      G_STRUCT_OFFSET (GtkSourceViewClass, redo),
725 			      NULL, NULL, NULL,
726 			      G_TYPE_NONE, 0);
727 
728 	/**
729 	 * GtkSourceView::show-completion:
730 	 * @view: The #GtkSourceView who emits the signal
731 	 *
732 	 * The ::show-completion signal is a key binding signal which gets
733 	 * emitted when the user requests a completion, by pressing
734 	 * <keycombo><keycap>Control</keycap><keycap>space</keycap></keycombo>.
735 	 *
736 	 * This will create a #GtkSourceCompletionContext with the activation
737 	 * type as %GTK_SOURCE_COMPLETION_ACTIVATION_USER_REQUESTED.
738 	 *
739 	 * Applications should not connect to it, but may emit it with
740 	 * g_signal_emit_by_name() if they need to activate the completion by
741 	 * another means, for example with another key binding or a menu entry.
742 	 */
743 	signals[SHOW_COMPLETION] =
744 		g_signal_new ("show-completion",
745 			      G_TYPE_FROM_CLASS (klass),
746 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
747 			      G_STRUCT_OFFSET (GtkSourceViewClass, show_completion),
748 			      NULL, NULL, NULL,
749 			      G_TYPE_NONE, 0);
750 
751 	/**
752 	 * GtkSourceView::line-mark-activated:
753 	 * @view: the #GtkSourceView
754 	 * @iter: a #GtkTextIter
755 	 * @event: the #GdkEvent that activated the event
756 	 *
757 	 * Emitted when a line mark has been activated (for instance when there
758 	 * was a button press in the line marks gutter). You can use @iter to
759 	 * determine on which line the activation took place.
760 	 */
761 	signals[LINE_MARK_ACTIVATED] =
762 		g_signal_new ("line-mark-activated",
763 			      G_TYPE_FROM_CLASS (klass),
764 			      G_SIGNAL_RUN_LAST,
765 			      G_STRUCT_OFFSET (GtkSourceViewClass, line_mark_activated),
766 			      NULL, NULL, NULL,
767 			      G_TYPE_NONE,
768 			      2,
769 			      GTK_TYPE_TEXT_ITER,
770 			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
771 
772 	/**
773 	 * GtkSourceView::move-lines:
774 	 * @view: the #GtkSourceView which received the signal
775 	 * @copy: %TRUE if the line should be copied, %FALSE if it should be
776 	 *   moved. This parameter is deprecated and will be removed in a later
777 	 *   version, it should be always %FALSE.
778 	 * @count: the number of lines to move over. Only 1 and -1 are
779 	 *   supported.
780 	 *
781 	 * The ::move-lines signal is a keybinding which gets emitted
782 	 * when the user initiates moving a line. The default binding key
783 	 * is Alt+Up/Down arrow. And moves the currently selected lines,
784 	 * or the current line by @count. For the moment, only
785 	 * @count of -1 or 1 is valid.
786 	 *
787 	 * The @copy parameter is deprecated, it has never been used by
788 	 * GtkSourceView (the value is always %FALSE) and was buggy.
789 	 *
790 	 * Since: 2.10
791 	 */
792 	signals[MOVE_LINES] =
793 		g_signal_new ("move-lines",
794 			      G_TYPE_FROM_CLASS (klass),
795 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
796 			      G_STRUCT_OFFSET (GtkSourceViewClass, move_lines),
797 			      NULL, NULL, NULL,
798 			      G_TYPE_NONE, 2,
799 			      G_TYPE_BOOLEAN,
800 			      G_TYPE_INT);
801 
802 	/**
803 	 * GtkSourceView::move-words:
804 	 * @view: the #GtkSourceView which received the signal
805 	 * @count: the number of words to move over
806 	 *
807 	 * The ::move-words signal is a keybinding which gets emitted
808 	 * when the user initiates moving a word. The default binding key
809 	 * is Alt+Left/Right Arrow and moves the current selection, or the current
810 	 * word by one word.
811 	 *
812 	 * Since: 3.0
813 	 */
814 	signals[MOVE_WORDS] =
815 		g_signal_new ("move-words",
816 			      G_TYPE_FROM_CLASS (klass),
817 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
818 			      G_STRUCT_OFFSET (GtkSourceViewClass, move_words),
819 			      NULL, NULL, NULL,
820 			      G_TYPE_NONE, 1,
821 			      G_TYPE_INT);
822 
823 	/**
824 	 * GtkSourceView::smart-home-end:
825 	 * @view: the #GtkSourceView
826 	 * @iter: a #GtkTextIter
827 	 * @count: the count
828 	 *
829 	 * Emitted when a the cursor was moved according to the smart home
830 	 * end setting. The signal is emitted after the cursor is moved, but
831 	 * during the GtkTextView::move-cursor action. This can be used to find
832 	 * out whether the cursor was moved by a normal home/end or by a smart
833 	 * home/end.
834 	 *
835 	 * Since: 3.0
836 	 */
837 	signals[SMART_HOME_END] =
838 		g_signal_new ("smart-home-end",
839 		              G_TYPE_FROM_CLASS (klass),
840 		              G_SIGNAL_RUN_LAST,
841 		              0,
842 		              NULL, NULL, NULL,
843 		              G_TYPE_NONE,
844 		              2,
845 		              GTK_TYPE_TEXT_ITER,
846 		              G_TYPE_INT);
847 
848 	/**
849 	 * GtkSourceView::move-to-matching-bracket:
850 	 * @view: the #GtkSourceView
851 	 * @extend_selection: %TRUE if the move should extend the selection
852 	 *
853 	 * Keybinding signal to move the cursor to the matching bracket.
854 	 *
855 	 * Since: 3.16
856 	 */
857 	signals[MOVE_TO_MATCHING_BRACKET] =
858 		/* we have to do it this way since we do not have any more vfunc slots */
859 		g_signal_new_class_handler ("move-to-matching-bracket",
860 		                            G_TYPE_FROM_CLASS (klass),
861 		                            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
862 		                            G_CALLBACK (gtk_source_view_move_to_matching_bracket),
863 		                            NULL, NULL, NULL,
864 		                            G_TYPE_NONE,
865 		                            1,
866 		                            G_TYPE_BOOLEAN);
867 
868 	/**
869 	 * GtkSourceView::change-number:
870 	 * @view: the #GtkSourceView
871 	 * @count: the number to add to the number at the current position
872 	 *
873 	 * Keybinding signal to edit a number at the current cursor position.
874 	 *
875 	 * Since: 3.16
876 	 */
877 	signals[CHANGE_NUMBER] =
878 		g_signal_new_class_handler ("change-number",
879 		                            G_TYPE_FROM_CLASS (klass),
880 		                            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
881 		                            G_CALLBACK (gtk_source_view_change_number),
882 		                            NULL, NULL, NULL,
883 		                            G_TYPE_NONE,
884 		                            1,
885 		                            G_TYPE_INT);
886 
887 	/**
888 	 * GtkSourceView::change-case:
889 	 * @view: the #GtkSourceView
890 	 * @case_type: the case to use
891 	 *
892 	 * Keybinding signal to change case of the text at the current cursor position.
893 	 *
894 	 * Since: 3.16
895 	 */
896 	signals[CHANGE_CASE] =
897 		g_signal_new_class_handler ("change-case",
898 		                            G_TYPE_FROM_CLASS (klass),
899 		                            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
900 		                            G_CALLBACK (gtk_source_view_change_case),
901 		                            NULL, NULL, NULL,
902 		                            G_TYPE_NONE,
903 		                            1,
904 		                            GTK_SOURCE_TYPE_CHANGE_CASE_TYPE);
905 
906 	/**
907 	 * GtkSourceView::join-lines:
908 	 * @view: the #GtkSourceView
909 	 *
910 	 * Keybinding signal to join the lines currently selected.
911 	 *
912 	 * Since: 3.16
913 	 */
914 	signals[JOIN_LINES] =
915 		g_signal_new_class_handler ("join-lines",
916 		                            G_TYPE_FROM_CLASS (klass),
917 		                            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
918 		                            G_CALLBACK (gtk_source_view_join_lines),
919 		                            NULL, NULL, NULL,
920 		                            G_TYPE_NONE,
921 		                            0);
922 
923 	binding_set = gtk_binding_set_by_class (klass);
924 
925 	gtk_binding_entry_add_signal (binding_set,
926 				      GDK_KEY_z,
927 				      GDK_CONTROL_MASK,
928 				      "undo", 0);
929 	gtk_binding_entry_add_signal (binding_set,
930 				      GDK_KEY_z,
931 				      GDK_CONTROL_MASK | GDK_SHIFT_MASK,
932 				      "redo", 0);
933 	gtk_binding_entry_add_signal (binding_set,
934 				      GDK_KEY_F14,
935 				      0,
936 				      "undo", 0);
937 	gtk_binding_entry_add_signal (binding_set,
938 				      GDK_KEY_space,
939 				      GDK_CONTROL_MASK,
940 				      "show-completion", 0);
941 
942 	gtk_binding_entry_add_signal (binding_set,
943 				      GDK_KEY_Up,
944 				      GDK_MOD1_MASK,
945 				      "move-lines", 2,
946 				      G_TYPE_BOOLEAN, FALSE,
947 				      G_TYPE_INT, -1);
948 	gtk_binding_entry_add_signal (binding_set,
949 				      GDK_KEY_KP_Up,
950 				      GDK_MOD1_MASK,
951 				      "move-lines", 2,
952 				      G_TYPE_BOOLEAN, FALSE,
953 				      G_TYPE_INT, -1);
954 	gtk_binding_entry_add_signal (binding_set,
955 				      GDK_KEY_Down,
956 				      GDK_MOD1_MASK,
957 				      "move-lines", 2,
958 				      G_TYPE_BOOLEAN, FALSE,
959 				      G_TYPE_INT, 1);
960 	gtk_binding_entry_add_signal (binding_set,
961 				      GDK_KEY_KP_Down,
962 				      GDK_MOD1_MASK,
963 				      "move-lines", 2,
964 				      G_TYPE_BOOLEAN, FALSE,
965 				      G_TYPE_INT, 1);
966 
967 	gtk_binding_entry_add_signal (binding_set,
968 				      GDK_KEY_Left,
969 				      GDK_MOD1_MASK,
970 				      "move-words", 1,
971 				      G_TYPE_INT, -1);
972 	gtk_binding_entry_add_signal (binding_set,
973 				      GDK_KEY_KP_Left,
974 				      GDK_MOD1_MASK,
975 				      "move-words", 1,
976 				      G_TYPE_INT, -1);
977 	gtk_binding_entry_add_signal (binding_set,
978 				      GDK_KEY_Right,
979 				      GDK_MOD1_MASK,
980 				      "move-words", 1,
981 				      G_TYPE_INT, 1);
982 	gtk_binding_entry_add_signal (binding_set,
983 				      GDK_KEY_KP_Right,
984 				      GDK_MOD1_MASK,
985 				      "move-words", 1,
986 				      G_TYPE_INT, 1);
987 
988 	gtk_binding_entry_add_signal (binding_set,
989 				      GDK_KEY_Up,
990 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
991 				      "move-viewport", 2,
992 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
993 				      G_TYPE_INT, -1);
994 
995 	gtk_binding_entry_add_signal (binding_set,
996 				      GDK_KEY_KP_Up,
997 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
998 				      "move-viewport", 2,
999 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
1000 				      G_TYPE_INT, -1);
1001 
1002 	gtk_binding_entry_add_signal (binding_set,
1003 				      GDK_KEY_Down,
1004 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1005 				      "move-viewport", 2,
1006 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
1007 				      G_TYPE_INT, 1);
1008 
1009 	gtk_binding_entry_add_signal (binding_set,
1010 				      GDK_KEY_KP_Down,
1011 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1012 				      "move-viewport", 2,
1013 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_STEPS,
1014 				      G_TYPE_INT, 1);
1015 
1016 	gtk_binding_entry_add_signal (binding_set,
1017 				      GDK_KEY_Page_Up,
1018 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1019 				      "move-viewport", 2,
1020 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_PAGES,
1021 				      G_TYPE_INT, -1);
1022 
1023 	gtk_binding_entry_add_signal (binding_set,
1024 				      GDK_KEY_KP_Page_Up,
1025 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1026 				      "move-viewport", 2,
1027 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_PAGES,
1028 				      G_TYPE_INT, -1);
1029 
1030 	gtk_binding_entry_add_signal (binding_set,
1031 				      GDK_KEY_Page_Down,
1032 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1033 				      "move-viewport", 2,
1034 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_PAGES,
1035 				      G_TYPE_INT, 1);
1036 
1037 	gtk_binding_entry_add_signal (binding_set,
1038 				      GDK_KEY_KP_Page_Down,
1039 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1040 				      "move-viewport", 2,
1041 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_PAGES,
1042 				      G_TYPE_INT, 1);
1043 
1044 	gtk_binding_entry_add_signal (binding_set,
1045 				      GDK_KEY_Home,
1046 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1047 				      "move-viewport", 2,
1048 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
1049 				      G_TYPE_INT, -1);
1050 
1051 	gtk_binding_entry_add_signal (binding_set,
1052 				      GDK_KEY_KP_Home,
1053 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1054 				      "move-viewport", 2,
1055 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
1056 				      G_TYPE_INT, -1);
1057 
1058 	gtk_binding_entry_add_signal (binding_set,
1059 				      GDK_KEY_End,
1060 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1061 				      "move-viewport", 2,
1062 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
1063 				      G_TYPE_INT, 1);
1064 
1065 	gtk_binding_entry_add_signal (binding_set,
1066 				      GDK_KEY_KP_End,
1067 				      GDK_MOD1_MASK | GDK_SHIFT_MASK,
1068 				      "move-viewport", 2,
1069 				      GTK_TYPE_SCROLL_STEP, GTK_SCROLL_ENDS,
1070 				      G_TYPE_INT, 1);
1071 
1072 	gtk_binding_entry_add_signal (binding_set,
1073 				      GDK_KEY_percent,
1074 				      GDK_CONTROL_MASK,
1075 				      "move-to-matching-bracket", 1,
1076 				      G_TYPE_BOOLEAN, FALSE);
1077 
1078 	gtk_binding_entry_add_signal (binding_set,
1079 				      GDK_KEY_a,
1080 				      GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1081 				      "change-number", 1,
1082 				      G_TYPE_INT, 1);
1083 
1084 	gtk_binding_entry_add_signal (binding_set,
1085 				      GDK_KEY_x,
1086 				      GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1087 				      "change-number", 1,
1088 				      G_TYPE_INT, -1);
1089 }
1090 
1091 static GObject *
gtk_source_view_buildable_get_internal_child(GtkBuildable * buildable,GtkBuilder * builder,const gchar * childname)1092 gtk_source_view_buildable_get_internal_child (GtkBuildable *buildable,
1093 					      GtkBuilder   *builder,
1094 					      const gchar  *childname)
1095 {
1096 	GtkSourceView *view = GTK_SOURCE_VIEW (buildable);
1097 
1098 	if (g_strcmp0 (childname, "completion") == 0)
1099 	{
1100 		return G_OBJECT (gtk_source_view_get_completion (view));
1101 	}
1102 
1103 	return NULL;
1104 }
1105 
1106 static void
gtk_source_view_buildable_interface_init(GtkBuildableIface * iface)1107 gtk_source_view_buildable_interface_init (GtkBuildableIface *iface)
1108 {
1109 	iface->get_internal_child = gtk_source_view_buildable_get_internal_child;
1110 }
1111 
1112 static void
gtk_source_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1113 gtk_source_view_set_property (GObject      *object,
1114 			      guint         prop_id,
1115 			      const GValue *value,
1116 			      GParamSpec   *pspec)
1117 {
1118 	GtkSourceView *view;
1119 
1120 	g_return_if_fail (GTK_SOURCE_IS_VIEW (object));
1121 
1122 	view = GTK_SOURCE_VIEW (object);
1123 
1124 	switch (prop_id)
1125 	{
1126 		case PROP_SHOW_LINE_NUMBERS:
1127 			gtk_source_view_set_show_line_numbers (view, g_value_get_boolean (value));
1128 			break;
1129 
1130 		case PROP_SHOW_LINE_MARKS:
1131 			gtk_source_view_set_show_line_marks (view, g_value_get_boolean (value));
1132 			break;
1133 
1134 		case PROP_TAB_WIDTH:
1135 			gtk_source_view_set_tab_width (view, g_value_get_uint (value));
1136 			break;
1137 
1138 		case PROP_INDENT_WIDTH:
1139 			gtk_source_view_set_indent_width (view, g_value_get_int (value));
1140 			break;
1141 
1142 		case PROP_AUTO_INDENT:
1143 			gtk_source_view_set_auto_indent (view, g_value_get_boolean (value));
1144 			break;
1145 
1146 		case PROP_INSERT_SPACES:
1147 			gtk_source_view_set_insert_spaces_instead_of_tabs (view, g_value_get_boolean (value));
1148 			break;
1149 
1150 		case PROP_SHOW_RIGHT_MARGIN:
1151 			gtk_source_view_set_show_right_margin (view, g_value_get_boolean (value));
1152 			break;
1153 
1154 		case PROP_RIGHT_MARGIN_POSITION:
1155 			gtk_source_view_set_right_margin_position (view, g_value_get_uint (value));
1156 			break;
1157 
1158 		case PROP_SMART_HOME_END:
1159 			gtk_source_view_set_smart_home_end (view, g_value_get_enum (value));
1160 			break;
1161 
1162 		case PROP_HIGHLIGHT_CURRENT_LINE:
1163 			gtk_source_view_set_highlight_current_line (view, g_value_get_boolean (value));
1164 			break;
1165 
1166 		case PROP_INDENT_ON_TAB:
1167 			gtk_source_view_set_indent_on_tab (view, g_value_get_boolean (value));
1168 			break;
1169 
1170 		case PROP_DRAW_SPACES:
1171 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1172 			gtk_source_view_set_draw_spaces (view, g_value_get_flags (value));
1173 			G_GNUC_END_IGNORE_DEPRECATIONS;
1174 			break;
1175 
1176 		case PROP_BACKGROUND_PATTERN:
1177 			gtk_source_view_set_background_pattern (view, g_value_get_enum (value));
1178 			break;
1179 
1180 		case PROP_SMART_BACKSPACE:
1181 			gtk_source_view_set_smart_backspace (view, g_value_get_boolean (value));
1182 			break;
1183 
1184 		default:
1185 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1186 			break;
1187 	}
1188 }
1189 
1190 static void
gtk_source_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1191 gtk_source_view_get_property (GObject    *object,
1192 			      guint       prop_id,
1193 			      GValue     *value,
1194 			      GParamSpec *pspec)
1195 {
1196 	GtkSourceView *view;
1197 
1198 	g_return_if_fail (GTK_SOURCE_IS_VIEW (object));
1199 
1200 	view = GTK_SOURCE_VIEW (object);
1201 
1202 	switch (prop_id)
1203 	{
1204 		case PROP_COMPLETION:
1205 			g_value_set_object (value, gtk_source_view_get_completion (view));
1206 			break;
1207 
1208 		case PROP_SHOW_LINE_NUMBERS:
1209 			g_value_set_boolean (value, gtk_source_view_get_show_line_numbers (view));
1210 			break;
1211 
1212 		case PROP_SHOW_LINE_MARKS:
1213 			g_value_set_boolean (value, gtk_source_view_get_show_line_marks (view));
1214 			break;
1215 
1216 		case PROP_TAB_WIDTH:
1217 			g_value_set_uint (value, gtk_source_view_get_tab_width (view));
1218 			break;
1219 
1220 		case PROP_INDENT_WIDTH:
1221 			g_value_set_int (value, gtk_source_view_get_indent_width (view));
1222 			break;
1223 
1224 		case PROP_AUTO_INDENT:
1225 			g_value_set_boolean (value, gtk_source_view_get_auto_indent (view));
1226 			break;
1227 
1228 		case PROP_INSERT_SPACES:
1229 			g_value_set_boolean (value, gtk_source_view_get_insert_spaces_instead_of_tabs (view));
1230 			break;
1231 
1232 		case PROP_SHOW_RIGHT_MARGIN:
1233 			g_value_set_boolean (value, gtk_source_view_get_show_right_margin (view));
1234 			break;
1235 
1236 		case PROP_RIGHT_MARGIN_POSITION:
1237 			g_value_set_uint (value, gtk_source_view_get_right_margin_position (view));
1238 			break;
1239 
1240 		case PROP_SMART_HOME_END:
1241 			g_value_set_enum (value, gtk_source_view_get_smart_home_end (view));
1242 			break;
1243 
1244 		case PROP_HIGHLIGHT_CURRENT_LINE:
1245 			g_value_set_boolean (value, gtk_source_view_get_highlight_current_line (view));
1246 			break;
1247 
1248 		case PROP_INDENT_ON_TAB:
1249 			g_value_set_boolean (value, gtk_source_view_get_indent_on_tab (view));
1250 			break;
1251 
1252 		case PROP_DRAW_SPACES:
1253 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1254 			g_value_set_flags (value, gtk_source_view_get_draw_spaces (view));
1255 			G_GNUC_END_IGNORE_DEPRECATIONS;
1256 			break;
1257 
1258 		case PROP_BACKGROUND_PATTERN:
1259 			g_value_set_enum (value, gtk_source_view_get_background_pattern (view));
1260 			break;
1261 
1262 		case PROP_SMART_BACKSPACE:
1263 			g_value_set_boolean (value, gtk_source_view_get_smart_backspace (view));
1264 			break;
1265 
1266 		case PROP_SPACE_DRAWER:
1267 			g_value_set_object (value, gtk_source_view_get_space_drawer (view));
1268 			break;
1269 
1270 		default:
1271 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1272 			break;
1273 	}
1274 }
1275 
1276 static void
space_drawer_notify_cb(GtkSourceSpaceDrawer * space_drawer,GParamSpec * pspec,GtkSourceView * view)1277 space_drawer_notify_cb (GtkSourceSpaceDrawer *space_drawer,
1278 			GParamSpec           *pspec,
1279 			GtkSourceView        *view)
1280 {
1281 	gtk_widget_queue_draw (GTK_WIDGET (view));
1282 	g_object_notify (G_OBJECT (view), "draw-spaces");
1283 }
1284 
1285 static void
notify_buffer_cb(GtkSourceView * view)1286 notify_buffer_cb (GtkSourceView *view)
1287 {
1288 	set_source_buffer (view, gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
1289 }
1290 
1291 static void
gtk_source_view_init(GtkSourceView * view)1292 gtk_source_view_init (GtkSourceView *view)
1293 {
1294 	GtkStyleContext *context;
1295 
1296 	GtkTargetList *target_list;
1297 
1298 	view->priv = gtk_source_view_get_instance_private (view);
1299 
1300 	view->priv->tab_width = DEFAULT_TAB_WIDTH;
1301 	view->priv->tabs_set = FALSE;
1302 	view->priv->indent_width = -1;
1303 	view->priv->indent_on_tab = TRUE;
1304 	view->priv->smart_home_end = GTK_SOURCE_SMART_HOME_END_DISABLED;
1305 	view->priv->right_margin_pos = DEFAULT_RIGHT_MARGIN_POSITION;
1306 	view->priv->cached_right_margin_pos = -1;
1307 
1308 	gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 2);
1309 	gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 2);
1310 
1311 	view->priv->right_margin_line_color = NULL;
1312 	view->priv->right_margin_overlay_color = NULL;
1313 
1314 	view->priv->space_drawer = gtk_source_space_drawer_new ();
1315 	g_signal_connect_object (view->priv->space_drawer,
1316 				 "notify",
1317 				 G_CALLBACK (space_drawer_notify_cb),
1318 				 view,
1319 				 0);
1320 
1321 	view->priv->mark_categories = g_hash_table_new_full (g_str_hash,
1322 	                                                     g_str_equal,
1323 	                                                     (GDestroyNotify) g_free,
1324 	                                                     (GDestroyNotify) mark_category_free);
1325 
1326 	target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (view));
1327 	g_return_if_fail (target_list != NULL);
1328 
1329 	gtk_target_list_add_table (target_list, drop_types, G_N_ELEMENTS (drop_types));
1330 
1331 	gtk_widget_set_has_tooltip (GTK_WIDGET (view), TRUE);
1332 
1333 	g_signal_connect (view,
1334 			  "drag_data_received",
1335 			  G_CALLBACK (view_dnd_drop),
1336 			  NULL);
1337 
1338 	g_signal_connect (view,
1339 			  "notify::buffer",
1340 			  G_CALLBACK (notify_buffer_cb),
1341 			  NULL);
1342 
1343 	context = gtk_widget_get_style_context (GTK_WIDGET (view));
1344 	gtk_style_context_add_class (context, "sourceview");
1345 }
1346 
1347 static void
gtk_source_view_dispose(GObject * object)1348 gtk_source_view_dispose (GObject *object)
1349 {
1350 	GtkSourceView *view = GTK_SOURCE_VIEW (object);
1351 
1352 	g_clear_object (&view->priv->completion);
1353 	g_clear_object (&view->priv->left_gutter);
1354 	g_clear_object (&view->priv->right_gutter);
1355 	g_clear_object (&view->priv->style_scheme);
1356 	g_clear_object (&view->priv->space_drawer);
1357 
1358 	remove_source_buffer (view);
1359 
1360 	/* Disconnect notify buffer because the destroy of the textview will set
1361 	 * the buffer to NULL, and we call get_buffer in the notify which would
1362 	 * reinstate a buffer which we don't want.
1363 	 * There is no problem calling g_signal_handlers_disconnect_by_func()
1364 	 * several times (if dispose() is called several times).
1365 	 */
1366 	g_signal_handlers_disconnect_by_func (view, notify_buffer_cb, NULL);
1367 
1368 	G_OBJECT_CLASS (gtk_source_view_parent_class)->dispose (object);
1369 }
1370 
1371 static void
gtk_source_view_finalize(GObject * object)1372 gtk_source_view_finalize (GObject *object)
1373 {
1374 	GtkSourceView *view = GTK_SOURCE_VIEW (object);
1375 
1376 	if (view->priv->right_margin_line_color != NULL)
1377 	{
1378 		gdk_rgba_free (view->priv->right_margin_line_color);
1379 	}
1380 
1381 	if (view->priv->right_margin_overlay_color != NULL)
1382 	{
1383 		gdk_rgba_free (view->priv->right_margin_overlay_color);
1384 	}
1385 
1386 	if (view->priv->mark_categories)
1387 	{
1388 		g_hash_table_destroy (view->priv->mark_categories);
1389 	}
1390 
1391 	G_OBJECT_CLASS (gtk_source_view_parent_class)->finalize (object);
1392 }
1393 
1394 static void
get_visible_region(GtkTextView * text_view,GtkTextIter * start,GtkTextIter * end)1395 get_visible_region (GtkTextView *text_view,
1396 		    GtkTextIter *start,
1397 		    GtkTextIter *end)
1398 {
1399 	GdkRectangle visible_rect;
1400 
1401 	gtk_text_view_get_visible_rect (text_view, &visible_rect);
1402 
1403 	gtk_text_view_get_line_at_y (text_view,
1404 				     start,
1405 				     visible_rect.y,
1406 				     NULL);
1407 
1408 	gtk_text_view_get_line_at_y (text_view,
1409 				     end,
1410 				     visible_rect.y + visible_rect.height,
1411 				     NULL);
1412 
1413 	gtk_text_iter_backward_line (start);
1414 	gtk_text_iter_forward_line (end);
1415 }
1416 
1417 static void
highlight_updated_cb(GtkSourceBuffer * buffer,GtkTextIter * _start,GtkTextIter * _end,GtkTextView * text_view)1418 highlight_updated_cb (GtkSourceBuffer *buffer,
1419 		      GtkTextIter     *_start,
1420 		      GtkTextIter     *_end,
1421 		      GtkTextView     *text_view)
1422 {
1423 	GtkTextIter start;
1424 	GtkTextIter end;
1425 	GtkTextIter visible_start;
1426 	GtkTextIter visible_end;
1427 	GtkTextIter intersect_start;
1428 	GtkTextIter intersect_end;
1429 
1430 #if 0
1431 	{
1432 		static gint nth_call = 0;
1433 
1434 		g_message ("%s(view=%p) %d [%d-%d]",
1435 			   G_STRFUNC,
1436 			   text_view,
1437 			   ++nth_call,
1438 			   gtk_text_iter_get_offset (_start),
1439 			   gtk_text_iter_get_offset (_end));
1440 	}
1441 #endif
1442 
1443 	start = *_start;
1444 	end = *_end;
1445 	gtk_text_iter_order (&start, &end);
1446 
1447 	get_visible_region (text_view, &visible_start, &visible_end);
1448 
1449 	if (gtk_text_iter_compare (&end, &visible_start) < 0 ||
1450 	    gtk_text_iter_compare (&visible_end, &start) < 0)
1451 	{
1452 		return;
1453 	}
1454 
1455 	if (gtk_text_iter_compare (&start, &visible_start) < 0)
1456 	{
1457 		intersect_start = visible_start;
1458 	}
1459 	else
1460 	{
1461 		intersect_start = start;
1462 	}
1463 
1464 	if (gtk_text_iter_compare (&visible_end, &end) < 0)
1465 	{
1466 		intersect_end = visible_end;
1467 	}
1468 	else
1469 	{
1470 		intersect_end = end;
1471 	}
1472 
1473 	/* GtkSourceContextEngine sends the highlight-updated signal to notify
1474 	 * the view, and in the view (here) we tell the ContextEngine to update
1475 	 * the highlighting, but only in the visible area. It seems that the
1476 	 * purpose is to reduce the number of tags that the ContextEngine
1477 	 * applies to the buffer.
1478 	 *
1479 	 * A previous implementation of this signal handler queued a redraw on
1480 	 * the view with gtk_widget_queue_draw_area(), instead of calling
1481 	 * directly _gtk_source_buffer_update_syntax_highlight(). The ::draw
1482 	 * handler also calls _gtk_source_buffer_update_syntax_highlight(), so
1483 	 * this had the desired effect, but it was less clear.
1484 	 * See the Git commit 949cd128064201935f90d999544e6a19f8e3baa6.
1485 	 * And: https://bugzilla.gnome.org/show_bug.cgi?id=767565
1486 	 */
1487 	_gtk_source_buffer_update_syntax_highlight (buffer,
1488 						    &intersect_start,
1489 						    &intersect_end,
1490 						    FALSE);
1491 }
1492 
1493 static void
search_start_cb(GtkSourceBufferInternal * buffer_internal,GtkSourceSearchContext * search_context,GtkSourceView * view)1494 search_start_cb (GtkSourceBufferInternal *buffer_internal,
1495 		 GtkSourceSearchContext  *search_context,
1496 		 GtkSourceView           *view)
1497 {
1498 	GtkTextIter visible_start;
1499 	GtkTextIter visible_end;
1500 	GtkSourceBuffer *buffer_search;
1501 
1502 	get_visible_region (GTK_TEXT_VIEW (view), &visible_start, &visible_end);
1503 
1504 	buffer_search = gtk_source_search_context_get_buffer (search_context);
1505 	g_assert (buffer_search == view->priv->source_buffer);
1506 
1507 	_gtk_source_search_context_update_highlight (search_context,
1508 						     &visible_start,
1509 						     &visible_end,
1510 						     FALSE);
1511 }
1512 
1513 static void
source_mark_updated_cb(GtkSourceBuffer * buffer,GtkSourceMark * mark,GtkTextView * text_view)1514 source_mark_updated_cb (GtkSourceBuffer *buffer,
1515 			GtkSourceMark   *mark,
1516 			GtkTextView     *text_view)
1517 {
1518 	/* TODO do something more intelligent here, namely
1519 	 * invalidate only the area under the mark if possible */
1520 	gtk_widget_queue_draw (GTK_WIDGET (text_view));
1521 }
1522 
1523 static void
buffer_style_scheme_changed_cb(GtkSourceBuffer * buffer,GParamSpec * pspec,GtkSourceView * view)1524 buffer_style_scheme_changed_cb (GtkSourceBuffer *buffer,
1525 				GParamSpec      *pspec,
1526 				GtkSourceView   *view)
1527 {
1528 	gtk_source_view_update_style_scheme (view);
1529 }
1530 
1531 static void
implicit_trailing_newline_changed_cb(GtkSourceBuffer * buffer,GParamSpec * pspec,GtkSourceView * view)1532 implicit_trailing_newline_changed_cb (GtkSourceBuffer *buffer,
1533 				      GParamSpec      *pspec,
1534 				      GtkSourceView   *view)
1535 {
1536 	/* For drawing or not a trailing newline. */
1537 	gtk_widget_queue_draw (GTK_WIDGET (view));
1538 }
1539 
1540 static void
remove_source_buffer(GtkSourceView * view)1541 remove_source_buffer (GtkSourceView *view)
1542 {
1543 	if (view->priv->source_buffer != NULL)
1544 	{
1545 		GtkSourceBufferInternal *buffer_internal;
1546 
1547 		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
1548 						      highlight_updated_cb,
1549 						      view);
1550 
1551 		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
1552 						      source_mark_updated_cb,
1553 						      view);
1554 
1555 		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
1556 						      buffer_style_scheme_changed_cb,
1557 						      view);
1558 
1559 		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
1560 						      implicit_trailing_newline_changed_cb,
1561 						      view);
1562 
1563 		buffer_internal = _gtk_source_buffer_internal_get_from_buffer (view->priv->source_buffer);
1564 
1565 		g_signal_handlers_disconnect_by_func (buffer_internal,
1566 						      search_start_cb,
1567 						      view);
1568 
1569 		g_object_unref (view->priv->source_buffer);
1570 		view->priv->source_buffer = NULL;
1571 	}
1572 }
1573 
1574 static void
set_source_buffer(GtkSourceView * view,GtkTextBuffer * buffer)1575 set_source_buffer (GtkSourceView *view,
1576 		   GtkTextBuffer *buffer)
1577 {
1578 	if (buffer == (GtkTextBuffer*) view->priv->source_buffer)
1579 	{
1580 		return;
1581 	}
1582 
1583 	remove_source_buffer (view);
1584 
1585 	if (GTK_SOURCE_IS_BUFFER (buffer))
1586 	{
1587 		GtkSourceBufferInternal *buffer_internal;
1588 
1589 		view->priv->source_buffer = g_object_ref (buffer);
1590 
1591 		g_signal_connect (buffer,
1592 				  "highlight-updated",
1593 				  G_CALLBACK (highlight_updated_cb),
1594 				  view);
1595 
1596 		g_signal_connect (buffer,
1597 				  "source-mark-updated",
1598 				  G_CALLBACK (source_mark_updated_cb),
1599 				  view);
1600 
1601 		g_signal_connect (buffer,
1602 				  "notify::style-scheme",
1603 				  G_CALLBACK (buffer_style_scheme_changed_cb),
1604 				  view);
1605 
1606 		g_signal_connect (buffer,
1607 				  "notify::implicit-trailing-newline",
1608 				  G_CALLBACK (implicit_trailing_newline_changed_cb),
1609 				  view);
1610 
1611 		buffer_internal = _gtk_source_buffer_internal_get_from_buffer (view->priv->source_buffer);
1612 
1613 		g_signal_connect (buffer_internal,
1614 				  "search-start",
1615 				  G_CALLBACK (search_start_cb),
1616 				  view);
1617 	}
1618 
1619 	gtk_source_view_update_style_scheme (view);
1620 }
1621 
1622 static void
scroll_to_insert(GtkSourceView * view,GtkTextBuffer * buffer)1623 scroll_to_insert (GtkSourceView *view,
1624 		  GtkTextBuffer *buffer)
1625 {
1626 	GtkTextMark *insert;
1627 	GtkTextIter iter;
1628 	GdkRectangle visible, location;
1629 
1630 	insert = gtk_text_buffer_get_insert (buffer);
1631 	gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
1632 
1633 	gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (view), &visible);
1634 	gtk_text_view_get_iter_location (GTK_TEXT_VIEW (view), &iter, &location);
1635 
1636 	if (location.y < visible.y || visible.y + visible.height < location.y)
1637 	{
1638 		gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
1639 					      insert,
1640 					      0.0,
1641 					      TRUE,
1642 					      0.5, 0.5);
1643 	}
1644 	else if (location.x < visible.x || visible.x + visible.width < location.x)
1645 	{
1646 		gdouble position;
1647 		GtkAdjustment *adjustment;
1648 
1649 		/* We revert the vertical position of the view because
1650 		 * _scroll_to_iter will cause it to move and the
1651 		 * insert mark is already visible vertically. */
1652 
1653 		adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view));
1654 		position = gtk_adjustment_get_value (adjustment);
1655 
1656 		/* Must use _to_iter as _to_mark scrolls in an
1657 		 * idle handler and would prevent use from
1658 		 * reverting the vertical position of the view. */
1659 		gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view),
1660 					      &iter,
1661 					      0.0,
1662 					      TRUE,
1663 					      0.5, 0.0);
1664 
1665 		gtk_adjustment_set_value (adjustment, position);
1666 	}
1667 }
1668 
1669 static void
gtk_source_view_undo(GtkSourceView * view)1670 gtk_source_view_undo (GtkSourceView *view)
1671 {
1672 	GtkTextBuffer *buffer;
1673 
1674 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
1675 
1676 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
1677 
1678 	if (gtk_text_view_get_editable (GTK_TEXT_VIEW (view)) &&
1679 	    GTK_SOURCE_IS_BUFFER (buffer) &&
1680 	    gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (buffer)))
1681 	{
1682 		gtk_source_buffer_undo (GTK_SOURCE_BUFFER (buffer));
1683 		scroll_to_insert (view, buffer);
1684 	}
1685 }
1686 
1687 static void
gtk_source_view_redo(GtkSourceView * view)1688 gtk_source_view_redo (GtkSourceView *view)
1689 {
1690 	GtkTextBuffer *buffer;
1691 
1692 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
1693 
1694 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
1695 
1696 	if (gtk_text_view_get_editable (GTK_TEXT_VIEW (view)) &&
1697 	    GTK_SOURCE_IS_BUFFER (buffer) &&
1698 	    gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (buffer)))
1699 	{
1700 		gtk_source_buffer_redo (GTK_SOURCE_BUFFER (buffer));
1701 		scroll_to_insert (view, buffer);
1702 	}
1703 }
1704 
1705 static void
gtk_source_view_show_completion_real(GtkSourceView * view)1706 gtk_source_view_show_completion_real (GtkSourceView *view)
1707 {
1708 	GtkSourceCompletion *completion;
1709 	GtkSourceCompletionContext *context;
1710 
1711 	completion = gtk_source_view_get_completion (view);
1712 	context = gtk_source_completion_create_context (completion, NULL);
1713 
1714 	gtk_source_completion_show (completion,
1715 	                            gtk_source_completion_get_providers (completion),
1716 	                            context);
1717 }
1718 
1719 static void
menu_item_activate_change_case_cb(GtkWidget * menu_item,GtkTextView * text_view)1720 menu_item_activate_change_case_cb (GtkWidget   *menu_item,
1721 				   GtkTextView *text_view)
1722 {
1723 	GtkTextBuffer *buffer;
1724 	GtkTextIter start, end;
1725 
1726 	buffer = gtk_text_view_get_buffer (text_view);
1727 	if (!GTK_SOURCE_IS_BUFFER (buffer))
1728 	{
1729 		return;
1730 	}
1731 
1732 	if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
1733 	{
1734 		GtkSourceChangeCaseType case_type;
1735 
1736 		case_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "change-case"));
1737 		gtk_source_buffer_change_case (GTK_SOURCE_BUFFER (buffer), case_type, &start, &end);
1738 	}
1739 }
1740 
1741 static void
menu_item_activate_cb(GtkWidget * menu_item,GtkTextView * text_view)1742 menu_item_activate_cb (GtkWidget   *menu_item,
1743 		       GtkTextView *text_view)
1744 {
1745 	const gchar *gtksignal;
1746 
1747 	gtksignal = g_object_get_data (G_OBJECT (menu_item), "gtk-signal");
1748 	g_signal_emit_by_name (G_OBJECT (text_view), gtksignal);
1749 }
1750 
1751 static void
gtk_source_view_populate_popup(GtkTextView * text_view,GtkWidget * popup)1752 gtk_source_view_populate_popup (GtkTextView *text_view,
1753 				GtkWidget   *popup)
1754 {
1755 	GtkTextBuffer *buffer;
1756 	GtkMenuShell *menu;
1757 	GtkWidget *menu_item;
1758 	GtkMenuShell *case_menu;
1759 
1760 	buffer = gtk_text_view_get_buffer (text_view);
1761 	if (!GTK_SOURCE_IS_BUFFER (buffer))
1762 	{
1763 		return;
1764 	}
1765 
1766 	if (!GTK_IS_MENU_SHELL (popup))
1767 	{
1768 		return;
1769 	}
1770 
1771 	menu = GTK_MENU_SHELL (popup);
1772 
1773 	if (_gtk_source_buffer_is_undo_redo_enabled (GTK_SOURCE_BUFFER (buffer)))
1774 	{
1775 		/* separator */
1776 		menu_item = gtk_separator_menu_item_new ();
1777 		gtk_menu_shell_prepend (menu, menu_item);
1778 		gtk_widget_show (menu_item);
1779 
1780 		/* create redo menu_item. */
1781 		menu_item = gtk_menu_item_new_with_mnemonic (_("_Redo"));
1782 		g_object_set_data (G_OBJECT (menu_item), "gtk-signal", (gpointer)"redo");
1783 		g_signal_connect (G_OBJECT (menu_item), "activate",
1784 				  G_CALLBACK (menu_item_activate_cb), text_view);
1785 		gtk_menu_shell_prepend (menu, menu_item);
1786 		gtk_widget_set_sensitive (menu_item,
1787 					  (gtk_text_view_get_editable (text_view) &&
1788 					   gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (buffer))));
1789 		gtk_widget_show (menu_item);
1790 
1791 		/* create undo menu_item. */
1792 		menu_item = gtk_menu_item_new_with_mnemonic (_("_Undo"));
1793 		g_object_set_data (G_OBJECT (menu_item), "gtk-signal", (gpointer)"undo");
1794 		g_signal_connect (G_OBJECT (menu_item), "activate",
1795 				  G_CALLBACK (menu_item_activate_cb), text_view);
1796 		gtk_menu_shell_prepend (menu, menu_item);
1797 		gtk_widget_set_sensitive (menu_item,
1798 					  (gtk_text_view_get_editable (text_view) &&
1799 					   gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (buffer))));
1800 		gtk_widget_show (menu_item);
1801 	}
1802 
1803 	/* separator */
1804 	menu_item = gtk_separator_menu_item_new ();
1805 	gtk_menu_shell_append (menu, menu_item);
1806 	gtk_widget_show (menu_item);
1807 
1808 	/* create change case menu */
1809 	case_menu = GTK_MENU_SHELL (gtk_menu_new ());
1810 
1811 	menu_item = gtk_menu_item_new_with_mnemonic (_("All _Upper Case"));
1812 	g_object_set_data (G_OBJECT (menu_item), "change-case", GINT_TO_POINTER(GTK_SOURCE_CHANGE_CASE_UPPER));
1813 	g_signal_connect (G_OBJECT (menu_item), "activate",
1814 			  G_CALLBACK (menu_item_activate_change_case_cb), text_view);
1815 	gtk_menu_shell_append (case_menu, menu_item);
1816 	gtk_widget_set_sensitive (menu_item,
1817 				  (gtk_text_view_get_editable (text_view) &&
1818 				   gtk_text_buffer_get_has_selection (buffer)));
1819 	gtk_widget_show (menu_item);
1820 
1821 	menu_item = gtk_menu_item_new_with_mnemonic (_("All _Lower Case"));
1822 	g_object_set_data (G_OBJECT (menu_item), "change-case", GINT_TO_POINTER(GTK_SOURCE_CHANGE_CASE_LOWER));
1823 	g_signal_connect (G_OBJECT (menu_item), "activate",
1824 			  G_CALLBACK (menu_item_activate_change_case_cb), text_view);
1825 	gtk_menu_shell_append (case_menu, menu_item);
1826 	gtk_widget_set_sensitive (menu_item,
1827 				  (gtk_text_view_get_editable (text_view) &&
1828 				   gtk_text_buffer_get_has_selection (buffer)));
1829 	gtk_widget_show (menu_item);
1830 
1831 	menu_item = gtk_menu_item_new_with_mnemonic (_("_Invert Case"));
1832 	g_object_set_data (G_OBJECT (menu_item), "change-case", GINT_TO_POINTER(GTK_SOURCE_CHANGE_CASE_TOGGLE));
1833 	g_signal_connect (G_OBJECT (menu_item), "activate",
1834 			  G_CALLBACK (menu_item_activate_change_case_cb), text_view);
1835 	gtk_menu_shell_append (case_menu, menu_item);
1836 	gtk_widget_set_sensitive (menu_item,
1837 				  (gtk_text_view_get_editable (text_view) &&
1838 				   gtk_text_buffer_get_has_selection (buffer)));
1839 	gtk_widget_show (menu_item);
1840 
1841 	menu_item = gtk_menu_item_new_with_mnemonic (_("_Title Case"));
1842 	g_object_set_data (G_OBJECT (menu_item), "change-case", GINT_TO_POINTER(GTK_SOURCE_CHANGE_CASE_TITLE));
1843 	g_signal_connect (G_OBJECT (menu_item), "activate",
1844 			  G_CALLBACK (menu_item_activate_change_case_cb), text_view);
1845 	gtk_menu_shell_append (case_menu, menu_item);
1846 	gtk_widget_set_sensitive (menu_item,
1847 				  (gtk_text_view_get_editable (text_view) &&
1848 				   gtk_text_buffer_get_has_selection (buffer)));
1849 	gtk_widget_show (menu_item);
1850 
1851 	menu_item = gtk_menu_item_new_with_mnemonic (_("C_hange Case"));
1852 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), GTK_WIDGET (case_menu));
1853 	gtk_menu_shell_append (menu, menu_item);
1854 	gtk_widget_set_sensitive (menu_item,
1855 				  (gtk_text_view_get_editable (text_view) &&
1856 				   gtk_text_buffer_get_has_selection (buffer)));
1857 	gtk_widget_show (menu_item);
1858 }
1859 
1860 static void
move_cursor(GtkTextView * text_view,const GtkTextIter * new_location,gboolean extend_selection)1861 move_cursor (GtkTextView       *text_view,
1862 	     const GtkTextIter *new_location,
1863 	     gboolean           extend_selection)
1864 {
1865 	GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
1866 	GtkTextMark *insert = gtk_text_buffer_get_insert (buffer);
1867 
1868 	if (extend_selection)
1869 	{
1870 		gtk_text_buffer_move_mark (buffer, insert, new_location);
1871 	}
1872 	else
1873 	{
1874 		gtk_text_buffer_place_cursor (buffer, new_location);
1875 	}
1876 
1877 	gtk_text_view_scroll_mark_onscreen (text_view, insert);
1878 }
1879 
1880 static void
move_to_first_char(GtkTextView * text_view,GtkTextIter * iter,gboolean display_line)1881 move_to_first_char (GtkTextView *text_view,
1882 		    GtkTextIter *iter,
1883 		    gboolean     display_line)
1884 {
1885 	GtkTextIter last;
1886 
1887 	last = *iter;
1888 
1889 	if (display_line)
1890 	{
1891 		gtk_text_view_backward_display_line_start (text_view, iter);
1892 		gtk_text_view_forward_display_line_end (text_view, &last);
1893 	}
1894 	else
1895 	{
1896 		gtk_text_iter_set_line_offset (iter, 0);
1897 
1898 		if (!gtk_text_iter_ends_line (&last))
1899 		{
1900 			gtk_text_iter_forward_to_line_end (&last);
1901 		}
1902 	}
1903 
1904 
1905 	while (gtk_text_iter_compare (iter, &last) < 0)
1906 	{
1907 		gunichar c;
1908 
1909 		c = gtk_text_iter_get_char (iter);
1910 
1911 		if (g_unichar_isspace (c))
1912 		{
1913 			if (!gtk_text_iter_forward_visible_cursor_position (iter))
1914 			{
1915 				break;
1916 			}
1917 		}
1918 		else
1919 		{
1920 			break;
1921 		}
1922 	}
1923 }
1924 
1925 static void
move_to_last_char(GtkTextView * text_view,GtkTextIter * iter,gboolean display_line)1926 move_to_last_char (GtkTextView *text_view,
1927 		   GtkTextIter *iter,
1928 		   gboolean     display_line)
1929 {
1930 	GtkTextIter first;
1931 
1932 	first = *iter;
1933 
1934 	if (display_line)
1935 	{
1936 		gtk_text_view_forward_display_line_end (text_view, iter);
1937 		gtk_text_view_backward_display_line_start (text_view, &first);
1938 	}
1939 	else
1940 	{
1941 		if (!gtk_text_iter_ends_line (iter))
1942 		{
1943 			gtk_text_iter_forward_to_line_end (iter);
1944 		}
1945 
1946 		gtk_text_iter_set_line_offset (&first, 0);
1947 	}
1948 
1949 	while (gtk_text_iter_compare (iter, &first) > 0)
1950 	{
1951 		gunichar c;
1952 
1953 		if (!gtk_text_iter_backward_visible_cursor_position (iter))
1954 		{
1955 			break;
1956 		}
1957 
1958 		c = gtk_text_iter_get_char (iter);
1959 
1960 		if (!g_unichar_isspace (c))
1961 		{
1962 			/* We've gone one cursor position too far. */
1963 			gtk_text_iter_forward_visible_cursor_position (iter);
1964 			break;
1965 		}
1966 	}
1967 }
1968 
1969 static void
do_cursor_move_home_end(GtkTextView * text_view,GtkTextIter * cur,GtkTextIter * iter,gboolean extend_selection,gint count)1970 do_cursor_move_home_end (GtkTextView *text_view,
1971 			 GtkTextIter *cur,
1972 			 GtkTextIter *iter,
1973 			 gboolean     extend_selection,
1974 			 gint         count)
1975 {
1976 	/* if we are clearing selection, we need to move_cursor even
1977 	 * if we are at proper iter because selection_bound may need
1978 	 * to be moved */
1979 	if (!gtk_text_iter_equal (cur, iter) || !extend_selection)
1980 	{
1981 		move_cursor (text_view, iter, extend_selection);
1982 		g_signal_emit (text_view, signals[SMART_HOME_END], 0, iter, count);
1983 	}
1984 }
1985 
1986 /* Returns %TRUE if handled. */
1987 static gboolean
move_cursor_smart_home_end(GtkTextView * text_view,GtkMovementStep step,gint count,gboolean extend_selection)1988 move_cursor_smart_home_end (GtkTextView     *text_view,
1989 			    GtkMovementStep  step,
1990 			    gint             count,
1991 			    gboolean         extend_selection)
1992 {
1993 	GtkSourceView *source_view = GTK_SOURCE_VIEW (text_view);
1994 	GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
1995 	gboolean move_display_line;
1996 	GtkTextMark *mark;
1997 	GtkTextIter cur;
1998 	GtkTextIter iter;
1999 
2000 	g_assert (step == GTK_MOVEMENT_DISPLAY_LINE_ENDS ||
2001 		  step == GTK_MOVEMENT_PARAGRAPH_ENDS);
2002 
2003 	move_display_line = step == GTK_MOVEMENT_DISPLAY_LINE_ENDS;
2004 
2005 	mark = gtk_text_buffer_get_insert (buffer);
2006 	gtk_text_buffer_get_iter_at_mark (buffer, &cur, mark);
2007 	iter = cur;
2008 
2009 	if (count == -1)
2010 	{
2011 		gboolean at_home;
2012 
2013 		move_to_first_char (text_view, &iter, move_display_line);
2014 
2015 		if (move_display_line)
2016 		{
2017 			at_home = gtk_text_view_starts_display_line (text_view, &cur);
2018 		}
2019 		else
2020 		{
2021 			at_home = gtk_text_iter_starts_line (&cur);
2022 		}
2023 
2024 		switch (source_view->priv->smart_home_end)
2025 		{
2026 			case GTK_SOURCE_SMART_HOME_END_BEFORE:
2027 				if (!gtk_text_iter_equal (&cur, &iter) || at_home)
2028 				{
2029 					do_cursor_move_home_end (text_view,
2030 					                         &cur,
2031 					                         &iter,
2032 					                         extend_selection,
2033 					                         count);
2034 					return TRUE;
2035 				}
2036 				break;
2037 
2038 			case GTK_SOURCE_SMART_HOME_END_AFTER:
2039 				if (at_home)
2040 				{
2041 					do_cursor_move_home_end (text_view,
2042 					                         &cur,
2043 					                         &iter,
2044 					                         extend_selection,
2045 					                         count);
2046 					return TRUE;
2047 				}
2048 				break;
2049 
2050 			case GTK_SOURCE_SMART_HOME_END_ALWAYS:
2051 				do_cursor_move_home_end (text_view,
2052 				                         &cur,
2053 				                         &iter,
2054 				                         extend_selection,
2055 				                         count);
2056 				return TRUE;
2057 
2058 			case GTK_SOURCE_SMART_HOME_END_DISABLED:
2059 			default:
2060 				break;
2061 		}
2062 	}
2063 	else if (count == 1)
2064 	{
2065 		gboolean at_end;
2066 
2067 		move_to_last_char (text_view, &iter, move_display_line);
2068 
2069 		if (move_display_line)
2070 		{
2071 			GtkTextIter display_end;
2072 			display_end = cur;
2073 
2074 			gtk_text_view_forward_display_line_end (text_view, &display_end);
2075 			at_end = gtk_text_iter_equal (&cur, &display_end);
2076 		}
2077 		else
2078 		{
2079 			at_end = gtk_text_iter_ends_line (&cur);
2080 		}
2081 
2082 		switch (source_view->priv->smart_home_end)
2083 		{
2084 			case GTK_SOURCE_SMART_HOME_END_BEFORE:
2085 				if (!gtk_text_iter_equal (&cur, &iter) || at_end)
2086 				{
2087 					do_cursor_move_home_end (text_view,
2088 					                         &cur,
2089 					                         &iter,
2090 					                         extend_selection,
2091 					                         count);
2092 					return TRUE;
2093 				}
2094 				break;
2095 
2096 			case GTK_SOURCE_SMART_HOME_END_AFTER:
2097 				if (at_end)
2098 				{
2099 					do_cursor_move_home_end (text_view,
2100 					                         &cur,
2101 					                         &iter,
2102 					                         extend_selection,
2103 					                         count);
2104 					return TRUE;
2105 				}
2106 				break;
2107 
2108 			case GTK_SOURCE_SMART_HOME_END_ALWAYS:
2109 				do_cursor_move_home_end (text_view,
2110 				                         &cur,
2111 				                         &iter,
2112 				                         extend_selection,
2113 				                         count);
2114 				return TRUE;
2115 
2116 			case GTK_SOURCE_SMART_HOME_END_DISABLED:
2117 			default:
2118 				break;
2119 		}
2120 	}
2121 
2122 	return FALSE;
2123 }
2124 
2125 static void
move_cursor_words(GtkTextView * text_view,gint count,gboolean extend_selection)2126 move_cursor_words (GtkTextView *text_view,
2127 		   gint         count,
2128 		   gboolean     extend_selection)
2129 {
2130 	GtkTextBuffer *buffer;
2131 	GtkTextIter insert;
2132 	GtkTextIter newplace;
2133 	GtkTextIter line_start;
2134 	GtkTextIter line_end;
2135 	gchar *line_text;
2136 
2137 	buffer = gtk_text_view_get_buffer (text_view);
2138 
2139 	gtk_text_buffer_get_iter_at_mark (buffer,
2140 					  &insert,
2141 					  gtk_text_buffer_get_insert (buffer));
2142 
2143 	line_start = line_end = newplace = insert;
2144 
2145 	/* Get the text of the current line for RTL analysis */
2146 	gtk_text_iter_set_line_offset (&line_start, 0);
2147 	gtk_text_iter_forward_line (&line_end);
2148 	line_text = gtk_text_iter_get_visible_text (&line_start, &line_end);
2149 
2150 	/* Swap direction for RTL to maintain visual cursor movement.
2151 	 * Otherwise, cursor will move in opposite direction which is counter
2152 	 * intuitve and causes confusion for RTL users.
2153 	 */
2154 	if (pango_find_base_dir (line_text, -1) == PANGO_DIRECTION_RTL)
2155 	{
2156 		count *= -1;
2157 	}
2158 
2159 	g_free (line_text);
2160 
2161 	if (count < 0)
2162 	{
2163 		if (!_gtk_source_iter_backward_visible_word_starts (&newplace, -count))
2164 		{
2165 			gtk_text_iter_set_line_offset (&newplace, 0);
2166 		}
2167 	}
2168 	else if (count > 0)
2169 	{
2170 		if (!_gtk_source_iter_forward_visible_word_ends (&newplace, count))
2171 		{
2172 			gtk_text_iter_forward_to_line_end (&newplace);
2173 		}
2174 	}
2175 
2176 	move_cursor (text_view, &newplace, extend_selection);
2177 }
2178 
2179 static void
gtk_source_view_move_cursor(GtkTextView * text_view,GtkMovementStep step,gint count,gboolean extend_selection)2180 gtk_source_view_move_cursor (GtkTextView     *text_view,
2181 			     GtkMovementStep  step,
2182 			     gint             count,
2183 			     gboolean         extend_selection)
2184 {
2185 	if (!gtk_text_view_get_cursor_visible (text_view))
2186 	{
2187 		goto chain_up;
2188 	}
2189 
2190 	gtk_text_view_reset_im_context (text_view);
2191 
2192 	switch (step)
2193 	{
2194 		case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2195 		case GTK_MOVEMENT_PARAGRAPH_ENDS:
2196 			if (move_cursor_smart_home_end (text_view, step, count, extend_selection))
2197 			{
2198 				return;
2199 			}
2200 			break;
2201 
2202 		case GTK_MOVEMENT_WORDS:
2203 			move_cursor_words (text_view, count, extend_selection);
2204 			return;
2205 
2206 		case GTK_MOVEMENT_LOGICAL_POSITIONS:
2207 		case GTK_MOVEMENT_VISUAL_POSITIONS:
2208 		case GTK_MOVEMENT_DISPLAY_LINES:
2209 		case GTK_MOVEMENT_PARAGRAPHS:
2210 		case GTK_MOVEMENT_PAGES:
2211 		case GTK_MOVEMENT_BUFFER_ENDS:
2212 		case GTK_MOVEMENT_HORIZONTAL_PAGES:
2213 		default:
2214 			break;
2215 	}
2216 
2217 chain_up:
2218 	GTK_TEXT_VIEW_CLASS (gtk_source_view_parent_class)->move_cursor (text_view,
2219 									 step,
2220 									 count,
2221 									 extend_selection);
2222 }
2223 
2224 static void
gtk_source_view_delete_from_cursor(GtkTextView * text_view,GtkDeleteType type,gint count)2225 gtk_source_view_delete_from_cursor (GtkTextView   *text_view,
2226 				    GtkDeleteType  type,
2227 				    gint           count)
2228 {
2229 	GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
2230 	GtkTextIter insert;
2231 	GtkTextIter start;
2232 	GtkTextIter end;
2233 
2234 	if (type != GTK_DELETE_WORD_ENDS)
2235 	{
2236 		GTK_TEXT_VIEW_CLASS (gtk_source_view_parent_class)->delete_from_cursor (text_view,
2237 											type,
2238 											count);
2239 		return;
2240 	}
2241 
2242 	gtk_text_view_reset_im_context (text_view);
2243 
2244 	gtk_text_buffer_get_iter_at_mark (buffer,
2245 					  &insert,
2246 					  gtk_text_buffer_get_insert (buffer));
2247 
2248 	start = insert;
2249 	end = insert;
2250 
2251 	if (count > 0)
2252 	{
2253 		if (!_gtk_source_iter_forward_visible_word_ends (&end, count))
2254 		{
2255 			gtk_text_iter_forward_to_line_end (&end);
2256 		}
2257 	}
2258 	else
2259 	{
2260 		if (!_gtk_source_iter_backward_visible_word_starts (&start, -count))
2261 		{
2262 			gtk_text_iter_set_line_offset (&start, 0);
2263 		}
2264 	}
2265 
2266 	gtk_text_buffer_delete_interactive (buffer, &start, &end,
2267 					    gtk_text_view_get_editable (text_view));
2268 }
2269 
2270 static gboolean
gtk_source_view_extend_selection(GtkTextView * text_view,GtkTextExtendSelection granularity,const GtkTextIter * location,GtkTextIter * start,GtkTextIter * end)2271 gtk_source_view_extend_selection (GtkTextView            *text_view,
2272 				  GtkTextExtendSelection  granularity,
2273 				  const GtkTextIter      *location,
2274 				  GtkTextIter            *start,
2275 				  GtkTextIter            *end)
2276 {
2277 	if (granularity == GTK_TEXT_EXTEND_SELECTION_WORD)
2278 	{
2279 		_gtk_source_iter_extend_selection_word (location, start, end);
2280 		return GDK_EVENT_STOP;
2281 	}
2282 
2283 	return GTK_TEXT_VIEW_CLASS (gtk_source_view_parent_class)->extend_selection (text_view,
2284 										     granularity,
2285 										     location,
2286 										     start,
2287 										     end);
2288 }
2289 
2290 static void
gtk_source_view_ensure_redrawn_rect_is_highlighted(GtkSourceView * view,cairo_t * cr)2291 gtk_source_view_ensure_redrawn_rect_is_highlighted (GtkSourceView *view,
2292 						    cairo_t       *cr)
2293 {
2294 	GdkRectangle clip;
2295 	GtkTextIter iter1, iter2;
2296 
2297 	if (view->priv->source_buffer == NULL ||
2298 	    !gdk_cairo_get_clip_rectangle (cr, &clip))
2299 	{
2300 		return;
2301 	}
2302 
2303 	gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view), &iter1, clip.y, NULL);
2304 	gtk_text_iter_backward_line (&iter1);
2305 	gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view), &iter2, clip.y + clip.height, NULL);
2306 	gtk_text_iter_forward_line (&iter2);
2307 
2308 	DEBUG ({
2309 		g_print ("    draw area: %d - %d\n", clip.y, clip.y + clip.height);
2310 		g_print ("    lines to update: %d - %d\n",
2311 			 gtk_text_iter_get_line (&iter1),
2312 			 gtk_text_iter_get_line (&iter2));
2313 	});
2314 
2315 	_gtk_source_buffer_update_syntax_highlight (view->priv->source_buffer,
2316 						    &iter1, &iter2, FALSE);
2317 	_gtk_source_buffer_update_search_highlight (view->priv->source_buffer,
2318 						    &iter1, &iter2, FALSE);
2319 }
2320 
2321 /* This function is taken from gtk+/tests/testtext.c */
2322 static void
gtk_source_view_get_lines(GtkTextView * text_view,gint first_y,gint last_y,GArray * buffer_coords,GArray * line_heights,GArray * numbers,gint * countp)2323 gtk_source_view_get_lines (GtkTextView *text_view,
2324 			   gint         first_y,
2325 			   gint         last_y,
2326 			   GArray      *buffer_coords,
2327 			   GArray      *line_heights,
2328 			   GArray      *numbers,
2329 			   gint        *countp)
2330 {
2331 	GtkTextIter iter;
2332 	gint count;
2333 	gint last_line_num = -1;
2334 
2335 	g_array_set_size (buffer_coords, 0);
2336 	g_array_set_size (numbers, 0);
2337 	if (line_heights != NULL)
2338 		g_array_set_size (line_heights, 0);
2339 
2340 	/* Get iter at first y */
2341 	gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
2342 
2343 	/* For each iter, get its location and add it to the arrays.
2344 	 * Stop when we pass last_y */
2345 	count = 0;
2346 
2347 	while (!gtk_text_iter_is_end (&iter))
2348 	{
2349 		gint y, height;
2350 
2351 		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2352 
2353 		g_array_append_val (buffer_coords, y);
2354 		if (line_heights)
2355 		{
2356 			g_array_append_val (line_heights, height);
2357 		}
2358 
2359 		last_line_num = gtk_text_iter_get_line (&iter);
2360 		g_array_append_val (numbers, last_line_num);
2361 
2362 		++count;
2363 
2364 		if ((y + height) >= last_y)
2365 			break;
2366 
2367 		gtk_text_iter_forward_line (&iter);
2368 	}
2369 
2370 	if (gtk_text_iter_is_end (&iter))
2371 	{
2372 		gint y, height;
2373 		gint line_num;
2374 
2375 		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2376 
2377 		line_num = gtk_text_iter_get_line (&iter);
2378 
2379 		if (line_num != last_line_num)
2380 		{
2381 			g_array_append_val (buffer_coords, y);
2382 			if (line_heights)
2383 				g_array_append_val (line_heights, height);
2384 			g_array_append_val (numbers, line_num);
2385 			++count;
2386 		}
2387 	}
2388 
2389 	*countp = count;
2390 }
2391 
2392 /* Another solution to paint the line background is to use the
2393  * GtkTextTag:paragraph-background property. But there are several issues:
2394  * - GtkTextTags are per buffer, not per view. It's better to keep the line
2395  *   highlighting per view.
2396  * - There is a problem for empty lines: a text tag can not be applied to an
2397  *   empty region. And it can not be worked around easily for the last line.
2398  *
2399  * See https://bugzilla.gnome.org/show_bug.cgi?id=310847 for more details.
2400  */
2401 static void
gtk_source_view_paint_line_background(GtkTextView * text_view,cairo_t * cr,int y,int height,const GdkRGBA * color)2402 gtk_source_view_paint_line_background (GtkTextView    *text_view,
2403 				       cairo_t        *cr,
2404 				       int             y, /* in buffer coordinates */
2405 				       int             height,
2406 				       const GdkRGBA  *color)
2407 {
2408 	gdouble x1, y1, x2, y2;
2409 
2410 	cairo_save (cr);
2411 	cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
2412 
2413 	gdk_cairo_set_source_rgba (cr, (GdkRGBA *)color);
2414 	cairo_set_line_width (cr, 1);
2415 	cairo_rectangle (cr, x1 + .5, y + .5, x2 - x1 - 1, height - 1);
2416 	cairo_stroke_preserve (cr);
2417 	cairo_fill (cr);
2418 	cairo_restore (cr);
2419 }
2420 
2421 static void
gtk_source_view_paint_marks_background(GtkSourceView * view,cairo_t * cr)2422 gtk_source_view_paint_marks_background (GtkSourceView *view,
2423 					cairo_t       *cr)
2424 {
2425 	GtkTextView *text_view;
2426 	GdkRectangle clip;
2427 	GArray *numbers;
2428 	GArray *pixels;
2429 	GArray *heights;
2430 	gint y1, y2;
2431 	gint count;
2432 	gint i;
2433 
2434 	if (view->priv->source_buffer == NULL ||
2435 	    !gdk_cairo_get_clip_rectangle (cr, &clip))
2436 	{
2437 		return;
2438 	}
2439 
2440 	text_view = GTK_TEXT_VIEW (view);
2441 
2442 	y1 = clip.y;
2443 	y2 = y1 + clip.height;
2444 
2445 	numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2446 	pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2447 	heights = g_array_new (FALSE, FALSE, sizeof (gint));
2448 
2449 	/* get the line numbers and y coordinates. */
2450 	gtk_source_view_get_lines (text_view,
2451 	                           y1,
2452 	                           y2,
2453 	                           pixels,
2454 	                           heights,
2455 	                           numbers,
2456 	                           &count);
2457 
2458 	if (count == 0)
2459 	{
2460 		gint n = 0;
2461 		gint y;
2462 		gint height;
2463 		GtkTextIter iter;
2464 
2465 		gtk_text_buffer_get_start_iter (gtk_text_view_get_buffer (text_view), &iter);
2466 		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2467 
2468 		g_array_append_val (pixels, y);
2469 		g_array_append_val (pixels, height);
2470 		g_array_append_val (numbers, n);
2471 		count = 1;
2472 	}
2473 
2474 	DEBUG ({
2475 		g_print ("    Painting marks background for line numbers %d - %d\n",
2476 		         g_array_index (numbers, gint, 0),
2477 		         g_array_index (numbers, gint, count - 1));
2478 	});
2479 
2480 	for (i = 0; i < count; ++i)
2481 	{
2482 		gint line_to_paint;
2483 		GSList *marks;
2484 		GdkRGBA background;
2485 		int priority;
2486 
2487 		line_to_paint = g_array_index (numbers, gint, i);
2488 
2489 		marks = gtk_source_buffer_get_source_marks_at_line (view->priv->source_buffer,
2490 		                                                    line_to_paint,
2491 		                                                    NULL);
2492 
2493 		priority = -1;
2494 
2495 		while (marks != NULL)
2496 		{
2497 			GtkSourceMarkAttributes *attrs;
2498 			gint prio;
2499 			GdkRGBA bg;
2500 
2501 			attrs = gtk_source_view_get_mark_attributes (view,
2502 			                                             gtk_source_mark_get_category (marks->data),
2503 			                                             &prio);
2504 
2505 			if (attrs != NULL &&
2506 			    prio > priority &&
2507 			    gtk_source_mark_attributes_get_background (attrs, &bg))
2508 			{
2509 				priority = prio;
2510 				background = bg;
2511 			}
2512 
2513 			marks = g_slist_delete_link (marks, marks);
2514 		}
2515 
2516 		if (priority != -1)
2517 		{
2518 			gtk_source_view_paint_line_background (text_view,
2519 			                                       cr,
2520 			                                       g_array_index (pixels, gint, i),
2521 			                                       g_array_index (heights, gint, i),
2522 			                                       &background);
2523 		}
2524 	}
2525 
2526 	g_array_free (heights, TRUE);
2527 	g_array_free (pixels, TRUE);
2528 	g_array_free (numbers, TRUE);
2529 }
2530 
2531 static void
gtk_source_view_paint_right_margin(GtkSourceView * view,cairo_t * cr)2532 gtk_source_view_paint_right_margin (GtkSourceView *view,
2533 				    cairo_t       *cr)
2534 {
2535 	GdkRectangle clip;
2536 	gdouble x;
2537 
2538 	GtkTextView *text_view = GTK_TEXT_VIEW (view);
2539 
2540 #ifdef ENABLE_PROFILE
2541 	static GTimer *timer = NULL;
2542 
2543 	if (timer == NULL)
2544 	{
2545 		timer = g_timer_new ();
2546 	}
2547 
2548 	g_timer_start (timer);
2549 #endif
2550 
2551 	g_return_if_fail (view->priv->right_margin_line_color != NULL);
2552 
2553 	if (!gdk_cairo_get_clip_rectangle (cr, &clip))
2554 	{
2555 		return;
2556 	}
2557 
2558 	if (view->priv->cached_right_margin_pos < 0)
2559 	{
2560 		view->priv->cached_right_margin_pos =
2561 			calculate_real_tab_width (view,
2562 						  view->priv->right_margin_pos,
2563 						  '_');
2564 	}
2565 
2566 	x = view->priv->cached_right_margin_pos + gtk_text_view_get_left_margin (text_view);
2567 
2568 	cairo_save (cr);
2569 	cairo_set_line_width (cr, 1.0);
2570 
2571 	if (x + 1 >= clip.x && x <= clip.x + clip.width)
2572 	{
2573 		cairo_move_to (cr, x + 0.5, clip.y);
2574 		cairo_line_to (cr, x + 0.5, clip.y + clip.height);
2575 
2576 		gdk_cairo_set_source_rgba (cr, view->priv->right_margin_line_color);
2577 		cairo_stroke (cr);
2578 	}
2579 
2580 	/* Only draw the overlay when the style scheme explicitly sets it. */
2581 	if (view->priv->right_margin_overlay_color != NULL && clip.x + clip.width > x + 1)
2582 	{
2583 		/* Draw the rectangle next to the line (x+1). */
2584 		cairo_rectangle (cr,
2585 				 x + 1, clip.y,
2586 				 clip.x + clip.width - (x + 1), clip.height);
2587 
2588 		gdk_cairo_set_source_rgba (cr, view->priv->right_margin_overlay_color);
2589 		cairo_fill (cr);
2590 	}
2591 
2592 	cairo_restore (cr);
2593 
2594 	PROFILE ({
2595 		g_timer_stop (timer);
2596 		g_print ("    gtk_source_view_paint_right_margin time: "
2597 			 "%g (sec * 1000)\n",
2598 			 g_timer_elapsed (timer, NULL) * 1000);
2599 	});
2600 }
2601 
2602 static gint
realign(gint offset,guint align)2603 realign (gint  offset,
2604 	 guint align)
2605 {
2606 	if (offset > 0 && align > 0)
2607 	{
2608 		gint padding;
2609 
2610 		padding = (align - (offset % align)) % align;
2611 		return offset + padding;
2612 	}
2613 
2614 	return 0;
2615 }
2616 
2617 static void
gtk_source_view_paint_background_pattern_grid(GtkSourceView * view,cairo_t * cr)2618 gtk_source_view_paint_background_pattern_grid (GtkSourceView *view,
2619 					       cairo_t       *cr)
2620 {
2621 	GdkRectangle clip;
2622 	gint x, y, x2, y2;
2623 	PangoContext *context;
2624 	PangoLayout *layout;
2625 	gint grid_width = 16;
2626 	gint grid_height = 16;
2627 
2628 	context = gtk_widget_get_pango_context (GTK_WIDGET (view));
2629 	layout = pango_layout_new (context);
2630 	pango_layout_set_text (layout, "X", 1);
2631 	pango_layout_get_pixel_size (layout, &grid_width, &grid_height);
2632 	g_object_unref (layout);
2633 
2634 	/* each character becomes 2 stacked boxes. */
2635 	grid_height = MAX (1, grid_height / 2);
2636 	grid_width = MAX (1, grid_width);
2637 
2638 	cairo_save (cr);
2639 
2640 	gdk_cairo_get_clip_rectangle (cr, &clip);
2641 
2642 	cairo_set_line_width (cr, 1.0);
2643 	gdk_cairo_set_source_rgba (cr, &view->priv->background_pattern_color);
2644 
2645 	/* Align our drawing position with a multiple of the grid size. */
2646 	x = realign (clip.x - grid_width, grid_width);
2647 	y = realign (clip.y - grid_height, grid_height);
2648 	x2 = realign (x + clip.width + grid_width * 2, grid_width);
2649 	y2 = realign (y + clip.height + grid_height * 2, grid_height);
2650 
2651 	for (; x <= x2; x += grid_width)
2652 	{
2653 		cairo_move_to (cr, x + .5, clip.y - .5);
2654 		cairo_line_to (cr, x + .5, clip.y + clip.height - .5);
2655 	}
2656 
2657 	for (; y <= y2; y += grid_height)
2658 	{
2659 		cairo_move_to (cr, clip.x + .5, y - .5);
2660 		cairo_line_to (cr, clip.x + clip.width + .5, y - .5);
2661 	}
2662 
2663 	cairo_stroke (cr);
2664 	cairo_restore (cr);
2665 }
2666 
2667 static void
gtk_source_view_paint_current_line_highlight(GtkSourceView * view,cairo_t * cr)2668 gtk_source_view_paint_current_line_highlight (GtkSourceView *view,
2669 					      cairo_t       *cr)
2670 {
2671 	GtkTextBuffer *buffer;
2672 	GtkTextIter cur;
2673 	gint y;
2674 	gint height;
2675 
2676 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
2677 	gtk_text_buffer_get_iter_at_mark (buffer,
2678 					  &cur,
2679 					  gtk_text_buffer_get_insert (buffer));
2680 	gtk_text_view_get_line_yrange (GTK_TEXT_VIEW (view), &cur, &y, &height);
2681 
2682 	gtk_source_view_paint_line_background (GTK_TEXT_VIEW (view),
2683 					       cr,
2684 					       y, height,
2685 					       &view->priv->current_line_color);
2686 }
2687 
2688 static void
gtk_source_view_draw_layer(GtkTextView * text_view,GtkTextViewLayer layer,cairo_t * cr)2689 gtk_source_view_draw_layer (GtkTextView      *text_view,
2690 			    GtkTextViewLayer  layer,
2691 			    cairo_t          *cr)
2692 {
2693 	GtkSourceView *view;
2694 
2695 	view = GTK_SOURCE_VIEW (text_view);
2696 
2697 	cairo_save (cr);
2698 
2699 	if (layer == GTK_TEXT_VIEW_LAYER_BELOW_TEXT)
2700 	{
2701 		gtk_source_view_ensure_redrawn_rect_is_highlighted (view, cr);
2702 
2703 		if (view->priv->background_pattern == GTK_SOURCE_BACKGROUND_PATTERN_TYPE_GRID &&
2704 		    view->priv->background_pattern_color_set)
2705 		{
2706 			gtk_source_view_paint_background_pattern_grid (view, cr);
2707 		}
2708 
2709 		if (gtk_widget_is_sensitive (GTK_WIDGET (view)) &&
2710 		    view->priv->highlight_current_line &&
2711 		    view->priv->current_line_color_set)
2712 		{
2713 			gtk_source_view_paint_current_line_highlight (view, cr);
2714 		}
2715 
2716 		gtk_source_view_paint_marks_background (view, cr);
2717 	}
2718 	else if (layer == GTK_TEXT_VIEW_LAYER_ABOVE_TEXT)
2719 	{
2720 		/* Draw the right margin vertical line + overlay. */
2721 		if (view->priv->show_right_margin)
2722 		{
2723 			gtk_source_view_paint_right_margin (view, cr);
2724 		}
2725 
2726 		if (view->priv->space_drawer != NULL)
2727 		{
2728 			_gtk_source_space_drawer_draw (view->priv->space_drawer, view, cr);
2729 		}
2730 	}
2731 
2732 	cairo_restore (cr);
2733 }
2734 
2735 static gboolean
gtk_source_view_draw(GtkWidget * widget,cairo_t * cr)2736 gtk_source_view_draw (GtkWidget *widget,
2737 		      cairo_t   *cr)
2738 {
2739 	GtkSourceView *view = GTK_SOURCE_VIEW (widget);
2740 	gboolean event_handled;
2741 
2742 #ifdef ENABLE_PROFILE
2743 	static GTimer *timer = NULL;
2744 	if (timer == NULL)
2745 	{
2746 		timer = g_timer_new ();
2747 	}
2748 
2749 	g_timer_start (timer);
2750 #endif
2751 
2752 	DEBUG ({
2753 		g_print ("> gtk_source_view_draw start\n");
2754 	});
2755 
2756 	event_handled = GTK_WIDGET_CLASS (gtk_source_view_parent_class)->draw (widget, cr);
2757 
2758 	if (view->priv->left_gutter != NULL)
2759 	{
2760 		_gtk_source_gutter_draw (view->priv->left_gutter, view, cr);
2761 	}
2762 
2763 	if (view->priv->right_gutter != NULL)
2764 	{
2765 		_gtk_source_gutter_draw (view->priv->right_gutter, view, cr);
2766 	}
2767 
2768 	PROFILE ({
2769 		g_timer_stop (timer);
2770 		g_print ("    gtk_source_view_draw time: %g (sec * 1000)\n",
2771 		         g_timer_elapsed (timer, NULL) * 1000);
2772 	});
2773 	DEBUG ({
2774 		g_print ("> gtk_source_view_draw end\n");
2775 	});
2776 
2777 	return event_handled;
2778 }
2779 
2780 /* This is a pretty important function... We call it when the tab_stop is changed,
2781  * and when the font is changed.
2782  * NOTE: You must change this with the default font for now...
2783  * It may be a good idea to set the tab_width for each GtkTextTag as well
2784  * based on the font that we set at creation time
2785  * something like style_cache_set_tabs_from_font or the like.
2786  * Now, this *may* not be necessary because most tabs wont be inside of another highlight,
2787  * except for things like multi-line comments (which will usually have an italic font which
2788  * may or may not be a different size than the standard one), or if some random language
2789  * definition decides that it would be spiffy to have a bg color for "start of line" whitespace
2790  * "^\(\t\| \)+" would probably do the trick for that.
2791  */
2792 static gint
calculate_real_tab_width(GtkSourceView * view,guint tab_size,gchar c)2793 calculate_real_tab_width (GtkSourceView *view, guint tab_size, gchar c)
2794 {
2795 	PangoLayout *layout;
2796 	gchar *tab_string;
2797 	gint tab_width = 0;
2798 
2799 	if (tab_size == 0)
2800 	{
2801 		return -1;
2802 	}
2803 
2804 	tab_string = g_strnfill (tab_size, c);
2805 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), tab_string);
2806 	g_free (tab_string);
2807 
2808 	if (layout != NULL)
2809 	{
2810 		pango_layout_get_pixel_size (layout, &tab_width, NULL);
2811 		g_object_unref (G_OBJECT (layout));
2812 	}
2813 	else
2814 	{
2815 		tab_width = -1;
2816 	}
2817 
2818 	return tab_width;
2819 }
2820 
2821 static GtkTextBuffer *
gtk_source_view_create_buffer(GtkTextView * text_view)2822 gtk_source_view_create_buffer (GtkTextView *text_view)
2823 {
2824 	return GTK_TEXT_BUFFER (gtk_source_buffer_new (NULL));
2825 }
2826 
2827 
2828 /* ----------------------------------------------------------------------
2829  * Public interface
2830  * ----------------------------------------------------------------------
2831  */
2832 
2833 /**
2834  * gtk_source_view_new:
2835  *
2836  * Creates a new #GtkSourceView.
2837  *
2838  * By default, an empty #GtkSourceBuffer will be lazily created and can be
2839  * retrieved with gtk_text_view_get_buffer().
2840  *
2841  * If you want to specify your own buffer, either override the
2842  * #GtkTextViewClass create_buffer factory method, or use
2843  * gtk_source_view_new_with_buffer().
2844  *
2845  * Returns: a new #GtkSourceView.
2846  */
2847 GtkWidget *
gtk_source_view_new(void)2848 gtk_source_view_new (void)
2849 {
2850 	return g_object_new (GTK_SOURCE_TYPE_VIEW, NULL);
2851 }
2852 
2853 /**
2854  * gtk_source_view_new_with_buffer:
2855  * @buffer: a #GtkSourceBuffer.
2856  *
2857  * Creates a new #GtkSourceView widget displaying the buffer
2858  * @buffer. One buffer can be shared among many widgets.
2859  *
2860  * Returns: a new #GtkSourceView.
2861  */
2862 GtkWidget *
gtk_source_view_new_with_buffer(GtkSourceBuffer * buffer)2863 gtk_source_view_new_with_buffer (GtkSourceBuffer *buffer)
2864 {
2865 	g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
2866 
2867 	return g_object_new (GTK_SOURCE_TYPE_VIEW,
2868 			     "buffer", buffer,
2869 			     NULL);
2870 }
2871 
2872 /**
2873  * gtk_source_view_get_show_line_numbers:
2874  * @view: a #GtkSourceView.
2875  *
2876  * Returns whether line numbers are displayed beside the text.
2877  *
2878  * Return value: %TRUE if the line numbers are displayed.
2879  */
2880 gboolean
gtk_source_view_get_show_line_numbers(GtkSourceView * view)2881 gtk_source_view_get_show_line_numbers (GtkSourceView *view)
2882 {
2883 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
2884 
2885 	return view->priv->show_line_numbers;
2886 }
2887 
2888 /**
2889  * gtk_source_view_set_show_line_numbers:
2890  * @view: a #GtkSourceView.
2891  * @show: whether line numbers should be displayed.
2892  *
2893  * If %TRUE line numbers will be displayed beside the text.
2894  */
2895 void
gtk_source_view_set_show_line_numbers(GtkSourceView * view,gboolean show)2896 gtk_source_view_set_show_line_numbers (GtkSourceView *view,
2897 				       gboolean       show)
2898 {
2899 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
2900 
2901 	show = show != FALSE;
2902 
2903 	if (show == view->priv->show_line_numbers)
2904 	{
2905 		return;
2906 	}
2907 
2908 	if (view->priv->line_renderer == NULL)
2909 	{
2910 		GtkSourceGutter *gutter;
2911 
2912 		gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
2913 
2914 		view->priv->line_renderer = gtk_source_gutter_renderer_lines_new ();
2915 		g_object_set (view->priv->line_renderer,
2916 		              "alignment-mode", GTK_SOURCE_GUTTER_RENDERER_ALIGNMENT_MODE_FIRST,
2917 		              "yalign", 0.5,
2918 		              "xalign", 1.0,
2919 		              "xpad", 3,
2920 		              NULL);
2921 
2922 		gtk_source_gutter_insert (gutter,
2923 		                          view->priv->line_renderer,
2924 		                          GTK_SOURCE_VIEW_GUTTER_POSITION_LINES);
2925 	}
2926 
2927 	gtk_source_gutter_renderer_set_visible (view->priv->line_renderer, show);
2928 	view->priv->show_line_numbers = show;
2929 
2930 	g_object_notify (G_OBJECT (view), "show_line_numbers");
2931 }
2932 
2933 /**
2934  * gtk_source_view_get_show_line_marks:
2935  * @view: a #GtkSourceView.
2936  *
2937  * Returns whether line marks are displayed beside the text.
2938  *
2939  * Return value: %TRUE if the line marks are displayed.
2940  *
2941  * Since: 2.2
2942  */
2943 gboolean
gtk_source_view_get_show_line_marks(GtkSourceView * view)2944 gtk_source_view_get_show_line_marks (GtkSourceView *view)
2945 {
2946 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
2947 
2948 	return view->priv->show_line_marks;
2949 }
2950 
2951 static void
gutter_renderer_marks_activate(GtkSourceGutterRenderer * renderer,GtkTextIter * iter,const GdkRectangle * area,GdkEvent * event,GtkSourceView * view)2952 gutter_renderer_marks_activate (GtkSourceGutterRenderer *renderer,
2953 				GtkTextIter             *iter,
2954 				const GdkRectangle      *area,
2955 				GdkEvent                *event,
2956 				GtkSourceView           *view)
2957 {
2958 	g_signal_emit (view,
2959 	               signals[LINE_MARK_ACTIVATED],
2960 	               0,
2961 	               iter,
2962 	               event);
2963 }
2964 
2965 /**
2966  * gtk_source_view_set_show_line_marks:
2967  * @view: a #GtkSourceView.
2968  * @show: whether line marks should be displayed.
2969  *
2970  * If %TRUE line marks will be displayed beside the text.
2971  *
2972  * Since: 2.2
2973  */
2974 void
gtk_source_view_set_show_line_marks(GtkSourceView * view,gboolean show)2975 gtk_source_view_set_show_line_marks (GtkSourceView *view,
2976 				     gboolean       show)
2977 {
2978 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
2979 
2980 	show = show != FALSE;
2981 
2982 	if (show == view->priv->show_line_marks)
2983 	{
2984 		return;
2985 	}
2986 
2987 	if (view->priv->marks_renderer == NULL)
2988 	{
2989 		GtkSourceGutter *gutter;
2990 
2991 		gutter = gtk_source_view_get_gutter (view, GTK_TEXT_WINDOW_LEFT);
2992 
2993 		view->priv->marks_renderer = gtk_source_gutter_renderer_marks_new ();
2994 
2995 		gtk_source_gutter_insert (gutter,
2996 		                          view->priv->marks_renderer,
2997 		                          GTK_SOURCE_VIEW_GUTTER_POSITION_MARKS);
2998 
2999 		g_signal_connect (view->priv->marks_renderer,
3000 		                  "activate",
3001 		                  G_CALLBACK (gutter_renderer_marks_activate),
3002 		                  view);
3003 	}
3004 
3005 	gtk_source_gutter_renderer_set_visible (view->priv->marks_renderer, show);
3006 	view->priv->show_line_marks = show;
3007 
3008 	g_object_notify (G_OBJECT (view), "show_line_marks");
3009 }
3010 
3011 static gboolean
set_tab_stops_internal(GtkSourceView * view)3012 set_tab_stops_internal (GtkSourceView *view)
3013 {
3014 	PangoTabArray *tab_array;
3015 	gint real_tab_width;
3016 
3017 	real_tab_width = calculate_real_tab_width (view, view->priv->tab_width, ' ');
3018 
3019 	if (real_tab_width < 0)
3020 	{
3021 		return FALSE;
3022 	}
3023 
3024 	tab_array = pango_tab_array_new (1, TRUE);
3025 	pango_tab_array_set_tab (tab_array, 0, PANGO_TAB_LEFT, real_tab_width);
3026 
3027 	gtk_text_view_set_tabs (GTK_TEXT_VIEW (view),
3028 				tab_array);
3029 	view->priv->tabs_set = TRUE;
3030 
3031 	pango_tab_array_free (tab_array);
3032 
3033 	return TRUE;
3034 }
3035 
3036 /**
3037  * gtk_source_view_set_tab_width:
3038  * @view: a #GtkSourceView.
3039  * @width: width of tab in characters.
3040  *
3041  * Sets the width of tabulation in characters. The #GtkTextBuffer still contains
3042  * \t characters, but they can take a different visual width in a #GtkSourceView
3043  * widget.
3044  */
3045 void
gtk_source_view_set_tab_width(GtkSourceView * view,guint width)3046 gtk_source_view_set_tab_width (GtkSourceView *view,
3047 			       guint          width)
3048 {
3049 	guint save_width;
3050 
3051 	g_return_if_fail (GTK_SOURCE_VIEW (view));
3052 	g_return_if_fail (0 < width && width <= MAX_TAB_WIDTH);
3053 
3054 	if (view->priv->tab_width == width)
3055 	{
3056 		return;
3057 	}
3058 
3059 	save_width = view->priv->tab_width;
3060 	view->priv->tab_width = width;
3061 	if (set_tab_stops_internal (view))
3062 	{
3063 		g_object_notify (G_OBJECT (view), "tab-width");
3064 	}
3065 	else
3066 	{
3067 		g_warning ("Impossible to set tab width.");
3068 		view->priv->tab_width = save_width;
3069 	}
3070 }
3071 
3072 /**
3073  * gtk_source_view_get_tab_width:
3074  * @view: a #GtkSourceView.
3075  *
3076  * Returns the width of tabulation in characters.
3077  *
3078  * Return value: width of tab.
3079  */
3080 guint
gtk_source_view_get_tab_width(GtkSourceView * view)3081 gtk_source_view_get_tab_width (GtkSourceView *view)
3082 {
3083 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), DEFAULT_TAB_WIDTH);
3084 
3085 	return view->priv->tab_width;
3086 }
3087 
3088 /**
3089  * gtk_source_view_set_indent_width:
3090  * @view: a #GtkSourceView.
3091  * @width: indent width in characters.
3092  *
3093  * Sets the number of spaces to use for each step of indent when the tab key is
3094  * pressed. If @width is -1, the value of the #GtkSourceView:tab-width property
3095  * will be used.
3096  *
3097  * The #GtkSourceView:indent-width interacts with the
3098  * #GtkSourceView:insert-spaces-instead-of-tabs property and
3099  * #GtkSourceView:tab-width. An example will be clearer: if the
3100  * #GtkSourceView:indent-width is 4 and
3101  * #GtkSourceView:tab-width is 8 and
3102  * #GtkSourceView:insert-spaces-instead-of-tabs is %FALSE, then pressing the tab
3103  * key at the beginning of a line will insert 4 spaces. So far so good. Pressing
3104  * the tab key a second time will remove the 4 spaces and insert a \t character
3105  * instead (since #GtkSourceView:tab-width is 8). On the other hand, if
3106  * #GtkSourceView:insert-spaces-instead-of-tabs is %TRUE, the second tab key
3107  * pressed will insert 4 more spaces for a total of 8 spaces in the
3108  * #GtkTextBuffer.
3109  *
3110  * The test-widget program (available in the GtkSourceView repository) may be
3111  * useful to better understand the indentation settings (enable the space
3112  * drawing!).
3113  */
3114 void
gtk_source_view_set_indent_width(GtkSourceView * view,gint width)3115 gtk_source_view_set_indent_width (GtkSourceView *view,
3116 				  gint           width)
3117 {
3118 	g_return_if_fail (GTK_SOURCE_VIEW (view));
3119 	g_return_if_fail (width == -1 || (0 < width && width <= MAX_INDENT_WIDTH));
3120 
3121 	if (view->priv->indent_width != width)
3122 	{
3123 		view->priv->indent_width = width;
3124 		g_object_notify (G_OBJECT (view), "indent-width");
3125 	}
3126 }
3127 
3128 /**
3129  * gtk_source_view_get_indent_width:
3130  * @view: a #GtkSourceView.
3131  *
3132  * Returns the number of spaces to use for each step of indent.
3133  * See gtk_source_view_set_indent_width() for details.
3134  *
3135  * Return value: indent width.
3136  */
3137 gint
gtk_source_view_get_indent_width(GtkSourceView * view)3138 gtk_source_view_get_indent_width (GtkSourceView *view)
3139 {
3140 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), 0);
3141 
3142 	return view->priv->indent_width;
3143 }
3144 
3145 static gchar *
compute_indentation(GtkSourceView * view,GtkTextIter * cur)3146 compute_indentation (GtkSourceView *view,
3147 		     GtkTextIter   *cur)
3148 {
3149 	GtkTextIter start;
3150 	GtkTextIter end;
3151 	gunichar ch;
3152 
3153 	start = *cur;
3154 	gtk_text_iter_set_line_offset (&start, 0);
3155 
3156 	end = start;
3157 
3158 	ch = gtk_text_iter_get_char (&end);
3159 
3160 	while (g_unichar_isspace (ch) &&
3161 	       (ch != '\n') &&
3162 	       (ch != '\r') &&
3163 	       (gtk_text_iter_compare (&end, cur) < 0))
3164 	{
3165 		if (!gtk_text_iter_forward_char (&end))
3166 		{
3167 			break;
3168 		}
3169 
3170 		ch = gtk_text_iter_get_char (&end);
3171 	}
3172 
3173 	if (gtk_text_iter_equal (&start, &end))
3174 	{
3175 		return NULL;
3176 	}
3177 
3178 	return gtk_text_iter_get_slice (&start, &end);
3179 }
3180 
3181 static guint
get_real_indent_width(GtkSourceView * view)3182 get_real_indent_width (GtkSourceView *view)
3183 {
3184 	return view->priv->indent_width < 0 ?
3185 	       view->priv->tab_width :
3186 	       (guint) view->priv->indent_width;
3187 }
3188 
3189 static gchar *
get_indent_string(guint tabs,guint spaces)3190 get_indent_string (guint tabs,
3191 		   guint spaces)
3192 {
3193 	gchar *str;
3194 
3195 	str = g_malloc (tabs + spaces + 1);
3196 
3197 	if (tabs > 0)
3198 	{
3199 		memset (str, '\t', tabs);
3200 	}
3201 
3202 	if (spaces > 0)
3203 	{
3204 		memset (str + tabs, ' ', spaces);
3205 	}
3206 
3207 	str[tabs + spaces] = '\0';
3208 
3209 	return str;
3210 }
3211 
3212 /**
3213  * gtk_source_view_indent_lines:
3214  * @view: a #GtkSourceView.
3215  * @start: #GtkTextIter of the first line to indent
3216  * @end: #GtkTextIter of the last line to indent
3217  *
3218  * Inserts one indentation level at the beginning of the specified lines. The
3219  * empty lines are not indented.
3220  *
3221  * Since: 3.16
3222  */
3223 void
gtk_source_view_indent_lines(GtkSourceView * view,GtkTextIter * start,GtkTextIter * end)3224 gtk_source_view_indent_lines (GtkSourceView *view,
3225 			      GtkTextIter   *start,
3226 			      GtkTextIter   *end)
3227 {
3228 	GtkTextBuffer *buf;
3229 	gboolean bracket_hl;
3230 	GtkTextMark *start_mark, *end_mark;
3231 	gint start_line, end_line;
3232 	gchar *tab_buffer = NULL;
3233 	guint tabs = 0;
3234 	guint spaces = 0;
3235 	gint i;
3236 
3237 	if (view->priv->completion != NULL)
3238 	{
3239 		gtk_source_completion_block_interactive (view->priv->completion);
3240 	}
3241 
3242 	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
3243 
3244 	bracket_hl = gtk_source_buffer_get_highlight_matching_brackets (GTK_SOURCE_BUFFER (buf));
3245 	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buf), FALSE);
3246 
3247 	start_mark = gtk_text_buffer_create_mark (buf, NULL, start, FALSE);
3248 	end_mark = gtk_text_buffer_create_mark (buf, NULL, end, FALSE);
3249 
3250 	start_line = gtk_text_iter_get_line (start);
3251 	end_line = gtk_text_iter_get_line (end);
3252 
3253 	if ((gtk_text_iter_get_visible_line_offset (end) == 0) &&
3254 	    (end_line > start_line))
3255 	{
3256 		end_line--;
3257 	}
3258 
3259 	if (view->priv->insert_spaces)
3260 	{
3261 		spaces = get_real_indent_width (view);
3262 
3263 		tab_buffer = g_strnfill (spaces, ' ');
3264 	}
3265 	else if (view->priv->indent_width > 0 &&
3266 	         view->priv->indent_width != (gint)view->priv->tab_width)
3267 	{
3268 		guint indent_width;
3269 
3270 		indent_width = get_real_indent_width (view);
3271 		spaces = indent_width % view->priv->tab_width;
3272 		tabs = indent_width / view->priv->tab_width;
3273 
3274 		tab_buffer = get_indent_string (tabs, spaces);
3275 	}
3276 	else
3277 	{
3278 		tabs = 1;
3279 		tab_buffer = g_strdup ("\t");
3280 	}
3281 
3282 	gtk_text_buffer_begin_user_action (buf);
3283 
3284 	for (i = start_line; i <= end_line; i++)
3285 	{
3286 		GtkTextIter iter;
3287 		GtkTextIter iter2;
3288 		guint replaced_spaces = 0;
3289 
3290 		gtk_text_buffer_get_iter_at_line (buf, &iter, i);
3291 
3292 		/* Don't add indentation on completely empty lines, to not add
3293 		 * trailing spaces.
3294 		 * Note that non-empty lines containing only whitespaces are
3295 		 * indented like any other non-empty line, because those lines
3296 		 * already contain trailing spaces, some users use those
3297 		 * whitespaces to more easily insert text at the right place
3298 		 * without the need to insert the indentation each time.
3299 		 */
3300 		if (gtk_text_iter_ends_line (&iter))
3301 		{
3302 			continue;
3303 		}
3304 
3305 		/* add spaces always after tabs, to avoid the case
3306 		 * where "\t" becomes "  \t" with no visual difference */
3307 		while (gtk_text_iter_get_char (&iter) == '\t')
3308 		{
3309 			gtk_text_iter_forward_char (&iter);
3310 		}
3311 
3312 		/* if tabs are allowed try to merge the spaces
3313 		 * with the tab we are going to insert (if any) */
3314 		iter2 = iter;
3315 		while (!view->priv->insert_spaces &&
3316 		       (gtk_text_iter_get_char (&iter2) == ' ') &&
3317 			replaced_spaces < view->priv->tab_width)
3318 		{
3319 			++replaced_spaces;
3320 
3321 			gtk_text_iter_forward_char (&iter2);
3322 		}
3323 
3324 		if (replaced_spaces > 0)
3325 		{
3326 			gchar *indent_buf;
3327 			guint t, s;
3328 
3329 			t = tabs + (spaces + replaced_spaces) / view->priv->tab_width;
3330 			s = (spaces + replaced_spaces) % view->priv->tab_width;
3331 			indent_buf = get_indent_string (t, s);
3332 
3333 			gtk_text_buffer_delete (buf, &iter, &iter2);
3334 			gtk_text_buffer_insert (buf, &iter, indent_buf, -1);
3335 
3336 			g_free (indent_buf);
3337 		}
3338 		else
3339 		{
3340 			gtk_text_buffer_insert (buf, &iter, tab_buffer, -1);
3341 		}
3342 	}
3343 
3344 	gtk_text_buffer_end_user_action (buf);
3345 
3346 	g_free (tab_buffer);
3347 
3348 	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buf), bracket_hl);
3349 
3350 	if (view->priv->completion != NULL)
3351 	{
3352 		gtk_source_completion_unblock_interactive (view->priv->completion);
3353 	}
3354 
3355 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
3356 					    gtk_text_buffer_get_insert (buf));
3357 
3358 	/* revalidate iters */
3359 	gtk_text_buffer_get_iter_at_mark (buf, start, start_mark);
3360 	gtk_text_buffer_get_iter_at_mark (buf, end, end_mark);
3361 
3362 	gtk_text_buffer_delete_mark (buf, start_mark);
3363 	gtk_text_buffer_delete_mark (buf, end_mark);
3364 }
3365 
3366 /**
3367  * gtk_source_view_unindent_lines:
3368  * @view: a #GtkSourceView.
3369  * @start: #GtkTextIter of the first line to indent
3370  * @end: #GtkTextIter of the last line to indent
3371  *
3372  * Removes one indentation level at the beginning of the
3373  * specified lines.
3374  *
3375  * Since: 3.16
3376  */
3377 void
gtk_source_view_unindent_lines(GtkSourceView * view,GtkTextIter * start,GtkTextIter * end)3378 gtk_source_view_unindent_lines (GtkSourceView *view,
3379 				GtkTextIter   *start,
3380 				GtkTextIter   *end)
3381 {
3382 	GtkTextBuffer *buf;
3383 	gboolean bracket_hl;
3384 	GtkTextMark *start_mark, *end_mark;
3385 	gint start_line, end_line;
3386 	gint tab_width;
3387 	gint indent_width;
3388 	gint i;
3389 
3390 	if (view->priv->completion != NULL)
3391 	{
3392 		gtk_source_completion_block_interactive (view->priv->completion);
3393 	}
3394 
3395 	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
3396 
3397 	bracket_hl = gtk_source_buffer_get_highlight_matching_brackets (GTK_SOURCE_BUFFER (buf));
3398 	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buf), FALSE);
3399 
3400 	start_mark = gtk_text_buffer_create_mark (buf, NULL, start, FALSE);
3401 	end_mark = gtk_text_buffer_create_mark (buf, NULL, end, FALSE);
3402 
3403 	start_line = gtk_text_iter_get_line (start);
3404 	end_line = gtk_text_iter_get_line (end);
3405 
3406 	if ((gtk_text_iter_get_visible_line_offset (end) == 0) &&
3407 	    (end_line > start_line))
3408 	{
3409 		end_line--;
3410 	}
3411 
3412 	tab_width = view->priv->tab_width;
3413 	indent_width = get_real_indent_width (view);
3414 
3415 	gtk_text_buffer_begin_user_action (buf);
3416 
3417 	for (i = start_line; i <= end_line; i++)
3418 	{
3419 		GtkTextIter iter, iter2;
3420 		gint to_delete = 0;
3421 		gint to_delete_equiv = 0;
3422 
3423 		gtk_text_buffer_get_iter_at_line (buf, &iter, i);
3424 
3425 		iter2 = iter;
3426 
3427 		while (!gtk_text_iter_ends_line (&iter2) &&
3428 		       to_delete_equiv < indent_width)
3429 		{
3430 			gunichar c;
3431 
3432 			c = gtk_text_iter_get_char (&iter2);
3433 			if (c == '\t')
3434 			{
3435 				to_delete_equiv += tab_width - to_delete_equiv % tab_width;
3436 				++to_delete;
3437 			}
3438 			else if (c == ' ')
3439 			{
3440 				++to_delete_equiv;
3441 				++to_delete;
3442 			}
3443 			else
3444 			{
3445 				break;
3446 			}
3447 
3448 			gtk_text_iter_forward_char (&iter2);
3449 		}
3450 
3451 		if (to_delete > 0)
3452 		{
3453 			gtk_text_iter_set_line_offset (&iter2, to_delete);
3454 			gtk_text_buffer_delete (buf, &iter, &iter2);
3455 		}
3456 	}
3457 
3458 	gtk_text_buffer_end_user_action (buf);
3459 
3460 	gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buf), bracket_hl);
3461 
3462 	if (view->priv->completion != NULL)
3463 	{
3464 		gtk_source_completion_unblock_interactive (view->priv->completion);
3465 	}
3466 
3467 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
3468 					    gtk_text_buffer_get_insert (buf));
3469 
3470 	/* revalidate iters */
3471 	gtk_text_buffer_get_iter_at_mark (buf, start, start_mark);
3472 	gtk_text_buffer_get_iter_at_mark (buf, end, end_mark);
3473 
3474 	gtk_text_buffer_delete_mark (buf, start_mark);
3475 	gtk_text_buffer_delete_mark (buf, end_mark);
3476 }
3477 
3478 static gint
get_line_offset_in_equivalent_spaces(GtkSourceView * view,GtkTextIter * iter)3479 get_line_offset_in_equivalent_spaces (GtkSourceView *view,
3480 				      GtkTextIter   *iter)
3481 {
3482 	GtkTextIter i;
3483 	gint tab_width;
3484 	gint n = 0;
3485 
3486 	tab_width = view->priv->tab_width;
3487 
3488 	i = *iter;
3489 	gtk_text_iter_set_line_offset (&i, 0);
3490 
3491 	while (!gtk_text_iter_equal (&i, iter))
3492 	{
3493 		gunichar c;
3494 
3495 		c = gtk_text_iter_get_char (&i);
3496 		if (c == '\t')
3497 		{
3498 			n += tab_width - n % tab_width;
3499 		}
3500 		else
3501 		{
3502 			++n;
3503 		}
3504 
3505 		gtk_text_iter_forward_char (&i);
3506 	}
3507 
3508 	return n;
3509 }
3510 
3511 static void
insert_tab_or_spaces(GtkSourceView * view,GtkTextIter * start,GtkTextIter * end)3512 insert_tab_or_spaces (GtkSourceView *view,
3513 		      GtkTextIter   *start,
3514 		      GtkTextIter   *end)
3515 {
3516 	GtkTextBuffer *buf;
3517 	gchar *tab_buf;
3518 	gint cursor_offset = 0;
3519 
3520 	if (view->priv->insert_spaces)
3521 	{
3522 		gint indent_width;
3523 		gint pos;
3524 		gint spaces;
3525 
3526 		indent_width = get_real_indent_width (view);
3527 
3528 		/* CHECK: is this a performance problem? */
3529 		pos = get_line_offset_in_equivalent_spaces (view, start);
3530 		spaces = indent_width - pos % indent_width;
3531 
3532 		tab_buf = g_strnfill (spaces, ' ');
3533 	}
3534 	else if (view->priv->indent_width > 0 &&
3535 	         view->priv->indent_width != (gint)view->priv->tab_width)
3536 	{
3537 		GtkTextIter iter;
3538 		gint i;
3539 		gint tab_width;
3540 		gint indent_width;
3541 		gint from;
3542 		gint to;
3543 		gint preceding_spaces = 0;
3544 		gint following_tabs = 0;
3545 		gint equiv_spaces;
3546 		gint tabs;
3547 		gint spaces;
3548 
3549 		tab_width = view->priv->tab_width;
3550 		indent_width = get_real_indent_width (view);
3551 
3552 		/* CHECK: is this a performance problem? */
3553 		from = get_line_offset_in_equivalent_spaces (view, start);
3554 		to = indent_width * (1 + from / indent_width);
3555 		equiv_spaces = to - from;
3556 
3557 		/* extend the selection to include
3558 		 * preceding spaces so that if indentation
3559 		 * width < tab width, two conseutive indentation
3560 		 * width units get compressed into a tab */
3561 		iter = *start;
3562 		for (i = 0; i < tab_width; ++i)
3563 		{
3564 			gtk_text_iter_backward_char (&iter);
3565 
3566 			if (gtk_text_iter_get_char (&iter) == ' ')
3567 			{
3568 				++preceding_spaces;
3569 			}
3570 			else
3571 			{
3572 				break;
3573 			}
3574 		}
3575 
3576 		gtk_text_iter_backward_chars (start, preceding_spaces);
3577 
3578 		/* now also extend the selection to the following tabs
3579 		 * since we do not want to insert spaces before a tab
3580 		 * since it may have no visual effect */
3581 		while (gtk_text_iter_get_char (end) == '\t')
3582 		{
3583 			++following_tabs;
3584 			gtk_text_iter_forward_char (end);
3585 		}
3586 
3587 		tabs = (preceding_spaces + equiv_spaces) / tab_width;
3588 		spaces = (preceding_spaces + equiv_spaces) % tab_width;
3589 
3590 		tab_buf = get_indent_string (tabs + following_tabs, spaces);
3591 
3592 		cursor_offset = gtk_text_iter_get_offset (start) +
3593 			        tabs +
3594 		                (following_tabs > 0 ? 1 : spaces);
3595 	}
3596 	else
3597 	{
3598 		tab_buf = g_strdup ("\t");
3599 	}
3600 
3601 	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
3602 
3603 	gtk_text_buffer_begin_user_action (buf);
3604 
3605 	gtk_text_buffer_delete (buf, start, end);
3606 	gtk_text_buffer_insert (buf, start, tab_buf, -1);
3607 
3608 	/* adjust cursor position if needed */
3609 	if (cursor_offset > 0)
3610 	{
3611 		GtkTextIter iter;
3612 
3613 		gtk_text_buffer_get_iter_at_offset (buf, &iter, cursor_offset);
3614 		gtk_text_buffer_place_cursor (buf, &iter);
3615 	}
3616 
3617 	gtk_text_buffer_end_user_action (buf);
3618 
3619 	g_free (tab_buf);
3620 }
3621 
3622 static void
gtk_source_view_move_words(GtkSourceView * view,gint step)3623 gtk_source_view_move_words (GtkSourceView *view,
3624 			    gint           step)
3625 {
3626 	GtkTextBuffer *buf;
3627 	GtkTextIter s, e, ns, ne;
3628 	GtkTextMark *nsmark, *nemark;
3629 	gchar *old_text, *new_text;
3630 
3631 	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
3632 
3633 	if (step == 0 || gtk_text_view_get_editable (GTK_TEXT_VIEW (view)) == FALSE)
3634 	{
3635 		return;
3636 	}
3637 
3638 	gtk_text_buffer_get_selection_bounds (buf, &s, &e);
3639 
3640 	if (gtk_text_iter_compare (&s, &e) == 0)
3641 	{
3642 		if (!gtk_text_iter_starts_word (&s))
3643 		{
3644 			if (!gtk_text_iter_inside_word (&s) && !gtk_text_iter_ends_word (&s))
3645 			{
3646 				return;
3647 			}
3648 			else
3649 			{
3650 				gtk_text_iter_backward_word_start (&s);
3651 			}
3652 		}
3653 
3654 		if (!gtk_text_iter_starts_word (&s))
3655 		{
3656 			return;
3657 		}
3658 
3659 		e = s;
3660 
3661 		if (!gtk_text_iter_ends_word (&e))
3662 		{
3663 			if (!gtk_text_iter_forward_word_end (&e))
3664 			{
3665 				gtk_text_iter_forward_to_end (&e);
3666 			}
3667 
3668 			if (!gtk_text_iter_ends_word (&e))
3669 			{
3670 				return;
3671 			}
3672 		}
3673 	}
3674 
3675 	/* Swap the selection with the next or previous word, based on step */
3676 	if (step > 0)
3677 	{
3678 		ne = e;
3679 
3680 		if (!gtk_text_iter_forward_word_ends (&ne, step))
3681 		{
3682 			gtk_text_iter_forward_to_end (&ne);
3683 		}
3684 
3685 		if (!gtk_text_iter_ends_word (&ne) || gtk_text_iter_equal (&ne, &e))
3686 		{
3687 			return;
3688 		}
3689 
3690 		ns = ne;
3691 
3692 		if (!gtk_text_iter_backward_word_start (&ns))
3693 		{
3694 			return;
3695 		}
3696 	}
3697 	else
3698 	{
3699 		ns = s;
3700 
3701 		if (!gtk_text_iter_backward_word_starts (&ns, -step))
3702 		{
3703 			return;
3704 		}
3705 
3706 		ne = ns;
3707 
3708 		if (!gtk_text_iter_forward_word_end (&ne))
3709 		{
3710 			return;
3711 		}
3712 	}
3713 
3714 	if (gtk_text_iter_in_range (&ns, &s, &e) ||
3715 	    gtk_text_iter_in_range (&ne, &s, &e))
3716 	{
3717 		return;
3718 	}
3719 
3720 	old_text = gtk_text_buffer_get_text (buf, &s, &e, TRUE);
3721 	new_text = gtk_text_buffer_get_text (buf, &ns, &ne, TRUE);
3722 
3723 	gtk_text_buffer_begin_user_action (buf);
3724 
3725 	nsmark = gtk_text_buffer_create_mark (buf, NULL, &ns, TRUE);
3726 	nemark = gtk_text_buffer_create_mark (buf, NULL, &ne, FALSE);
3727 
3728 	gtk_text_buffer_delete (buf, &s, &e);
3729 	gtk_text_buffer_insert (buf, &s, new_text, -1);
3730 
3731 	gtk_text_buffer_get_iter_at_mark (buf, &ns, nsmark);
3732 	gtk_text_buffer_get_iter_at_mark (buf, &ne, nemark);
3733 
3734 	gtk_text_buffer_delete (buf, &ns, &ne);
3735 	gtk_text_buffer_insert (buf, &ns, old_text, -1);
3736 
3737 	ne = ns;
3738 	gtk_text_buffer_get_iter_at_mark (buf, &ns, nsmark);
3739 
3740 	gtk_text_buffer_select_range (buf, &ns, &ne);
3741 
3742 	gtk_text_buffer_delete_mark (buf, nsmark);
3743 	gtk_text_buffer_delete_mark (buf, nemark);
3744 
3745 	gtk_text_buffer_end_user_action (buf);
3746 
3747 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
3748 	                                    gtk_text_buffer_get_insert (buf));
3749 
3750 	g_free (old_text);
3751 	g_free (new_text);
3752 }
3753 
3754 static gboolean
buffer_contains_trailing_newline(GtkTextBuffer * buffer)3755 buffer_contains_trailing_newline (GtkTextBuffer *buffer)
3756 {
3757 	GtkTextIter iter;
3758 	gunichar ch;
3759 
3760 	gtk_text_buffer_get_end_iter (buffer, &iter);
3761 	gtk_text_iter_backward_char (&iter);
3762 	ch = gtk_text_iter_get_char (&iter);
3763 
3764 	return (ch == '\n' || ch == '\r');
3765 }
3766 
3767 /* FIXME could be a function of GtkSourceBuffer, it's also useful for the
3768  * FileLoader.
3769  */
3770 static void
remove_trailing_newline(GtkTextBuffer * buffer)3771 remove_trailing_newline (GtkTextBuffer *buffer)
3772 {
3773 	GtkTextIter start;
3774 	GtkTextIter end;
3775 
3776 	gtk_text_buffer_get_end_iter (buffer, &end);
3777 	start = end;
3778 
3779 	gtk_text_iter_set_line_offset (&start, 0);
3780 
3781 	if (gtk_text_iter_ends_line (&start) &&
3782 	    gtk_text_iter_backward_line (&start))
3783 	{
3784 		if (!gtk_text_iter_ends_line (&start))
3785 		{
3786 			gtk_text_iter_forward_to_line_end (&start);
3787 		}
3788 
3789 		gtk_text_buffer_delete (buffer, &start, &end);
3790 	}
3791 }
3792 
3793 static void
gtk_source_view_move_lines(GtkSourceView * view,gboolean copy,gint step)3794 gtk_source_view_move_lines (GtkSourceView *view,
3795 			    gboolean       copy,
3796 			    gint           step)
3797 {
3798 	GtkTextBuffer *buffer;
3799 	GtkTextIter start;
3800 	GtkTextIter end;
3801 	GtkTextIter insert_pos;
3802 	GtkTextMark *start_mark;
3803 	GtkTextMark *end_mark;
3804 	gchar *text;
3805 	gboolean initially_contains_trailing_newline;
3806 	gboolean down;
3807 
3808 	if (copy)
3809 	{
3810 		g_warning ("The 'copy' parameter of GtkSourceView::move-lines is deprecated.");
3811 	}
3812 
3813 	if (step != 1 && step != -1)
3814 	{
3815 		g_warning ("The 'count' parameter of GtkSourceView::move-lines should be either 1 or -1.");
3816 	}
3817 
3818 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
3819 
3820 	if (step == 0 || !gtk_text_view_get_editable (GTK_TEXT_VIEW (view)))
3821 	{
3822 		return;
3823 	}
3824 
3825 	/* FIXME: for now we just handle a step of one line */
3826 
3827 	down = step > 0;
3828 
3829 	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
3830 
3831 	/* Get the entire lines, including the paragraph terminator. */
3832 	gtk_text_iter_set_line_offset (&start, 0);
3833 	if (!gtk_text_iter_starts_line (&end) ||
3834 	    gtk_text_iter_get_line (&start) == gtk_text_iter_get_line (&end))
3835 	{
3836 		gtk_text_iter_forward_line (&end);
3837 	}
3838 
3839 	if ((!down && gtk_text_iter_is_start (&start)) ||
3840 	    (down && gtk_text_iter_is_end (&end)))
3841 	{
3842 		/* Nothing to do, and the undo/redo history must remain
3843 		 * unchanged.
3844 		 */
3845 		return;
3846 	}
3847 
3848 	start_mark = gtk_text_buffer_create_mark (buffer, NULL, &start, TRUE);
3849 	end_mark = gtk_text_buffer_create_mark (buffer, NULL, &end, FALSE);
3850 
3851 	gtk_text_buffer_begin_user_action (buffer);
3852 
3853 	initially_contains_trailing_newline = buffer_contains_trailing_newline (buffer);
3854 
3855 	if (!initially_contains_trailing_newline)
3856 	{
3857 		/* Insert a trailing newline. */
3858 		gtk_text_buffer_get_end_iter (buffer, &end);
3859 		gtk_text_buffer_insert (buffer, &end, "\n", -1);
3860 	}
3861 
3862 	/* At this point all lines finish with a newline or carriage return, so
3863 	 * there are no special cases for the last line.
3864 	 */
3865 
3866 	gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
3867 	gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
3868 	gtk_text_buffer_delete_mark (buffer, start_mark);
3869 	gtk_text_buffer_delete_mark (buffer, end_mark);
3870 	start_mark = NULL;
3871 	end_mark = NULL;
3872 
3873 	text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
3874 
3875 	if (!copy)
3876 	{
3877 		gtk_text_buffer_delete (buffer, &start, &end);
3878 	}
3879 
3880 	if (down)
3881 	{
3882 		insert_pos = end;
3883 		gtk_text_iter_forward_line (&insert_pos);
3884 	}
3885 	else
3886 	{
3887 		insert_pos = start;
3888 		gtk_text_iter_backward_line (&insert_pos);
3889 	}
3890 
3891 	start_mark = gtk_text_buffer_create_mark (buffer, NULL, &insert_pos, TRUE);
3892 
3893 	gtk_text_buffer_insert (buffer, &insert_pos, text, -1);
3894 	g_free (text);
3895 
3896 	/* Select the moved text. */
3897 	gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
3898 	gtk_text_buffer_delete_mark (buffer, start_mark);
3899 
3900 	gtk_text_buffer_select_range (buffer, &start, &insert_pos);
3901 
3902 	if (!initially_contains_trailing_newline)
3903 	{
3904 		remove_trailing_newline (buffer);
3905 	}
3906 
3907 	gtk_text_buffer_end_user_action (buffer);
3908 
3909 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
3910 					    gtk_text_buffer_get_insert (buffer));
3911 }
3912 
3913 static gboolean
do_smart_backspace(GtkSourceView * view)3914 do_smart_backspace (GtkSourceView *view)
3915 {
3916 	GtkTextBuffer *buffer;
3917 	gboolean default_editable;
3918 	GtkTextIter insert;
3919 	GtkTextIter end;
3920 	GtkTextIter leading_end;
3921 	guint visual_column;
3922 	gint indent_width;
3923 
3924 	buffer = GTK_TEXT_BUFFER (view->priv->source_buffer);
3925 	default_editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view));
3926 
3927 	if (gtk_text_buffer_get_selection_bounds (buffer, &insert, &end))
3928 	{
3929 		return FALSE;
3930 	}
3931 
3932 	/* If the line isn't empty up to our cursor, ignore. */
3933 	_gtk_source_iter_get_leading_spaces_end_boundary (&insert, &leading_end);
3934 	if (gtk_text_iter_compare (&leading_end, &insert) < 0)
3935 	{
3936 		return FALSE;
3937 	}
3938 
3939 	visual_column = gtk_source_view_get_visual_column (view, &insert);
3940 	indent_width = view->priv->indent_width;
3941 	if (indent_width <= 0)
3942 	{
3943 		indent_width = view->priv->tab_width;
3944 	}
3945 
3946 	g_return_val_if_fail (indent_width > 0, FALSE);
3947 
3948 	/* If the cursor is not at an indent_width boundary, it probably means
3949 	 * that we want to adjust the spaces.
3950 	 */
3951 	if ((gint)visual_column < indent_width)
3952 	{
3953 		return FALSE;
3954 	}
3955 
3956 	if ((visual_column % indent_width) == 0)
3957 	{
3958 		guint target_column;
3959 
3960 		g_assert ((gint)visual_column >= indent_width);
3961 		target_column = visual_column - indent_width;
3962 
3963 		while (gtk_source_view_get_visual_column (view, &insert) > target_column)
3964 		{
3965 			gtk_text_iter_backward_cursor_position (&insert);
3966 		}
3967 
3968 		gtk_text_buffer_begin_user_action (buffer);
3969 		gtk_text_buffer_delete_interactive (buffer, &insert, &end, default_editable);
3970 		while (gtk_source_view_get_visual_column (view, &insert) < target_column)
3971 		{
3972 			if (!gtk_text_buffer_insert_interactive (buffer, &insert, " ", 1, default_editable))
3973 			{
3974 				break;
3975 			}
3976 		}
3977 		gtk_text_buffer_end_user_action (buffer);
3978 
3979 		return TRUE;
3980 	}
3981 
3982 	return FALSE;
3983 }
3984 
3985 static gboolean
do_ctrl_backspace(GtkSourceView * view)3986 do_ctrl_backspace (GtkSourceView *view)
3987 {
3988 	GtkTextBuffer *buffer;
3989 	GtkTextIter insert;
3990 	GtkTextIter end;
3991 	GtkTextIter leading_end;
3992 	gboolean default_editable;
3993 
3994 	buffer = GTK_TEXT_BUFFER (view->priv->source_buffer);
3995 	default_editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view));
3996 
3997 	if (gtk_text_buffer_get_selection_bounds (buffer, &insert, &end))
3998 	{
3999 		return FALSE;
4000 	}
4001 
4002 	/* A <Control>BackSpace at the beginning of the line should only move us to the
4003 	 * end of the previous line. Anything more than that is non-obvious because it requires
4004 	 * looking in a position other than where the cursor is.
4005 	 */
4006 	if ((gtk_text_iter_get_line_offset (&insert) == 0) &&
4007 	    (gtk_text_iter_get_line (&insert) > 0))
4008 	{
4009 		gtk_text_iter_backward_cursor_position (&insert);
4010 		gtk_text_buffer_delete_interactive (buffer, &insert, &end, default_editable);
4011 		return TRUE;
4012 	}
4013 
4014 	/* If only leading whitespaces are on the left of the cursor, delete up
4015 	 * to the zero position.
4016 	 */
4017 	_gtk_source_iter_get_leading_spaces_end_boundary (&insert, &leading_end);
4018 	if (gtk_text_iter_compare (&insert, &leading_end) <= 0)
4019 	{
4020 		gtk_text_iter_set_line_offset (&insert, 0);
4021 		gtk_text_buffer_delete_interactive (buffer, &insert, &end, default_editable);
4022 		return TRUE;
4023 	}
4024 
4025 	return FALSE;
4026 }
4027 
4028 static gboolean
gtk_source_view_key_press_event(GtkWidget * widget,GdkEventKey * event)4029 gtk_source_view_key_press_event (GtkWidget   *widget,
4030 				 GdkEventKey *event)
4031 {
4032 	GtkSourceView *view;
4033 	GtkTextBuffer *buf;
4034 	GtkTextIter cur;
4035 	GtkTextMark *mark;
4036 	guint modifiers;
4037 	gint key;
4038 	gboolean editable;
4039 
4040 	view = GTK_SOURCE_VIEW (widget);
4041 	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
4042 
4043 	editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (widget));
4044 
4045 	/* Be careful when testing for modifier state equality:
4046 	 * caps lock, num lock,etc need to be taken into account */
4047 	modifiers = gtk_accelerator_get_default_mod_mask ();
4048 
4049 	key = event->keyval;
4050 
4051 	mark = gtk_text_buffer_get_insert (buf);
4052 	gtk_text_buffer_get_iter_at_mark (buf, &cur, mark);
4053 
4054 	if ((key == GDK_KEY_Return || key == GDK_KEY_KP_Enter) &&
4055 	    !(event->state & GDK_SHIFT_MASK) &&
4056 	    view->priv->auto_indent)
4057 	{
4058 		/* Auto-indent means that when you press ENTER at the end of a
4059 		 * line, the new line is automatically indented at the same
4060 		 * level as the previous line.
4061 		 * SHIFT+ENTER allows to avoid autoindentation.
4062 		 */
4063 		gchar *indent = NULL;
4064 
4065 		/* Calculate line indentation and create indent string. */
4066 		indent = compute_indentation (view, &cur);
4067 
4068 		if (indent != NULL)
4069 		{
4070 			/* Allow input methods to internally handle a key press event.
4071 			 * If this function returns TRUE, then no further processing should be done
4072 			 * for this keystroke. */
4073 			if (gtk_text_view_im_context_filter_keypress (GTK_TEXT_VIEW (view), event))
4074 			{
4075 				g_free (indent);
4076 				return GDK_EVENT_STOP;
4077 			}
4078 
4079 			/* If an input method has inserted some text while handling the key press event,
4080 			 * the cur iterm may be invalid, so get the iter again */
4081 			gtk_text_buffer_get_iter_at_mark (buf, &cur, mark);
4082 
4083 			/* Insert new line and auto-indent. */
4084 			gtk_text_buffer_begin_user_action (buf);
4085 			gtk_text_buffer_insert (buf, &cur, "\n", 1);
4086 			gtk_text_buffer_insert (buf, &cur, indent, strlen (indent));
4087 			g_free (indent);
4088 			gtk_text_buffer_end_user_action (buf);
4089 			gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (widget),
4090 							    mark);
4091 			return GDK_EVENT_STOP;
4092 		}
4093 	}
4094 
4095 	/* if tab or shift+tab:
4096 	 * with shift+tab key is GDK_ISO_Left_Tab (yay! on win32 and mac too!)
4097 	 */
4098 	if ((key == GDK_KEY_Tab || key == GDK_KEY_KP_Tab || key == GDK_KEY_ISO_Left_Tab) &&
4099 	    ((event->state & modifiers) == 0 ||
4100 	     (event->state & modifiers) == GDK_SHIFT_MASK) &&
4101 	    editable &&
4102 	    gtk_text_view_get_accepts_tab (GTK_TEXT_VIEW (view)))
4103 	{
4104 		GtkTextIter s, e;
4105 		gboolean has_selection;
4106 
4107 		has_selection = gtk_text_buffer_get_selection_bounds (buf, &s, &e);
4108 
4109 		if (view->priv->indent_on_tab)
4110 		{
4111 			/* shift+tab: always unindent */
4112 			if (event->state & GDK_SHIFT_MASK)
4113 			{
4114 				_gtk_source_buffer_save_and_clear_selection (GTK_SOURCE_BUFFER (buf));
4115 				gtk_source_view_unindent_lines (view, &s, &e);
4116 				_gtk_source_buffer_restore_selection (GTK_SOURCE_BUFFER (buf));
4117 				return GDK_EVENT_STOP;
4118 			}
4119 
4120 			/* tab: if we have a selection which spans one whole line
4121 			 * or more, we mass indent, if the selection spans less then
4122 			 * the full line just replace the text with \t
4123 			 */
4124 			if (has_selection &&
4125 			    ((gtk_text_iter_starts_line (&s) && gtk_text_iter_ends_line (&e)) ||
4126 			     (gtk_text_iter_get_line (&s) != gtk_text_iter_get_line (&e))))
4127 			{
4128 				_gtk_source_buffer_save_and_clear_selection (GTK_SOURCE_BUFFER (buf));
4129 				gtk_source_view_indent_lines (view, &s, &e);
4130 				_gtk_source_buffer_restore_selection (GTK_SOURCE_BUFFER (buf));
4131 				return GDK_EVENT_STOP;
4132 			}
4133 		}
4134 
4135 		insert_tab_or_spaces (view, &s, &e);
4136 		return GDK_EVENT_STOP;
4137 	}
4138 
4139 	if (key == GDK_KEY_BackSpace)
4140 	{
4141 		if ((event->state & modifiers) == 0)
4142 		{
4143 			if (view->priv->smart_backspace && do_smart_backspace (view))
4144 			{
4145 				return GDK_EVENT_STOP;
4146 			}
4147 		}
4148 		else if ((event->state & modifiers) == GDK_CONTROL_MASK)
4149 		{
4150 			if (do_ctrl_backspace (view))
4151 			{
4152 				return GDK_EVENT_STOP;
4153 			}
4154 		}
4155 	}
4156 
4157 	return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->key_press_event (widget, event);
4158 }
4159 
4160 /**
4161  * gtk_source_view_get_auto_indent:
4162  * @view: a #GtkSourceView.
4163  *
4164  * Returns whether auto-indentation of text is enabled.
4165  *
4166  * Returns: %TRUE if auto indentation is enabled.
4167  */
4168 gboolean
gtk_source_view_get_auto_indent(GtkSourceView * view)4169 gtk_source_view_get_auto_indent (GtkSourceView *view)
4170 {
4171 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4172 
4173 	return view->priv->auto_indent;
4174 }
4175 
4176 /**
4177  * gtk_source_view_set_auto_indent:
4178  * @view: a #GtkSourceView.
4179  * @enable: whether to enable auto indentation.
4180  *
4181  * If %TRUE auto-indentation of text is enabled.
4182  *
4183  * When Enter is pressed to create a new line, the auto-indentation inserts the
4184  * same indentation as the previous line. This is <emphasis>not</emphasis> a
4185  * "smart indentation" where an indentation level is added or removed depending
4186  * on the context.
4187  */
4188 void
gtk_source_view_set_auto_indent(GtkSourceView * view,gboolean enable)4189 gtk_source_view_set_auto_indent (GtkSourceView *view,
4190 				 gboolean       enable)
4191 {
4192 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4193 
4194 	enable = enable != FALSE;
4195 
4196 	if (view->priv->auto_indent != enable)
4197 	{
4198 		view->priv->auto_indent = enable;
4199 		g_object_notify (G_OBJECT (view), "auto_indent");
4200 	}
4201 }
4202 
4203 /**
4204  * gtk_source_view_get_insert_spaces_instead_of_tabs:
4205  * @view: a #GtkSourceView.
4206  *
4207  * Returns whether when inserting a tabulator character it should
4208  * be replaced by a group of space characters.
4209  *
4210  * Returns: %TRUE if spaces are inserted instead of tabs.
4211  */
4212 gboolean
gtk_source_view_get_insert_spaces_instead_of_tabs(GtkSourceView * view)4213 gtk_source_view_get_insert_spaces_instead_of_tabs (GtkSourceView *view)
4214 {
4215 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4216 
4217 	return view->priv->insert_spaces;
4218 }
4219 
4220 /**
4221  * gtk_source_view_set_insert_spaces_instead_of_tabs:
4222  * @view: a #GtkSourceView.
4223  * @enable: whether to insert spaces instead of tabs.
4224  *
4225  * If %TRUE a tab key pressed is replaced by a group of space characters. Of
4226  * course it is still possible to insert a real \t programmatically with the
4227  * #GtkTextBuffer API.
4228  */
4229 void
gtk_source_view_set_insert_spaces_instead_of_tabs(GtkSourceView * view,gboolean enable)4230 gtk_source_view_set_insert_spaces_instead_of_tabs (GtkSourceView *view,
4231 						   gboolean       enable)
4232 {
4233 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4234 
4235 	enable = enable != FALSE;
4236 
4237 	if (view->priv->insert_spaces != enable)
4238 	{
4239 		view->priv->insert_spaces = enable;
4240 		g_object_notify (G_OBJECT (view), "insert_spaces_instead_of_tabs");
4241 	}
4242 }
4243 
4244 /**
4245  * gtk_source_view_get_indent_on_tab:
4246  * @view: a #GtkSourceView.
4247  *
4248  * Returns whether when the tab key is pressed the current selection
4249  * should get indented instead of replaced with the \t character.
4250  *
4251  * Return value: %TRUE if the selection is indented when tab is pressed.
4252  */
4253 gboolean
gtk_source_view_get_indent_on_tab(GtkSourceView * view)4254 gtk_source_view_get_indent_on_tab (GtkSourceView *view)
4255 {
4256 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4257 
4258 	return view->priv->indent_on_tab;
4259 }
4260 
4261 /**
4262  * gtk_source_view_set_indent_on_tab:
4263  * @view: a #GtkSourceView.
4264  * @enable: whether to indent a block when tab is pressed.
4265  *
4266  * If %TRUE, when the tab key is pressed when several lines are selected, the
4267  * selected lines are indented of one level instead of being replaced with a \t
4268  * character. Shift+Tab unindents the selection.
4269  *
4270  * If the first or last line is not selected completely, it is also indented or
4271  * unindented.
4272  *
4273  * When the selection doesn't span several lines, the tab key always replaces
4274  * the selection with a normal \t character.
4275  */
4276 void
gtk_source_view_set_indent_on_tab(GtkSourceView * view,gboolean enable)4277 gtk_source_view_set_indent_on_tab (GtkSourceView *view,
4278 				   gboolean       enable)
4279 {
4280 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4281 
4282 	enable = enable != FALSE;
4283 
4284 	if (view->priv->indent_on_tab != enable)
4285 	{
4286 		view->priv->indent_on_tab = enable;
4287 		g_object_notify (G_OBJECT (view), "indent_on_tab");
4288 	}
4289 }
4290 
4291 static void
view_dnd_drop(GtkTextView * view,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint timestamp,gpointer data)4292 view_dnd_drop (GtkTextView      *view,
4293 	       GdkDragContext   *context,
4294 	       gint              x,
4295 	       gint              y,
4296 	       GtkSelectionData *selection_data,
4297 	       guint             info,
4298 	       guint             timestamp,
4299 	       gpointer          data)
4300 {
4301 
4302 	GtkTextIter iter;
4303 
4304 	if (info == TARGET_COLOR)
4305 	{
4306 		guint16 *vals;
4307 		gchar string[] = "#000000";
4308 		gint buffer_x;
4309 		gint buffer_y;
4310 		gint length = gtk_selection_data_get_length (selection_data);
4311 
4312 		if (length < 0)
4313 		{
4314 			return;
4315 		}
4316 
4317 		if (gtk_selection_data_get_format (selection_data) != 16 || length != 8)
4318 		{
4319 			g_warning ("Received invalid color data\n");
4320 			return;
4321 		}
4322 
4323 		vals = (gpointer) gtk_selection_data_get_data (selection_data);
4324 
4325 		vals[0] /= 256;
4326 	        vals[1] /= 256;
4327 		vals[2] /= 256;
4328 
4329 		g_snprintf (string, sizeof (string), "#%02X%02X%02X", vals[0], vals[1], vals[2]);
4330 
4331 		gtk_text_view_window_to_buffer_coords (view,
4332 						       GTK_TEXT_WINDOW_TEXT,
4333 						       x,
4334 						       y,
4335 						       &buffer_x,
4336 						       &buffer_y);
4337 		gtk_text_view_get_iter_at_location (view, &iter, buffer_x, buffer_y);
4338 
4339 		if (gtk_text_view_get_editable (view))
4340 		{
4341 			gtk_text_buffer_insert (gtk_text_view_get_buffer (view),
4342 						&iter,
4343 						string,
4344 						strlen (string));
4345 			gtk_text_buffer_place_cursor (gtk_text_view_get_buffer (view),
4346 						&iter);
4347 		}
4348 
4349 		/*
4350 		 * FIXME: Check if the iter is inside a selection
4351 		 * If it is, remove the selection and then insert at
4352 		 * the cursor position - Paolo
4353 		 */
4354 
4355 		return;
4356 	}
4357 }
4358 
4359 /**
4360  * gtk_source_view_get_highlight_current_line:
4361  * @view: a #GtkSourceView.
4362  *
4363  * Returns whether the current line is highlighted.
4364  *
4365  * Return value: %TRUE if the current line is highlighted.
4366  */
4367 gboolean
gtk_source_view_get_highlight_current_line(GtkSourceView * view)4368 gtk_source_view_get_highlight_current_line (GtkSourceView *view)
4369 {
4370 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4371 
4372 	return view->priv->highlight_current_line;
4373 }
4374 
4375 /**
4376  * gtk_source_view_set_highlight_current_line:
4377  * @view: a #GtkSourceView.
4378  * @highlight: whether to highlight the current line.
4379  *
4380  * If @highlight is %TRUE the current line will be highlighted.
4381  */
4382 void
gtk_source_view_set_highlight_current_line(GtkSourceView * view,gboolean highlight)4383 gtk_source_view_set_highlight_current_line (GtkSourceView *view,
4384 					    gboolean       highlight)
4385 {
4386 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4387 
4388 	highlight = highlight != FALSE;
4389 
4390 	if (view->priv->highlight_current_line != highlight)
4391 	{
4392 		view->priv->highlight_current_line = highlight;
4393 
4394 		gtk_widget_queue_draw (GTK_WIDGET (view));
4395 
4396 		g_object_notify (G_OBJECT (view), "highlight_current_line");
4397 	}
4398 }
4399 
4400 /**
4401  * gtk_source_view_get_show_right_margin:
4402  * @view: a #GtkSourceView.
4403  *
4404  * Returns whether a right margin is displayed.
4405  *
4406  * Return value: %TRUE if the right margin is shown.
4407  */
4408 gboolean
gtk_source_view_get_show_right_margin(GtkSourceView * view)4409 gtk_source_view_get_show_right_margin (GtkSourceView *view)
4410 {
4411 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4412 
4413 	return view->priv->show_right_margin;
4414 }
4415 
4416 /**
4417  * gtk_source_view_set_show_right_margin:
4418  * @view: a #GtkSourceView.
4419  * @show: whether to show a right margin.
4420  *
4421  * If %TRUE a right margin is displayed.
4422  */
4423 void
gtk_source_view_set_show_right_margin(GtkSourceView * view,gboolean show)4424 gtk_source_view_set_show_right_margin (GtkSourceView *view,
4425 				       gboolean       show)
4426 {
4427 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4428 
4429 	show = show != FALSE;
4430 
4431 	if (view->priv->show_right_margin != show)
4432 	{
4433 		view->priv->show_right_margin = show;
4434 
4435 		gtk_widget_queue_draw (GTK_WIDGET (view));
4436 
4437 		g_object_notify (G_OBJECT (view), "show-right-margin");
4438 	}
4439 }
4440 
4441 /**
4442  * gtk_source_view_get_right_margin_position:
4443  * @view: a #GtkSourceView.
4444  *
4445  * Gets the position of the right margin in the given @view.
4446  *
4447  * Return value: the position of the right margin.
4448  */
4449 guint
gtk_source_view_get_right_margin_position(GtkSourceView * view)4450 gtk_source_view_get_right_margin_position (GtkSourceView *view)
4451 {
4452 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), DEFAULT_RIGHT_MARGIN_POSITION);
4453 
4454 	return view->priv->right_margin_pos;
4455 }
4456 
4457 /**
4458  * gtk_source_view_set_right_margin_position:
4459  * @view: a #GtkSourceView.
4460  * @pos: the width in characters where to position the right margin.
4461  *
4462  * Sets the position of the right margin in the given @view.
4463  */
4464 void
gtk_source_view_set_right_margin_position(GtkSourceView * view,guint pos)4465 gtk_source_view_set_right_margin_position (GtkSourceView *view,
4466 					   guint          pos)
4467 {
4468 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4469 	g_return_if_fail (1 <= pos && pos <= MAX_RIGHT_MARGIN_POSITION);
4470 
4471 	if (view->priv->right_margin_pos != pos)
4472 	{
4473 		view->priv->right_margin_pos = pos;
4474 		view->priv->cached_right_margin_pos = -1;
4475 
4476 		gtk_widget_queue_draw (GTK_WIDGET (view));
4477 
4478 		g_object_notify (G_OBJECT (view), "right-margin-position");
4479 	}
4480 }
4481 
4482 /**
4483  * gtk_source_view_set_smart_backspace:
4484  * @view: a #GtkSourceView.
4485  * @smart_backspace: whether to enable smart Backspace handling.
4486  *
4487  * When set to %TRUE, pressing the Backspace key will try to delete spaces
4488  * up to the previous tab stop.
4489  *
4490  * Since: 3.18
4491  */
4492 void
gtk_source_view_set_smart_backspace(GtkSourceView * view,gboolean smart_backspace)4493 gtk_source_view_set_smart_backspace (GtkSourceView *view,
4494                                      gboolean       smart_backspace)
4495 {
4496 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4497 
4498 	smart_backspace = smart_backspace != FALSE;
4499 
4500 	if (smart_backspace != view->priv->smart_backspace)
4501 	{
4502 		view->priv->smart_backspace = smart_backspace;
4503 		g_object_notify (G_OBJECT (view), "smart-backspace");
4504 	}
4505 }
4506 
4507 /**
4508  * gtk_source_view_get_smart_backspace:
4509  * @view: a #GtkSourceView.
4510  *
4511  * Returns %TRUE if pressing the Backspace key will try to delete spaces
4512  * up to the previous tab stop.
4513  *
4514  * Returns: %TRUE if smart Backspace handling is enabled.
4515  *
4516  * Since: 3.18
4517  */
4518 gboolean
gtk_source_view_get_smart_backspace(GtkSourceView * view)4519 gtk_source_view_get_smart_backspace (GtkSourceView *view)
4520 {
4521 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4522 
4523 	return view->priv->smart_backspace;
4524 }
4525 
4526 /**
4527  * gtk_source_view_set_smart_home_end:
4528  * @view: a #GtkSourceView.
4529  * @smart_home_end: the desired behavior among #GtkSourceSmartHomeEndType.
4530  *
4531  * Set the desired movement of the cursor when HOME and END keys
4532  * are pressed.
4533  */
4534 void
gtk_source_view_set_smart_home_end(GtkSourceView * view,GtkSourceSmartHomeEndType smart_home_end)4535 gtk_source_view_set_smart_home_end (GtkSourceView             *view,
4536 				    GtkSourceSmartHomeEndType  smart_home_end)
4537 {
4538 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4539 
4540 	if (view->priv->smart_home_end != smart_home_end)
4541 	{
4542 		view->priv->smart_home_end = smart_home_end;
4543 		g_object_notify (G_OBJECT (view), "smart_home_end");
4544 	}
4545 }
4546 
4547 /**
4548  * gtk_source_view_get_smart_home_end:
4549  * @view: a #GtkSourceView.
4550  *
4551  * Returns a #GtkSourceSmartHomeEndType end value specifying
4552  * how the cursor will move when HOME and END keys are pressed.
4553  *
4554  * Returns: a #GtkSourceSmartHomeEndType value.
4555  */
4556 GtkSourceSmartHomeEndType
gtk_source_view_get_smart_home_end(GtkSourceView * view)4557 gtk_source_view_get_smart_home_end (GtkSourceView *view)
4558 {
4559 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
4560 
4561 	return view->priv->smart_home_end;
4562 }
4563 
4564 /**
4565  * gtk_source_view_set_draw_spaces:
4566  * @view: a #GtkSourceView.
4567  * @flags: #GtkSourceDrawSpacesFlags specifing how white spaces should
4568  * be displayed
4569  *
4570  * Set if and how the spaces should be visualized. Specifying @flags as 0 will
4571  * disable display of spaces.
4572  *
4573  * For a finer-grained method, there is also the GtkSourceTag's
4574  * #GtkSourceTag:draw-spaces property.
4575  *
4576  * Deprecated: 3.24: Use gtk_source_space_drawer_set_types_for_locations()
4577  * instead.
4578  */
4579 void
gtk_source_view_set_draw_spaces(GtkSourceView * view,GtkSourceDrawSpacesFlags flags)4580 gtk_source_view_set_draw_spaces (GtkSourceView            *view,
4581 				 GtkSourceDrawSpacesFlags  flags)
4582 {
4583 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4584 
4585 	if (view->priv->space_drawer == NULL)
4586 	{
4587 		return;
4588 	}
4589 
4590 	_gtk_source_space_drawer_set_flags (view->priv->space_drawer, flags);
4591 }
4592 
4593 /**
4594  * gtk_source_view_get_draw_spaces:
4595  * @view: a #GtkSourceView
4596  *
4597  * Returns the #GtkSourceDrawSpacesFlags specifying if and how spaces
4598  * should be displayed for this @view.
4599  *
4600  * Returns: the #GtkSourceDrawSpacesFlags, 0 if no spaces should be drawn.
4601  * Deprecated: 3.24: Use gtk_source_space_drawer_get_types_for_locations()
4602  * instead.
4603  */
4604 GtkSourceDrawSpacesFlags
gtk_source_view_get_draw_spaces(GtkSourceView * view)4605 gtk_source_view_get_draw_spaces (GtkSourceView *view)
4606 {
4607 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), 0);
4608 
4609 	if (view->priv->space_drawer == NULL)
4610 	{
4611 		return 0;
4612 	}
4613 
4614 	return _gtk_source_space_drawer_get_flags (view->priv->space_drawer);
4615 }
4616 
4617 /**
4618  * gtk_source_view_get_visual_column:
4619  * @view: a #GtkSourceView.
4620  * @iter: a position in @view.
4621  *
4622  * Determines the visual column at @iter taking into consideration the
4623  * #GtkSourceView:tab-width of @view.
4624  *
4625  * Returns: the visual column at @iter.
4626  */
4627 guint
gtk_source_view_get_visual_column(GtkSourceView * view,const GtkTextIter * iter)4628 gtk_source_view_get_visual_column (GtkSourceView     *view,
4629 				   const GtkTextIter *iter)
4630 {
4631 	gunichar tab_char;
4632 	GtkTextIter position;
4633 	guint column, indent_width;
4634 
4635 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), 0);
4636 	g_return_val_if_fail (iter != NULL, 0);
4637 
4638 	tab_char = g_utf8_get_char ("\t");
4639 
4640 	column = 0;
4641 	indent_width = get_real_indent_width (view);
4642 
4643 	position = *iter;
4644 	gtk_text_iter_set_line_offset (&position, 0);
4645 
4646 	while (!gtk_text_iter_equal (&position, iter))
4647 	{
4648 		if (gtk_text_iter_get_char (&position) == tab_char)
4649 		{
4650 			column += (indent_width - (column % indent_width));
4651 		}
4652 		else
4653 		{
4654 			++column;
4655 		}
4656 
4657 		/* FIXME: this does not handle invisible text correctly, but
4658 		 * gtk_text_iter_forward_visible_cursor_position is too slow */
4659 		if (!gtk_text_iter_forward_char (&position))
4660 		{
4661 			break;
4662 		}
4663 	}
4664 
4665 	return column;
4666 }
4667 
4668 static void
update_background_pattern_color(GtkSourceView * view)4669 update_background_pattern_color (GtkSourceView *view)
4670 {
4671 	if (view->priv->style_scheme == NULL)
4672 	{
4673 		view->priv->background_pattern_color_set = FALSE;
4674 		return;
4675 	}
4676 
4677 	view->priv->background_pattern_color_set =
4678 		_gtk_source_style_scheme_get_background_pattern_color (view->priv->style_scheme,
4679 								       &view->priv->background_pattern_color);
4680 }
4681 
4682 static void
update_current_line_color(GtkSourceView * view)4683 update_current_line_color (GtkSourceView *view)
4684 {
4685 	if (view->priv->style_scheme == NULL)
4686 	{
4687 		view->priv->current_line_color_set = FALSE;
4688 		return;
4689 	}
4690 
4691 	view->priv->current_line_color_set =
4692 		_gtk_source_style_scheme_get_current_line_color (view->priv->style_scheme,
4693 								 &view->priv->current_line_color);
4694 }
4695 
4696 static void
update_right_margin_colors(GtkSourceView * view)4697 update_right_margin_colors (GtkSourceView *view)
4698 {
4699 	GtkWidget *widget = GTK_WIDGET (view);
4700 
4701 	if (view->priv->right_margin_line_color != NULL)
4702 	{
4703 		gdk_rgba_free (view->priv->right_margin_line_color);
4704 		view->priv->right_margin_line_color = NULL;
4705 	}
4706 
4707 	if (view->priv->right_margin_overlay_color != NULL)
4708 	{
4709 		gdk_rgba_free (view->priv->right_margin_overlay_color);
4710 		view->priv->right_margin_overlay_color = NULL;
4711 	}
4712 
4713 	if (view->priv->style_scheme != NULL)
4714 	{
4715 		GtkSourceStyle *style;
4716 
4717 		style = _gtk_source_style_scheme_get_right_margin_style (view->priv->style_scheme);
4718 
4719 		if (style != NULL)
4720 		{
4721 			gchar *color_str = NULL;
4722 			gboolean color_set;
4723 			GdkRGBA color;
4724 
4725 			g_object_get (style,
4726 				      "foreground", &color_str,
4727 				      "foreground-set", &color_set,
4728 				      NULL);
4729 
4730 			if (color_set &&
4731 			    color_str != NULL &&
4732 			    gdk_rgba_parse (&color, color_str))
4733 			{
4734 				view->priv->right_margin_line_color = gdk_rgba_copy (&color);
4735 				view->priv->right_margin_line_color->alpha =
4736 					RIGHT_MARGIN_LINE_ALPHA / 255.;
4737 			}
4738 
4739 			g_free (color_str);
4740 			color_str = NULL;
4741 
4742 			g_object_get (style,
4743 				      "background", &color_str,
4744 				      "background-set", &color_set,
4745 				      NULL);
4746 
4747 			if (color_set &&
4748 			    color_str != NULL &&
4749 			    gdk_rgba_parse (&color, color_str))
4750 			{
4751 				view->priv->right_margin_overlay_color = gdk_rgba_copy (&color);
4752 				view->priv->right_margin_overlay_color->alpha =
4753 					RIGHT_MARGIN_OVERLAY_ALPHA / 255.;
4754 			}
4755 
4756 			g_free (color_str);
4757 		}
4758 	}
4759 
4760 	if (view->priv->right_margin_line_color == NULL)
4761 	{
4762 		GtkStyleContext *context;
4763 		GdkRGBA color;
4764 
4765 		context = gtk_widget_get_style_context (widget);
4766 		gtk_style_context_save (context);
4767 		gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
4768 		gtk_style_context_get_color (context,
4769 					     gtk_style_context_get_state (context),
4770 					     &color);
4771 		gtk_style_context_restore (context);
4772 
4773 		view->priv->right_margin_line_color = gdk_rgba_copy (&color);
4774 		view->priv->right_margin_line_color->alpha =
4775 			RIGHT_MARGIN_LINE_ALPHA / 255.;
4776 	}
4777 }
4778 
4779 static void
update_style(GtkSourceView * view)4780 update_style (GtkSourceView *view)
4781 {
4782 	update_background_pattern_color (view);
4783 	update_current_line_color (view);
4784 	update_right_margin_colors (view);
4785 
4786 	if (view->priv->space_drawer != NULL)
4787 	{
4788 		_gtk_source_space_drawer_update_color (view->priv->space_drawer, view);
4789 	}
4790 
4791 	gtk_widget_queue_draw (GTK_WIDGET (view));
4792 }
4793 
4794 static void
gtk_source_view_update_style_scheme(GtkSourceView * view)4795 gtk_source_view_update_style_scheme (GtkSourceView *view)
4796 {
4797 	GtkTextBuffer *buffer;
4798 	GtkSourceStyleScheme *new_scheme = NULL;
4799 
4800 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
4801 
4802 	if (GTK_SOURCE_IS_BUFFER (buffer))
4803 	{
4804 		new_scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer));
4805 	}
4806 
4807 	if (view->priv->style_scheme == new_scheme)
4808 	{
4809 		return;
4810 	}
4811 
4812 	if (view->priv->style_scheme != NULL)
4813 	{
4814 		_gtk_source_style_scheme_unapply (view->priv->style_scheme, view);
4815 	}
4816 
4817 	g_set_object (&view->priv->style_scheme, new_scheme);
4818 
4819 	if (view->priv->style_scheme != NULL)
4820 	{
4821 		_gtk_source_style_scheme_apply (view->priv->style_scheme, view);
4822 	}
4823 
4824 	update_style (view);
4825 }
4826 
4827 static void
gtk_source_view_style_updated(GtkWidget * widget)4828 gtk_source_view_style_updated (GtkWidget *widget)
4829 {
4830 	GtkSourceView *view = GTK_SOURCE_VIEW (widget);
4831 
4832 	/* Call default handler first. */
4833 	if (GTK_WIDGET_CLASS (gtk_source_view_parent_class)->style_updated != NULL)
4834 	{
4835 		GTK_WIDGET_CLASS (gtk_source_view_parent_class)->style_updated (widget);
4836 	}
4837 
4838 	/* Re-set tab stops, but only if we already modified them, i.e.
4839 	 * do nothing with good old 8-space tabs.
4840 	 */
4841 	if (view->priv->tabs_set)
4842 	{
4843 		set_tab_stops_internal (view);
4844 	}
4845 
4846 	/* Make sure the margin position is recalculated on next redraw. */
4847 	view->priv->cached_right_margin_pos = -1;
4848 
4849 	update_style (view);
4850 }
4851 
4852 static MarkCategory *
mark_category_new(GtkSourceMarkAttributes * attributes,gint priority)4853 mark_category_new (GtkSourceMarkAttributes *attributes,
4854 		   gint                     priority)
4855 {
4856 	MarkCategory* category = g_slice_new (MarkCategory);
4857 
4858 	category->attributes = g_object_ref (attributes);
4859 	category->priority = priority;
4860 
4861 	return category;
4862 }
4863 
4864 static void
mark_category_free(MarkCategory * category)4865 mark_category_free (MarkCategory *category)
4866 {
4867 	if (category != NULL)
4868 	{
4869 		g_object_unref (category->attributes);
4870 		g_slice_free (MarkCategory, category);
4871 	}
4872 }
4873 
4874 /**
4875  * gtk_source_view_get_completion:
4876  * @view: a #GtkSourceView.
4877  *
4878  * Gets the #GtkSourceCompletion associated with @view. The returned object is
4879  * guaranteed to be the same for the lifetime of @view. Each #GtkSourceView
4880  * object has a different #GtkSourceCompletion.
4881  *
4882  * Returns: (transfer none): the #GtkSourceCompletion associated with @view.
4883  */
4884 GtkSourceCompletion *
gtk_source_view_get_completion(GtkSourceView * view)4885 gtk_source_view_get_completion (GtkSourceView *view)
4886 {
4887 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
4888 
4889 	if (view->priv->completion == NULL)
4890 	{
4891 		view->priv->completion = gtk_source_completion_new (view);
4892 	}
4893 
4894 	return view->priv->completion;
4895 }
4896 
4897 /**
4898  * gtk_source_view_get_gutter:
4899  * @view: a #GtkSourceView.
4900  * @window_type: the gutter window type.
4901  *
4902  * Returns the #GtkSourceGutter object associated with @window_type for @view.
4903  * Only GTK_TEXT_WINDOW_LEFT and GTK_TEXT_WINDOW_RIGHT are supported,
4904  * respectively corresponding to the left and right gutter. The line numbers
4905  * and mark category icons are rendered in the left gutter.
4906  *
4907  * Since: 2.8
4908  *
4909  * Returns: (transfer none): the #GtkSourceGutter.
4910  */
4911 GtkSourceGutter *
gtk_source_view_get_gutter(GtkSourceView * view,GtkTextWindowType window_type)4912 gtk_source_view_get_gutter (GtkSourceView     *view,
4913 			    GtkTextWindowType  window_type)
4914 {
4915 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
4916 	g_return_val_if_fail (window_type == GTK_TEXT_WINDOW_LEFT ||
4917 	                      window_type == GTK_TEXT_WINDOW_RIGHT, NULL);
4918 
4919 	if (window_type == GTK_TEXT_WINDOW_LEFT)
4920 	{
4921 		if (view->priv->left_gutter == NULL)
4922 		{
4923 			view->priv->left_gutter = _gtk_source_gutter_new (view, window_type);
4924 		}
4925 
4926 		return view->priv->left_gutter;
4927 	}
4928 	else
4929 	{
4930 		if (view->priv->right_gutter == NULL)
4931 		{
4932 			view->priv->right_gutter = _gtk_source_gutter_new (view, window_type);
4933 		}
4934 
4935 		return view->priv->right_gutter;
4936 	}
4937 }
4938 
4939 /**
4940  * gtk_source_view_set_mark_attributes:
4941  * @view: a #GtkSourceView.
4942  * @category: the category.
4943  * @attributes: mark attributes.
4944  * @priority: priority of the category.
4945  *
4946  * Sets attributes and priority for the @category.
4947  */
4948 void
gtk_source_view_set_mark_attributes(GtkSourceView * view,const gchar * category,GtkSourceMarkAttributes * attributes,gint priority)4949 gtk_source_view_set_mark_attributes (GtkSourceView           *view,
4950 				     const gchar             *category,
4951 				     GtkSourceMarkAttributes *attributes,
4952 				     gint                     priority)
4953 {
4954 	MarkCategory *mark_category;
4955 
4956 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
4957 	g_return_if_fail (category != NULL);
4958 	g_return_if_fail (GTK_SOURCE_IS_MARK_ATTRIBUTES (attributes));
4959 	g_return_if_fail (priority >= 0);
4960 
4961 	mark_category = mark_category_new (attributes, priority);
4962 	g_hash_table_replace (view->priv->mark_categories,
4963 	                      g_strdup (category),
4964 	                      mark_category);
4965 }
4966 
4967 /**
4968  * gtk_source_view_get_mark_attributes:
4969  * @view: a #GtkSourceView.
4970  * @category: the category.
4971  * @priority: place where priority of the category will be stored.
4972  *
4973  * Gets attributes and priority for the @category.
4974  *
4975  * Returns: (transfer none): #GtkSourceMarkAttributes for the @category.
4976  * The object belongs to @view, so it must not be unreffed.
4977  */
4978 GtkSourceMarkAttributes *
gtk_source_view_get_mark_attributes(GtkSourceView * view,const gchar * category,gint * priority)4979 gtk_source_view_get_mark_attributes (GtkSourceView *view,
4980 				     const gchar   *category,
4981 				     gint          *priority)
4982 {
4983 	MarkCategory *mark_category;
4984 
4985 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
4986 	g_return_val_if_fail (category != NULL, NULL);
4987 
4988 	mark_category = g_hash_table_lookup (view->priv->mark_categories,
4989 	                                     category);
4990 
4991 	if (mark_category != NULL)
4992 	{
4993 		if (priority != NULL)
4994 		{
4995 			*priority = mark_category->priority;
4996 		}
4997 
4998 		return mark_category->attributes;
4999 	}
5000 
5001 	return NULL;
5002 }
5003 
5004 /**
5005  * gtk_source_view_set_background_pattern:
5006  * @view: a #GtkSourceView.
5007  * @background_pattern: the #GtkSourceBackgroundPatternType.
5008  *
5009  * Set if and how the background pattern should be displayed.
5010  *
5011  * Since: 3.16
5012  */
5013 void
gtk_source_view_set_background_pattern(GtkSourceView * view,GtkSourceBackgroundPatternType background_pattern)5014 gtk_source_view_set_background_pattern (GtkSourceView                  *view,
5015 					GtkSourceBackgroundPatternType  background_pattern)
5016 {
5017 	g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
5018 
5019 	if (view->priv->background_pattern != background_pattern)
5020 	{
5021 		view->priv->background_pattern = background_pattern;
5022 
5023 		gtk_widget_queue_draw (GTK_WIDGET (view));
5024 
5025 		g_object_notify (G_OBJECT (view), "background-pattern");
5026 	}
5027 }
5028 
5029 /**
5030  * gtk_source_view_get_background_pattern:
5031  * @view: a #GtkSourceView
5032  *
5033  * Returns the #GtkSourceBackgroundPatternType specifying if and how
5034  * the background pattern should be displayed for this @view.
5035  *
5036  * Returns: the #GtkSourceBackgroundPatternType.
5037  * Since: 3.16
5038  */
5039 GtkSourceBackgroundPatternType
gtk_source_view_get_background_pattern(GtkSourceView * view)5040 gtk_source_view_get_background_pattern (GtkSourceView *view)
5041 {
5042 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), GTK_SOURCE_BACKGROUND_PATTERN_TYPE_NONE);
5043 
5044 	return view->priv->background_pattern;
5045 }
5046 
5047 /**
5048  * gtk_source_view_get_space_drawer:
5049  * @view: a #GtkSourceView.
5050  *
5051  * Gets the #GtkSourceSpaceDrawer associated with @view. The returned object is
5052  * guaranteed to be the same for the lifetime of @view. Each #GtkSourceView
5053  * object has a different #GtkSourceSpaceDrawer.
5054  *
5055  * Returns: (transfer none): the #GtkSourceSpaceDrawer associated with @view.
5056  * Since: 3.24
5057  */
5058 GtkSourceSpaceDrawer *
gtk_source_view_get_space_drawer(GtkSourceView * view)5059 gtk_source_view_get_space_drawer (GtkSourceView *view)
5060 {
5061 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
5062 
5063 	return view->priv->space_drawer;
5064 }
5065