1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
2 /*  gtksourcebuffer.c
3  *
4  *  Copyright (C) 1999,2000,2001,2002 by:
5  *          Mikael Hermansson <tyan@linux.se>
6  *          Chris Phelps <chicane@reninet.com>
7  *          Jeroen Zwartepoorte <jeroen@xs4all.nl>
8  *
9  *  Copyright (C) 2003 - Paolo Maggi <paolo.maggi@polito.it>
10  *          Gustavo Giráldez <gustavo.giraldez@gmx.net>
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU Library General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU Library General Public License for more details.
21  *
22  *  You should have received a copy of the GNU Library General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include <string.h>
32 #include <gtk/gtk.h>
33 
34 #include "gtksourceview-i18n.h"
35 #include "gtksourcelanguage-private.h"
36 #include "gtksourcebuffer.h"
37 #include "gtksourceundomanager.h"
38 #include "gtksourceview-marshal.h"
39 #include "gtksourceiter.h"
40 #include "gtksourcestyleschememanager.h"
41 #include "gtksourcestyle-private.h"
42 #include "gtksourceundomanagerdefault.h"
43 
44 /**
45  * SECTION:buffer
46  * @Short_description: Buffer object for #GtkSourceView
47  * @Title: GtkSourceBuffer
48  * @See_also: #GtkTextBuffer,#GtkSourceView
49  *
50  * The #GtkSourceBuffer object is the model for #GtkSourceView widgets.
51  * It extends the #GtkTextBuffer object by adding features useful to display
52  * and edit source code as syntax highlighting and bracket matching. It
53  * also implements support for undo/redo operations.
54  *
55  * To create a #GtkSourceBuffer use gtk_source_buffer_new() or
56  * gtk_source_buffer_new_with_language(). The second form is just a convenience
57  * function which allows you to initially set a #GtkSourceLanguage.
58  *
59  * By default highlighting is enabled, but you can disable it with
60  * gtk_source_buffer_set_highlight_syntax().
61  */
62 
63 /*
64 #define ENABLE_DEBUG
65 #define ENABLE_PROFILE
66 */
67 #undef ENABLE_DEBUG
68 #undef ENABLE_PROFILE
69 
70 #ifdef ENABLE_DEBUG
71 #define DEBUG(x) (x)
72 #else
73 #define DEBUG(x)
74 #endif
75 
76 #ifdef ENABLE_PROFILE
77 #define PROFILE(x) (x)
78 #else
79 #define PROFILE(x)
80 #endif
81 
82 #define MAX_CHARS_BEFORE_FINDING_A_MATCH    10000
83 
84 #define TAG_CONTEXT_CLASS_NAME "GtkSourceViewTagContextClassName"
85 
86 /* Signals */
87 enum {
88 	HIGHLIGHT_UPDATED,
89 	SOURCE_MARK_UPDATED,
90 	UNDO,
91 	REDO,
92 	LAST_SIGNAL
93 };
94 
95 /* Properties */
96 enum {
97 	PROP_0,
98 	PROP_CAN_UNDO,
99 	PROP_CAN_REDO,
100 	PROP_HIGHLIGHT_SYNTAX,
101 	PROP_HIGHLIGHT_MATCHING_BRACKETS,
102 	PROP_MAX_UNDO_LEVELS,
103 	PROP_LANGUAGE,
104 	PROP_STYLE_SCHEME,
105 	PROP_UNDO_MANAGER
106 };
107 
108 struct _GtkSourceBufferPrivate
109 {
110 	gint                   highlight_syntax:1;
111 	gint                   highlight_brackets:1;
112 
113 	gint                   constructed:1;
114 
115 	GtkTextTag            *bracket_match_tag;
116 	GtkTextMark           *bracket_mark;
117 	guint                  bracket_found:1;
118 
119 	GArray                *source_marks;
120 
121 	GtkSourceLanguage     *language;
122 
123 	GtkSourceEngine       *highlight_engine;
124 	GtkSourceStyleScheme  *style_scheme;
125 
126 	GtkSourceUndoManager  *undo_manager;
127 	gint                   max_undo_levels;
128 
129 	gint                   allow_bracket_match:1;
130 };
131 
132 G_DEFINE_TYPE (GtkSourceBuffer, gtk_source_buffer, GTK_TYPE_TEXT_BUFFER)
133 
134 static guint 	 buffer_signals[LAST_SIGNAL];
135 
136 static GObject	*gtk_source_buffer_constructor		(GType                    type,
137 							 guint                    n_construct_properties,
138 							 GObjectConstructParam   *construct_param);
139 static void 	 gtk_source_buffer_finalize		(GObject                 *object);
140 static void 	 gtk_source_buffer_dispose		(GObject                 *object);
141 static void      gtk_source_buffer_set_property         (GObject                 *object,
142 							 guint                    prop_id,
143 							 const GValue            *value,
144 							 GParamSpec              *pspec);
145 static void      gtk_source_buffer_get_property         (GObject                 *object,
146 							 guint                    prop_id,
147 							 GValue                  *value,
148 							 GParamSpec              *pspec);
149 static void 	 gtk_source_buffer_can_undo_handler 	(GtkSourceUndoManager    *manager,
150 							 GtkSourceBuffer         *buffer);
151 static void 	 gtk_source_buffer_can_redo_handler	(GtkSourceUndoManager    *manager,
152 							 GtkSourceBuffer         *buffer);
153 static void 	 gtk_source_buffer_real_insert_text 	(GtkTextBuffer           *buffer,
154 							 GtkTextIter             *iter,
155 							 const gchar             *text,
156 							 gint                     len);
157 static void	 gtk_source_buffer_real_insert_pixbuf	(GtkTextBuffer           *buffer,
158 							 GtkTextIter             *pos,
159 							 GdkPixbuf               *pixbuf);
160 static void	 gtk_source_buffer_real_insert_anchor	(GtkTextBuffer           *buffer,
161 							 GtkTextIter             *pos,
162 							 GtkTextChildAnchor      *anchor);
163 static void 	 gtk_source_buffer_real_delete_range 	(GtkTextBuffer           *buffer,
164 							 GtkTextIter             *iter,
165 							 GtkTextIter             *end);
166 static void 	 gtk_source_buffer_real_mark_set	(GtkTextBuffer		 *buffer,
167 							 const GtkTextIter	 *location,
168 							 GtkTextMark		 *mark);
169 
170 static void 	 gtk_source_buffer_real_apply_tag	(GtkTextBuffer		 *buffer,
171 							 GtkTextTag		 *tag,
172 							 const GtkTextIter	 *start,
173 							 const GtkTextIter	 *end);
174 
175 static void 	 gtk_source_buffer_real_mark_deleted	(GtkTextBuffer		 *buffer,
176 							 GtkTextMark		 *mark);
177 static gboolean	 gtk_source_buffer_find_bracket_match_with_limit (GtkSourceBuffer *buffer,
178 								  GtkTextIter     *orig,
179 								  gint             max_chars);
180 
181 static void	 gtk_source_buffer_real_undo		(GtkSourceBuffer	 *buffer);
182 static void	 gtk_source_buffer_real_redo		(GtkSourceBuffer	 *buffer);
183 
184 static void
gtk_source_buffer_class_init(GtkSourceBufferClass * klass)185 gtk_source_buffer_class_init (GtkSourceBufferClass *klass)
186 {
187 	GObjectClass        *object_class;
188 	GtkTextBufferClass  *tb_class;
189 	GType                param_types[2];
190 
191 	object_class 	= G_OBJECT_CLASS (klass);
192 	tb_class	= GTK_TEXT_BUFFER_CLASS (klass);
193 
194 	object_class->constructor  = gtk_source_buffer_constructor;
195 	object_class->finalize	   = gtk_source_buffer_finalize;
196 	object_class->dispose	   = gtk_source_buffer_dispose;
197 	object_class->get_property = gtk_source_buffer_get_property;
198 	object_class->set_property = gtk_source_buffer_set_property;
199 
200 	tb_class->delete_range        = gtk_source_buffer_real_delete_range;
201 	tb_class->insert_text 	      = gtk_source_buffer_real_insert_text;
202 	tb_class->insert_pixbuf       = gtk_source_buffer_real_insert_pixbuf;
203 	tb_class->insert_child_anchor = gtk_source_buffer_real_insert_anchor;
204 	tb_class->apply_tag           = gtk_source_buffer_real_apply_tag;
205 
206 	tb_class->mark_set	= gtk_source_buffer_real_mark_set;
207 	tb_class->mark_deleted	= gtk_source_buffer_real_mark_deleted;
208 
209 	klass->undo = gtk_source_buffer_real_undo;
210 	klass->redo = gtk_source_buffer_real_redo;
211 
212 	/**
213 	 * GtkSourceBuffer:highlight-syntax:
214 	 *
215 	 * Whether to highlight syntax in the buffer.
216 	 */
217 	g_object_class_install_property (object_class,
218 					 PROP_HIGHLIGHT_SYNTAX,
219 					 g_param_spec_boolean ("highlight-syntax",
220 							       _("Highlight Syntax"),
221 							       _("Whether to highlight syntax "
222 								 "in the buffer"),
223 							       TRUE,
224 							       G_PARAM_READWRITE));
225 
226 	/**
227 	 * GtkSourceBuffer:highlight-matching-brackets:
228 	 *
229 	 * Whether to highlight matching brackets in the buffer.
230 	 */
231 	g_object_class_install_property (object_class,
232 					 PROP_HIGHLIGHT_MATCHING_BRACKETS,
233 					 g_param_spec_boolean ("highlight-matching-brackets",
234 							       _("Highlight Matching Brackets"),
235 							       _("Whether to highlight matching brackets"),
236 							       TRUE,
237 							       G_PARAM_READWRITE));
238 
239 	/**
240 	 * GtkSourceBuffer:max-undo-levels:
241 	 *
242 	 * Number of undo levels for the buffer. -1 means no limit. This property
243 	 * will only affect the default undo manager.
244 	 */
245 	g_object_class_install_property (object_class,
246 					 PROP_MAX_UNDO_LEVELS,
247 					 g_param_spec_int ("max-undo-levels",
248 							   _("Maximum Undo Levels"),
249 							   _("Number of undo levels for "
250 							     "the buffer"),
251 							   -1,
252 							   G_MAXINT,
253 							   1000,
254 							   G_PARAM_READWRITE));
255 
256 	g_object_class_install_property (object_class,
257 					 PROP_LANGUAGE,
258 					 g_param_spec_object ("language",
259 							      /* Translators: throughout gtksourceview "language" stands
260 							       * for "programming language", not "spoken language" */
261 							      _("Language"),
262 							      _("Language object to get "
263 								"highlighting patterns from"),
264 							      GTK_TYPE_SOURCE_LANGUAGE,
265 							      G_PARAM_READWRITE));
266 
267 	g_object_class_install_property (object_class,
268 					 PROP_CAN_UNDO,
269 					 g_param_spec_boolean ("can-undo",
270 							       _("Can undo"),
271 							       _("Whether Undo operation is possible"),
272 							       FALSE,
273 							       G_PARAM_READABLE));
274 
275 	g_object_class_install_property (object_class,
276 					 PROP_CAN_REDO,
277 					 g_param_spec_boolean ("can-redo",
278 							       _("Can redo"),
279 							       _("Whether Redo operation is possible"),
280 							       FALSE,
281 							       G_PARAM_READABLE));
282 
283 	/**
284 	 * GtkSourceBuffer:style-scheme:
285 	 *
286 	 * Style scheme. It contains styles for syntax highlighting, optionally
287 	 * foreground, background, cursor color, current line color, and matching
288 	 * brackets style.
289 	 */
290 	g_object_class_install_property (object_class,
291 					 PROP_STYLE_SCHEME,
292 					 g_param_spec_object ("style_scheme",
293 							      _("Style scheme"),
294 							      _("Style scheme"),
295 							      GTK_TYPE_SOURCE_STYLE_SCHEME,
296 							      G_PARAM_READWRITE));
297 
298 	g_object_class_install_property (object_class,
299 	                                 PROP_UNDO_MANAGER,
300 	                                 g_param_spec_object ("undo-manager",
301 	                                                      _("Undo manager"),
302 	                                                      _("The buffer undo manager"),
303 	                                                      GTK_TYPE_SOURCE_UNDO_MANAGER,
304 	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
305 
306 	param_types[0] = GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE;
307 	param_types[1] = GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE;
308 
309 	buffer_signals[HIGHLIGHT_UPDATED] =
310 	    g_signal_newv ("highlight_updated",
311 			   G_OBJECT_CLASS_TYPE (object_class),
312 			   G_SIGNAL_RUN_LAST,
313 			   NULL,
314 			   NULL, NULL,
315 			   _gtksourceview_marshal_VOID__BOXED_BOXED,
316 			   G_TYPE_NONE,
317 			   2, param_types);
318 	/**
319 	 * GtkSourceBuffer::source-mark-updated
320 	 * @buffer: the buffer that received the signal
321 	 *
322 	 * The ::source_mark_updated signal is emitted each time
323 	 * a mark is added to, moved or removed from the @buffer.
324 	 **/
325 	buffer_signals[SOURCE_MARK_UPDATED] =
326 	    g_signal_new ("source_mark_updated",
327 			   G_OBJECT_CLASS_TYPE (object_class),
328 			   G_SIGNAL_RUN_LAST,
329 			   0,
330 			   NULL, NULL,
331 			   g_cclosure_marshal_VOID__OBJECT,
332 			   G_TYPE_NONE,
333 			   1, GTK_TYPE_TEXT_MARK);
334 
335 	buffer_signals[UNDO] =
336 	    g_signal_new ("undo",
337 			  G_OBJECT_CLASS_TYPE (object_class),
338 			  G_SIGNAL_RUN_LAST,
339 			  G_STRUCT_OFFSET (GtkSourceBufferClass, undo),
340 			  NULL, NULL,
341 			  g_cclosure_marshal_VOID__VOID,
342 			  G_TYPE_NONE,
343 			  0);
344 
345 	buffer_signals[REDO] =
346 	    g_signal_new ("redo",
347 			  G_OBJECT_CLASS_TYPE (object_class),
348 			  G_SIGNAL_RUN_LAST,
349 			  G_STRUCT_OFFSET (GtkSourceBufferClass, redo),
350 			  NULL, NULL,
351 			  g_cclosure_marshal_VOID__VOID,
352 			  G_TYPE_NONE,
353 			  0);
354 
355 	g_type_class_add_private (object_class, sizeof(GtkSourceBufferPrivate));
356 }
357 
358 static void
set_undo_manager(GtkSourceBuffer * buffer,GtkSourceUndoManager * manager)359 set_undo_manager (GtkSourceBuffer      *buffer,
360                   GtkSourceUndoManager *manager)
361 {
362 	if (manager == buffer->priv->undo_manager)
363 	{
364 		return;
365 	}
366 
367 	if (buffer->priv->undo_manager != NULL)
368 	{
369 		g_signal_handlers_disconnect_by_func (buffer->priv->undo_manager,
370 		                                      G_CALLBACK (gtk_source_buffer_can_undo_handler),
371 		                                      buffer);
372 
373 		g_signal_handlers_disconnect_by_func (buffer->priv->undo_manager,
374 		                                      G_CALLBACK (gtk_source_buffer_can_redo_handler),
375 		                                      buffer);
376 
377 		g_object_unref (buffer->priv->undo_manager);
378 		buffer->priv->undo_manager = NULL;
379 	}
380 
381 	if (manager != NULL)
382 	{
383 		buffer->priv->undo_manager = g_object_ref (manager);
384 
385 		g_signal_connect (buffer->priv->undo_manager,
386 		                  "can-undo-changed",
387 		                  G_CALLBACK (gtk_source_buffer_can_undo_handler),
388 		                  buffer);
389 
390 		g_signal_connect (buffer->priv->undo_manager,
391 		                  "can-redo-changed",
392 		                  G_CALLBACK (gtk_source_buffer_can_redo_handler),
393 		                  buffer);
394 
395 		/* Notify possible changes in the can-undo/redo state */
396 		g_object_notify (G_OBJECT (buffer), "can-undo");
397 		g_object_notify (G_OBJECT (buffer), "can-redo");
398 	}
399 }
400 
401 static void
gtk_source_buffer_init(GtkSourceBuffer * buffer)402 gtk_source_buffer_init (GtkSourceBuffer *buffer)
403 {
404 	GtkSourceBufferPrivate *priv;
405 
406 	priv = G_TYPE_INSTANCE_GET_PRIVATE (buffer, GTK_TYPE_SOURCE_BUFFER,
407 					    GtkSourceBufferPrivate);
408 
409 	buffer->priv = priv;
410 
411 	priv->highlight_syntax = TRUE;
412 	priv->highlight_brackets = TRUE;
413 	priv->bracket_mark = NULL;
414 	priv->bracket_found = FALSE;
415 
416 	priv->source_marks = g_array_new (FALSE, FALSE, sizeof (GtkSourceMark *));
417 	priv->style_scheme = _gtk_source_style_scheme_get_default ();
418 
419 	if (priv->style_scheme != NULL)
420 		g_object_ref (priv->style_scheme);
421 }
422 
423 static GObject *
gtk_source_buffer_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_param)424 gtk_source_buffer_constructor (GType                  type,
425 			       guint                  n_construct_properties,
426 			       GObjectConstructParam *construct_param)
427 {
428 	GObject *object;
429 	GtkSourceBuffer *buffer;
430 
431 	object = G_OBJECT_CLASS(gtk_source_buffer_parent_class)->constructor (type,
432 									      n_construct_properties,
433 									      construct_param);
434 
435 	/* we need to know that the tag-table was set */
436 	buffer = GTK_SOURCE_BUFFER (object);
437 	buffer->priv->constructed = TRUE;
438 
439 	if (buffer->priv->undo_manager == NULL)
440 	{
441 		/* This will install the default undo manager */
442 		gtk_source_buffer_set_undo_manager (buffer, NULL);
443 	}
444 
445 	return object;
446 }
447 
448 static void
gtk_source_buffer_finalize(GObject * object)449 gtk_source_buffer_finalize (GObject *object)
450 {
451 	GtkSourceBuffer *buffer;
452 
453 	g_return_if_fail (object != NULL);
454 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (object));
455 
456 	buffer = GTK_SOURCE_BUFFER (object);
457 	g_return_if_fail (buffer->priv != NULL);
458 
459 	if (buffer->priv->source_marks)
460 		g_array_free (buffer->priv->source_marks, TRUE);
461 
462 	G_OBJECT_CLASS (gtk_source_buffer_parent_class)->finalize (object);
463 }
464 
465 static void
gtk_source_buffer_dispose(GObject * object)466 gtk_source_buffer_dispose (GObject *object)
467 {
468 	GtkSourceBuffer *buffer;
469 
470 	g_return_if_fail (object != NULL);
471 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (object));
472 
473 	buffer = GTK_SOURCE_BUFFER (object);
474 	g_return_if_fail (buffer->priv != NULL);
475 
476 	if (buffer->priv->undo_manager != NULL)
477 	{
478 		set_undo_manager (buffer, NULL);
479 	}
480 
481 	if (buffer->priv->highlight_engine != NULL)
482 	{
483 		_gtk_source_engine_attach_buffer (buffer->priv->highlight_engine, NULL);
484 		g_object_unref (buffer->priv->highlight_engine);
485 		buffer->priv->highlight_engine = NULL;
486 	}
487 
488 	if (buffer->priv->language != NULL)
489 	{
490 		g_object_unref (buffer->priv->language);
491 		buffer->priv->language = NULL;
492 	}
493 
494 	if (buffer->priv->style_scheme != NULL)
495 	{
496 		g_object_unref (buffer->priv->style_scheme);
497 		buffer->priv->style_scheme = NULL;
498 	}
499 
500 	G_OBJECT_CLASS (gtk_source_buffer_parent_class)->dispose (object);
501 }
502 
503 static void
gtk_source_buffer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)504 gtk_source_buffer_set_property (GObject      *object,
505 				guint         prop_id,
506 				const GValue *value,
507 				GParamSpec   *pspec)
508 {
509 	GtkSourceBuffer *source_buffer;
510 
511 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (object));
512 
513 	source_buffer = GTK_SOURCE_BUFFER (object);
514 
515 	switch (prop_id)
516 	{
517 		case PROP_HIGHLIGHT_SYNTAX:
518 			gtk_source_buffer_set_highlight_syntax (source_buffer,
519 							      g_value_get_boolean (value));
520 			break;
521 
522 		case PROP_HIGHLIGHT_MATCHING_BRACKETS:
523 			gtk_source_buffer_set_highlight_matching_brackets (source_buffer,
524 								g_value_get_boolean (value));
525 			break;
526 
527 		case PROP_MAX_UNDO_LEVELS:
528 			gtk_source_buffer_set_max_undo_levels (source_buffer,
529 							       g_value_get_int (value));
530 			break;
531 
532 		case PROP_LANGUAGE:
533 			gtk_source_buffer_set_language (source_buffer,
534 							g_value_get_object (value));
535 			break;
536 
537 		case PROP_STYLE_SCHEME:
538 			gtk_source_buffer_set_style_scheme (source_buffer,
539 							    g_value_get_object (value));
540 			break;
541 
542 		case PROP_UNDO_MANAGER:
543 			gtk_source_buffer_set_undo_manager (source_buffer,
544 			                                    g_value_get_object (value));
545 			break;
546 
547 		default:
548 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
549 			break;
550 	}
551 }
552 
553 static void
gtk_source_buffer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)554 gtk_source_buffer_get_property (GObject    *object,
555 				guint       prop_id,
556 				GValue     *value,
557 				GParamSpec *pspec)
558 {
559 	GtkSourceBuffer *source_buffer;
560 
561 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (object));
562 
563 	source_buffer = GTK_SOURCE_BUFFER (object);
564 
565 	switch (prop_id)
566 	{
567 		case PROP_HIGHLIGHT_SYNTAX:
568 			g_value_set_boolean (value,
569 					     source_buffer->priv->highlight_syntax);
570 			break;
571 
572 		case PROP_HIGHLIGHT_MATCHING_BRACKETS:
573 			g_value_set_boolean (value,
574 					     source_buffer->priv->highlight_brackets);
575 			break;
576 
577 		case PROP_MAX_UNDO_LEVELS:
578 			g_value_set_int (value,
579 					 source_buffer->priv->max_undo_levels);
580 			break;
581 
582 		case PROP_LANGUAGE:
583 			g_value_set_object (value, source_buffer->priv->language);
584 			break;
585 
586 		case PROP_STYLE_SCHEME:
587 			g_value_set_object (value, source_buffer->priv->style_scheme);
588 			break;
589 
590 		case PROP_CAN_UNDO:
591 			g_value_set_boolean (value, gtk_source_buffer_can_undo (source_buffer));
592 			break;
593 
594 		case PROP_CAN_REDO:
595 			g_value_set_boolean (value, gtk_source_buffer_can_redo (source_buffer));
596 			break;
597 
598 		case PROP_UNDO_MANAGER:
599 			g_value_set_object (value, source_buffer->priv->undo_manager);
600 			break;
601 
602 		default:
603 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
604 			break;
605 	}
606 }
607 
608 /**
609  * gtk_source_buffer_new:
610  * @table: a #GtkTextTagTable, or %NULL to create a new one.
611  *
612  * Creates a new source buffer.
613  *
614  * Return value: a new source buffer.
615  **/
616 GtkSourceBuffer *
gtk_source_buffer_new(GtkTextTagTable * table)617 gtk_source_buffer_new (GtkTextTagTable *table)
618 {
619 	return g_object_new (GTK_TYPE_SOURCE_BUFFER,
620 			     "tag-table", table,
621 			     NULL);
622 }
623 
624 /**
625  * gtk_source_buffer_new_with_language:
626  * @language: a #GtkSourceLanguage.
627  *
628  * Creates a new source buffer using the highlighting patterns in
629  * @language.  This is equivalent to creating a new source buffer with
630  * a new tag table and then calling gtk_source_buffer_set_language().
631  *
632  * Return value: a new source buffer which will highlight text
633  * according to the highlighting patterns in @language.
634  **/
635 GtkSourceBuffer *
gtk_source_buffer_new_with_language(GtkSourceLanguage * language)636 gtk_source_buffer_new_with_language (GtkSourceLanguage *language)
637 {
638 	GtkSourceBuffer *buffer;
639 
640 	g_return_val_if_fail (GTK_IS_SOURCE_LANGUAGE (language), NULL);
641 
642 	buffer = gtk_source_buffer_new (NULL);
643 
644 	gtk_source_buffer_set_language (buffer, language);
645 
646 	return buffer;
647 }
648 
649 static void
gtk_source_buffer_can_undo_handler(GtkSourceUndoManager * manager,GtkSourceBuffer * buffer)650 gtk_source_buffer_can_undo_handler (GtkSourceUndoManager *manager,
651                                     GtkSourceBuffer      *buffer)
652 {
653 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
654 
655 	g_object_notify (G_OBJECT (buffer), "can-undo");
656 }
657 
658 static void
gtk_source_buffer_can_redo_handler(GtkSourceUndoManager * manager,GtkSourceBuffer * buffer)659 gtk_source_buffer_can_redo_handler (GtkSourceUndoManager *manager,
660                                     GtkSourceBuffer      *buffer)
661 {
662 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
663 
664 	g_object_notify (G_OBJECT (buffer), "can-redo");
665 }
666 
667 static void
update_bracket_match_style(GtkSourceBuffer * buffer)668 update_bracket_match_style (GtkSourceBuffer *buffer)
669 {
670 	if (buffer->priv->bracket_match_tag != NULL)
671 	{
672 		GtkSourceStyle *style = NULL;
673 
674 		if (buffer->priv->style_scheme)
675 			style = _gtk_source_style_scheme_get_matching_brackets_style (buffer->priv->style_scheme);
676 
677 		_gtk_source_style_apply (style, buffer->priv->bracket_match_tag);
678 	}
679 }
680 
681 static GtkTextTag *
get_bracket_match_tag(GtkSourceBuffer * buffer)682 get_bracket_match_tag (GtkSourceBuffer *buffer)
683 {
684 	if (buffer->priv->bracket_match_tag == NULL)
685 	{
686 		buffer->priv->bracket_match_tag =
687 			gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
688 						    NULL,
689 						    NULL);
690 		update_bracket_match_style (buffer);
691 	}
692 
693 	return buffer->priv->bracket_match_tag;
694 }
695 
696 /*
697  * This is private, just used by the compositor to not print bracket
698  * matches. Note that unlike get_bracket_match_tag() it returns NULL
699  * if the tag is not set.
700  */
701 GtkTextTag *
_gtk_source_buffer_get_bracket_match_tag(GtkSourceBuffer * buffer)702 _gtk_source_buffer_get_bracket_match_tag (GtkSourceBuffer *buffer)
703 {
704 	return buffer->priv->bracket_match_tag;
705 }
706 
707 static void
gtk_source_buffer_move_cursor(GtkTextBuffer * buffer,const GtkTextIter * iter,GtkTextMark * mark)708 gtk_source_buffer_move_cursor (GtkTextBuffer     *buffer,
709 			       const GtkTextIter *iter,
710 			       GtkTextMark       *mark)
711 {
712 	GtkTextIter iter1, iter2;
713 
714 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
715 	g_return_if_fail (iter != NULL);
716 	g_return_if_fail (mark != NULL);
717 	g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
718 
719 	if (mark != gtk_text_buffer_get_insert (buffer))
720 		return;
721 
722 	if (GTK_SOURCE_BUFFER (buffer)->priv->bracket_found)
723 	{
724 		gtk_text_buffer_get_iter_at_mark (buffer,
725 						  &iter1,
726 						  GTK_SOURCE_BUFFER (buffer)->priv->bracket_mark);
727 		iter2 = iter1;
728 		gtk_text_iter_forward_char (&iter2);
729 		gtk_text_buffer_remove_tag (buffer,
730 					    GTK_SOURCE_BUFFER (buffer)->priv->bracket_match_tag,
731 					    &iter1,
732 					    &iter2);
733 	}
734 
735 	if (!GTK_SOURCE_BUFFER (buffer)->priv->highlight_brackets)
736 		return;
737 
738 	iter1 = *iter;
739 	if (gtk_source_buffer_find_bracket_match_with_limit (GTK_SOURCE_BUFFER (buffer),
740 	                                                     &iter1,
741 	                                                     MAX_CHARS_BEFORE_FINDING_A_MATCH))
742 	{
743 		if (!GTK_SOURCE_BUFFER (buffer)->priv->bracket_mark)
744 			GTK_SOURCE_BUFFER (buffer)->priv->bracket_mark =
745 				gtk_text_buffer_create_mark (buffer,
746 							     NULL,
747 							     &iter1,
748 							     FALSE);
749 		else
750 			gtk_text_buffer_move_mark (buffer,
751 						   GTK_SOURCE_BUFFER (buffer)->priv->bracket_mark,
752 						   &iter1);
753 
754 		iter2 = iter1;
755 		gtk_text_iter_forward_char (&iter2);
756 
757 		/* allow_bracket_match will allow the bracket match tag to be
758 		   applied to the buffer. See apply_tag_real for more
759 		   information */
760 		GTK_SOURCE_BUFFER (buffer)->priv->allow_bracket_match = TRUE;
761 		gtk_text_buffer_apply_tag (buffer,
762 					   get_bracket_match_tag (GTK_SOURCE_BUFFER (buffer)),
763 					   &iter1,
764 					   &iter2);
765 		GTK_SOURCE_BUFFER (buffer)->priv->allow_bracket_match = FALSE;
766 
767 		GTK_SOURCE_BUFFER (buffer)->priv->bracket_found = TRUE;
768 	}
769 	else
770 	{
771 		GTK_SOURCE_BUFFER (buffer)->priv->bracket_found = FALSE;
772 	}
773 }
774 
775 static void
gtk_source_buffer_content_inserted(GtkTextBuffer * buffer,gint start_offset,gint end_offset)776 gtk_source_buffer_content_inserted (GtkTextBuffer *buffer,
777 				    gint           start_offset,
778 				    gint           end_offset)
779 {
780 	GtkTextMark *mark;
781 	GtkTextIter insert_iter;
782 	GtkSourceBuffer *source_buffer = GTK_SOURCE_BUFFER (buffer);
783 
784 	mark = gtk_text_buffer_get_insert (buffer);
785 	gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, mark);
786 	gtk_source_buffer_move_cursor (buffer, &insert_iter, mark);
787 
788 	if (source_buffer->priv->highlight_engine != NULL)
789 		_gtk_source_engine_text_inserted (source_buffer->priv->highlight_engine,
790 						  start_offset,
791 						  end_offset);
792 }
793 
794 static void
gtk_source_buffer_real_insert_text(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len)795 gtk_source_buffer_real_insert_text (GtkTextBuffer *buffer,
796 				    GtkTextIter   *iter,
797 				    const gchar   *text,
798 				    gint           len)
799 {
800 	gint start_offset;
801 
802 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
803 	g_return_if_fail (iter != NULL);
804 	g_return_if_fail (text != NULL);
805 	g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
806 
807 	start_offset = gtk_text_iter_get_offset (iter);
808 
809 	/*
810 	 * iter is invalidated when
811 	 * insertion occurs (because the buffer contents change), but the
812 	 * default signal handler revalidates it to point to the end of the
813 	 * inserted text
814 	 */
815 	GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->insert_text (buffer, iter, text, len);
816 
817 	gtk_source_buffer_content_inserted (buffer,
818 					    start_offset,
819 					    gtk_text_iter_get_offset (iter));
820 }
821 
822 /* insert_pixbuf and insert_child_anchor do nothing except notifying
823  * the highlighting engine about the change, because engine's idea
824  * of buffer char count must be correct at all times */
825 static void
gtk_source_buffer_real_insert_pixbuf(GtkTextBuffer * buffer,GtkTextIter * iter,GdkPixbuf * pixbuf)826 gtk_source_buffer_real_insert_pixbuf (GtkTextBuffer *buffer,
827 				      GtkTextIter   *iter,
828 				      GdkPixbuf     *pixbuf)
829 {
830 	gint start_offset;
831 
832 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
833 	g_return_if_fail (iter != NULL);
834 	g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
835 
836 	start_offset = gtk_text_iter_get_offset (iter);
837 
838 	/*
839 	 * iter is invalidated when
840 	 * insertion occurs (because the buffer contents change), but the
841 	 * default signal handler revalidates it to point to the end of the
842 	 * inserted text
843 	 */
844 	GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->insert_pixbuf (buffer, iter, pixbuf);
845 
846 	gtk_source_buffer_content_inserted (buffer,
847 					    start_offset,
848 					    gtk_text_iter_get_offset (iter));
849 }
850 
851 static void
gtk_source_buffer_real_insert_anchor(GtkTextBuffer * buffer,GtkTextIter * iter,GtkTextChildAnchor * anchor)852 gtk_source_buffer_real_insert_anchor (GtkTextBuffer      *buffer,
853 				      GtkTextIter        *iter,
854 				      GtkTextChildAnchor *anchor)
855 {
856 	gint start_offset;
857 
858 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
859 	g_return_if_fail (iter != NULL);
860 	g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
861 
862 	start_offset = gtk_text_iter_get_offset (iter);
863 
864 	/*
865 	 * iter is invalidated when
866 	 * insertion occurs (because the buffer contents change), but the
867 	 * default signal handler revalidates it to point to the end of the
868 	 * inserted text
869 	 */
870 	GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->insert_child_anchor (buffer, iter, anchor);
871 
872 	gtk_source_buffer_content_inserted (buffer,
873 					    start_offset,
874 					    gtk_text_iter_get_offset (iter));
875 }
876 
877 static void
gtk_source_buffer_real_delete_range(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end)878 gtk_source_buffer_real_delete_range (GtkTextBuffer *buffer,
879 				     GtkTextIter   *start,
880 				     GtkTextIter   *end)
881 {
882 	gint offset, length;
883 	GtkTextMark *mark;
884 	GtkTextIter iter;
885 	GtkSourceBuffer *source_buffer = GTK_SOURCE_BUFFER (buffer);
886 
887 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
888 	g_return_if_fail (start != NULL);
889 	g_return_if_fail (end != NULL);
890 	g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
891 	g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
892 
893 	gtk_text_iter_order (start, end);
894 	offset = gtk_text_iter_get_offset (start);
895 	length = gtk_text_iter_get_offset (end) - offset;
896 
897 	GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->delete_range (buffer, start, end);
898 
899 	mark = gtk_text_buffer_get_insert (buffer);
900 	gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
901 	gtk_source_buffer_move_cursor (buffer, &iter, mark);
902 
903 	/* emit text deleted for engines */
904 	if (source_buffer->priv->highlight_engine != NULL)
905 		_gtk_source_engine_text_deleted (source_buffer->priv->highlight_engine,
906 						 offset, length);
907 }
908 
909 /* This describes a mask of relevant context classes for highlighting matching
910    brackets. Additional classes can be added below */
911 static const gchar *cclass_mask_definitions[] = {
912 	"comment",
913 	"string",
914 };
915 
916 static gint
get_context_class_mask(GtkSourceBuffer * buffer,GtkTextIter * iter)917 get_context_class_mask (GtkSourceBuffer *buffer,
918                         GtkTextIter     *iter)
919 {
920 	gint i;
921 	gint ret = 0;
922 
923 	for (i = 0; i < sizeof (cclass_mask_definitions) / sizeof (gchar *); ++i)
924 	{
925 		gboolean hasclass = gtk_source_buffer_iter_has_context_class (buffer,
926 		                                                              iter,
927 		                                                              cclass_mask_definitions[i]);
928 
929 		ret |= hasclass << i;
930 	}
931 
932 	return ret;
933 }
934 
935 static gboolean
gtk_source_buffer_find_bracket_match_real(GtkSourceBuffer * buffer,GtkTextIter * orig,gint max_chars)936 gtk_source_buffer_find_bracket_match_real (GtkSourceBuffer *buffer,
937                                            GtkTextIter     *orig,
938                                            gint             max_chars)
939 {
940 	GtkTextIter iter;
941 
942 	gunichar base_char;
943 	gunichar search_char;
944 	gunichar cur_char;
945 	gint addition;
946 	gint char_cont;
947 	gint counter;
948 
949 	gboolean found;
950 
951 	gint cclass_mask;
952 
953 	iter = *orig;
954 
955 	cur_char = gtk_text_iter_get_char (&iter);
956 
957 	base_char = search_char = cur_char;
958 	cclass_mask = get_context_class_mask (buffer, &iter);
959 
960 	switch ((int) base_char) {
961 		case '{':
962 			addition = 1;
963 			search_char = '}';
964 			break;
965 		case '(':
966 			addition = 1;
967 			search_char = ')';
968 			break;
969 		case '[':
970 			addition = 1;
971 			search_char = ']';
972 			break;
973 		case '<':
974 			addition = 1;
975 			search_char = '>';
976 			break;
977 		case '}':
978 			addition = -1;
979 			search_char = '{';
980 			break;
981 		case ')':
982 			addition = -1;
983 			search_char = '(';
984 			break;
985 		case ']':
986 			addition = -1;
987 			search_char = '[';
988 			break;
989 		case '>':
990 			addition = -1;
991 			search_char = '<';
992 			break;
993 		default:
994 			addition = 0;
995 			break;
996 	}
997 
998 	if (addition == 0)
999 		return FALSE;
1000 
1001 	counter = 0;
1002 	found = FALSE;
1003 	char_cont = 0;
1004 
1005 	do {
1006 		gint current_mask;
1007 
1008 		gtk_text_iter_forward_chars (&iter, addition);
1009 		cur_char = gtk_text_iter_get_char (&iter);
1010 		++char_cont;
1011 
1012 		current_mask = get_context_class_mask (buffer, &iter);
1013 
1014 		/* Check if we lost a class, which means we don't look any
1015 		   further */
1016 		if (current_mask < cclass_mask)
1017 		{
1018 			found = FALSE;
1019 			break;
1020 		}
1021 
1022 		if ((cur_char == search_char || cur_char == base_char) &&
1023 		    cclass_mask == current_mask)
1024 		{
1025 			if ((cur_char == search_char) && counter == 0) {
1026 				found = TRUE;
1027 				break;
1028 			}
1029 			if (cur_char == base_char)
1030 				counter++;
1031 			else
1032 				counter--;
1033 		}
1034 	}
1035 	while (!gtk_text_iter_is_end (&iter) && !gtk_text_iter_is_start (&iter) &&
1036 		((char_cont < max_chars) || (max_chars < 0)));
1037 
1038 	if (found)
1039 		*orig = iter;
1040 
1041 	return found;
1042 }
1043 
1044 /* Note that we take into account both the character following the cursor and the
1045  * one preceding it. If there are brackets on both sides the one following the
1046  * cursor takes precedence.
1047  */
1048 static gboolean
gtk_source_buffer_find_bracket_match_with_limit(GtkSourceBuffer * buffer,GtkTextIter * orig,gint max_chars)1049 gtk_source_buffer_find_bracket_match_with_limit (GtkSourceBuffer *buffer,
1050                                                  GtkTextIter     *orig,
1051                                                  gint             max_chars)
1052 {
1053 	GtkTextIter iter;
1054 
1055 	if (gtk_source_buffer_find_bracket_match_real (buffer, orig, max_chars))
1056 	{
1057 		return TRUE;
1058 	}
1059 
1060 	iter = *orig;
1061 	if (!gtk_text_iter_starts_line (&iter) &&
1062 	    gtk_text_iter_backward_char (&iter))
1063 	{
1064 		if (gtk_source_buffer_find_bracket_match_real (buffer, &iter, max_chars))
1065 		{
1066 			*orig = iter;
1067 			return TRUE;
1068 		}
1069 	}
1070 
1071 	return FALSE;
1072 }
1073 
1074 #if 0
1075 /**
1076  * gtk_source_iter_find_matching_bracket:
1077  * @iter: a #GtkTextIter.
1078  *
1079  * Tries to match the bracket character currently at @iter with its
1080  * opening/closing counterpart, and if found moves @iter to the position
1081  * where it was found.
1082  *
1083  * @iter must be a #GtkTextIter belonging to a #GtkSourceBuffer.
1084  *
1085  * Return value: %TRUE if the matching bracket was found and the @iter
1086  * iter moved.
1087  **/
1088 gboolean
1089 gtk_source_iter_find_matching_bracket (GtkTextIter *iter)
1090 {
1091 	g_return_val_if_fail (iter != NULL, FALSE);
1092 
1093 	return gtk_source_buffer_find_bracket_match_with_limit (iter, -1);
1094 }
1095 #endif
1096 
1097 /**
1098  * gtk_source_buffer_can_undo:
1099  * @buffer: a #GtkSourceBuffer.
1100  *
1101  * Determines whether a source buffer can undo the last action.
1102  *
1103  * Return value: %TRUE if it's possible to undo the last action.
1104  **/
1105 gboolean
gtk_source_buffer_can_undo(GtkSourceBuffer * buffer)1106 gtk_source_buffer_can_undo (GtkSourceBuffer *buffer)
1107 {
1108 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
1109 
1110 	return gtk_source_undo_manager_can_undo (buffer->priv->undo_manager);
1111 }
1112 
1113 /**
1114  * gtk_source_buffer_can_redo:
1115  * @buffer: a #GtkSourceBuffer.
1116  *
1117  * Determines whether a source buffer can redo the last action
1118  * (i.e. if the last operation was an undo).
1119  *
1120  * Return value: %TRUE if a redo is possible.
1121  **/
1122 gboolean
gtk_source_buffer_can_redo(GtkSourceBuffer * buffer)1123 gtk_source_buffer_can_redo (GtkSourceBuffer *buffer)
1124 {
1125 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
1126 
1127 	return gtk_source_undo_manager_can_redo (buffer->priv->undo_manager);
1128 }
1129 
1130 /**
1131  * gtk_source_buffer_undo:
1132  * @buffer: a #GtkSourceBuffer.
1133  *
1134  * Undoes the last user action which modified the buffer.  Use
1135  * gtk_source_buffer_can_undo() to check whether a call to this
1136  * function will have any effect.
1137  *
1138  * Actions are defined as groups of operations between a call to
1139  * gtk_text_buffer_begin_user_action() and
1140  * gtk_text_buffer_end_user_action(), or sequences of similar edits
1141  * (inserts or deletes) on the same line.
1142  **/
1143 void
gtk_source_buffer_undo(GtkSourceBuffer * buffer)1144 gtk_source_buffer_undo (GtkSourceBuffer *buffer)
1145 {
1146 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1147 
1148 	g_signal_emit (buffer, buffer_signals[UNDO], 0);
1149 }
1150 
1151 /**
1152  * gtk_source_buffer_redo:
1153  * @buffer: a #GtkSourceBuffer.
1154  *
1155  * Redoes the last undo operation.  Use gtk_source_buffer_can_redo()
1156  * to check whether a call to this function will have any effect.
1157  **/
1158 void
gtk_source_buffer_redo(GtkSourceBuffer * buffer)1159 gtk_source_buffer_redo (GtkSourceBuffer *buffer)
1160 {
1161 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1162 
1163 	g_signal_emit (buffer, buffer_signals[REDO], 0);
1164 }
1165 
1166 /**
1167  * gtk_source_buffer_get_max_undo_levels:
1168  * @buffer: a #GtkSourceBuffer.
1169  *
1170  * Determines the number of undo levels the buffer will track for
1171  * buffer edits.
1172  *
1173  * Return value: the maximum number of possible undo levels or
1174  *               -1 if no limit is set.
1175  **/
1176 gint
gtk_source_buffer_get_max_undo_levels(GtkSourceBuffer * buffer)1177 gtk_source_buffer_get_max_undo_levels (GtkSourceBuffer *buffer)
1178 {
1179 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), 0);
1180 
1181 	return buffer->priv->max_undo_levels;
1182 }
1183 
1184 /**
1185  * gtk_source_buffer_set_max_undo_levels:
1186  * @buffer: a #GtkSourceBuffer.
1187  * @max_undo_levels: the desired maximum number of undo levels.
1188  *
1189  * Sets the number of undo levels for user actions the buffer will
1190  * track.  If the number of user actions exceeds the limit set by this
1191  * function, older actions will be discarded.
1192  *
1193  * If @max_undo_levels is -1, no limit is set.
1194  *
1195  * A new action is started whenever the function
1196  * gtk_text_buffer_begin_user_action() is called.  In general, this
1197  * happens whenever the user presses any key which modifies the
1198  * buffer, but the undo manager will try to merge similar consecutive
1199  * actions, such as multiple character insertions into one action.
1200  * But, inserting a newline does start a new action.
1201  **/
1202 void
gtk_source_buffer_set_max_undo_levels(GtkSourceBuffer * buffer,gint max_undo_levels)1203 gtk_source_buffer_set_max_undo_levels (GtkSourceBuffer *buffer,
1204 				       gint             max_undo_levels)
1205 {
1206 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1207 
1208 	if (buffer->priv->max_undo_levels == max_undo_levels)
1209 	{
1210 		return;
1211 	}
1212 
1213 	buffer->priv->max_undo_levels = max_undo_levels;
1214 
1215 	if (GTK_IS_SOURCE_UNDO_MANAGER_DEFAULT (buffer->priv->undo_manager))
1216 	{
1217 		gtk_source_undo_manager_default_set_max_undo_levels (GTK_SOURCE_UNDO_MANAGER_DEFAULT (buffer->priv->undo_manager),
1218 		                                                     max_undo_levels);
1219 	}
1220 
1221 	g_object_notify (G_OBJECT (buffer), "max-undo-levels");
1222 }
1223 
1224 /**
1225  * gtk_source_buffer_begin_not_undoable_action:
1226  * @buffer: a #GtkSourceBuffer.
1227  *
1228  * Marks the beginning of a not undoable action on the buffer,
1229  * disabling the undo manager.  Typically you would call this function
1230  * before initially setting the contents of the buffer (e.g. when
1231  * loading a file in a text editor).
1232  *
1233  * You may nest gtk_source_buffer_begin_not_undoable_action() /
1234  * gtk_source_buffer_end_not_undoable_action() blocks.
1235  **/
1236 void
gtk_source_buffer_begin_not_undoable_action(GtkSourceBuffer * buffer)1237 gtk_source_buffer_begin_not_undoable_action (GtkSourceBuffer *buffer)
1238 {
1239 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1240 
1241 	gtk_source_undo_manager_begin_not_undoable_action (buffer->priv->undo_manager);
1242 }
1243 
1244 /**
1245  * gtk_source_buffer_end_not_undoable_action:
1246  * @buffer: a #GtkSourceBuffer.
1247  *
1248  * Marks the end of a not undoable action on the buffer.  When the
1249  * last not undoable block is closed through the call to this
1250  * function, the list of undo actions is cleared and the undo manager
1251  * is re-enabled.
1252  **/
1253 void
gtk_source_buffer_end_not_undoable_action(GtkSourceBuffer * buffer)1254 gtk_source_buffer_end_not_undoable_action (GtkSourceBuffer *buffer)
1255 {
1256 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1257 
1258 	gtk_source_undo_manager_end_not_undoable_action (buffer->priv->undo_manager);
1259 }
1260 
1261 /**
1262  * gtk_source_buffer_get_highlight_matching_brackets:
1263  * @buffer: a #GtkSourceBuffer.
1264  *
1265  * Determines whether bracket match highlighting is activated for the
1266  * source buffer.
1267  *
1268  * Return value: %TRUE if the source buffer will highlight matching
1269  * brackets.
1270  **/
1271 gboolean
gtk_source_buffer_get_highlight_matching_brackets(GtkSourceBuffer * buffer)1272 gtk_source_buffer_get_highlight_matching_brackets (GtkSourceBuffer *buffer)
1273 {
1274 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
1275 
1276 	return (buffer->priv->highlight_brackets != FALSE);
1277 }
1278 
1279 /**
1280  * gtk_source_buffer_set_highlight_matching_brackets:
1281  * @buffer: a #GtkSourceBuffer.
1282  * @highlight: %TRUE if you want matching brackets highlighted.
1283  *
1284  * Controls the bracket match highlighting function in the buffer.  If
1285  * activated, when you position your cursor over a bracket character
1286  * (a parenthesis, a square bracket, etc.) the matching opening or
1287  * closing bracket character will be highlighted.  You can specify the
1288  * style with the gtk_source_buffer_set_bracket_match_style()
1289  * function.
1290  **/
1291 void
gtk_source_buffer_set_highlight_matching_brackets(GtkSourceBuffer * buffer,gboolean highlight)1292 gtk_source_buffer_set_highlight_matching_brackets (GtkSourceBuffer *buffer,
1293 						   gboolean         highlight)
1294 {
1295 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1296 
1297 	highlight = (highlight != FALSE);
1298 
1299 	if (highlight != buffer->priv->highlight_brackets)
1300 	{
1301 		GtkTextIter iter;
1302 		GtkTextMark *mark;
1303 
1304 		buffer->priv->highlight_brackets = highlight;
1305 
1306 		/* try to see if there is already a bracket match at the
1307 		 * current position, but only if the tag table is already set
1308 		 * otherwise we have problems when calling this function
1309 		 * on init (get_insert creates the tag table as a side effect */
1310 		if (buffer->priv->constructed)
1311 		{
1312 			mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
1313 			gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, mark);
1314 			gtk_source_buffer_move_cursor (GTK_TEXT_BUFFER (buffer), &iter, mark);
1315 		}
1316 
1317 		g_object_notify (G_OBJECT (buffer), "highlight-matching-brackets");
1318 	}
1319 }
1320 
1321 /**
1322  * gtk_source_buffer_get_highlight_syntax:
1323  * @buffer: a #GtkSourceBuffer.
1324  *
1325  * Determines whether syntax highlighting is activated in the source
1326  * buffer.
1327  *
1328  * Return value: %TRUE if syntax highlighting is enabled, %FALSE otherwise.
1329  **/
1330 gboolean
gtk_source_buffer_get_highlight_syntax(GtkSourceBuffer * buffer)1331 gtk_source_buffer_get_highlight_syntax (GtkSourceBuffer *buffer)
1332 {
1333 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
1334 
1335 	return (buffer->priv->highlight_syntax != FALSE);
1336 }
1337 
1338 /**
1339  * gtk_source_buffer_set_highlight_syntax:
1340  * @buffer: a #GtkSourceBuffer.
1341  * @highlight: %TRUE to enable syntax highlighting, %FALSE to disable it.
1342  *
1343  * Controls whether syntax is highlighted in the buffer. If @highlight
1344  * is %TRUE, the text will be highlighted according to the syntax
1345  * patterns specified in the language set with
1346  * gtk_source_buffer_set_language(). If @highlight is %FALSE, syntax highlighting
1347  * is disabled and all the GtkTextTag objects that have been added by the
1348  * syntax highlighting engine are removed from the buffer.
1349  **/
1350 void
gtk_source_buffer_set_highlight_syntax(GtkSourceBuffer * buffer,gboolean highlight)1351 gtk_source_buffer_set_highlight_syntax (GtkSourceBuffer *buffer,
1352 					gboolean         highlight)
1353 {
1354 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1355 
1356 	highlight = (highlight != FALSE);
1357 
1358 	if (buffer->priv->highlight_syntax != highlight)
1359 	{
1360 		buffer->priv->highlight_syntax = highlight;
1361 		g_object_notify (G_OBJECT (buffer), "highlight-syntax");
1362 	}
1363 }
1364 
1365 /**
1366  * gtk_source_buffer_set_language:
1367  * @buffer: a #GtkSourceBuffer.
1368  * @language: a #GtkSourceLanguage to set, or %NULL.
1369  *
1370  * Associate a #GtkSourceLanguage with the source buffer. If @language is
1371  * not-%NULL and syntax highlighting is enabled (see gtk_source_buffer_set_highlight_syntax()),
1372  * the syntax patterns defined in @language will be used to highlight the text
1373  * contained in the buffer. If @language is %NULL, the text contained in the
1374  * buffer is not highlighted.
1375  *
1376  * The buffer holds a reference to @language.
1377  **/
1378 void
gtk_source_buffer_set_language(GtkSourceBuffer * buffer,GtkSourceLanguage * language)1379 gtk_source_buffer_set_language (GtkSourceBuffer   *buffer,
1380 				GtkSourceLanguage *language)
1381 {
1382 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1383 	g_return_if_fail (GTK_IS_SOURCE_LANGUAGE (language) || language == NULL);
1384 
1385 	if (buffer->priv->language == language)
1386 		return;
1387 
1388 	if (buffer->priv->highlight_engine != NULL)
1389 	{
1390 		/* disconnect the old engine */
1391 		_gtk_source_engine_attach_buffer (buffer->priv->highlight_engine, NULL);
1392 		g_object_unref (buffer->priv->highlight_engine);
1393 		buffer->priv->highlight_engine = NULL;
1394 	}
1395 
1396 	if (buffer->priv->language != NULL)
1397 		g_object_unref (buffer->priv->language);
1398 
1399 	buffer->priv->language = language;
1400 
1401 	if (language != NULL)
1402 	{
1403 		g_object_ref (language);
1404 
1405 		/* get a new engine */
1406 		buffer->priv->highlight_engine = _gtk_source_language_create_engine (language);
1407 
1408 		if (buffer->priv->highlight_engine)
1409 		{
1410 			_gtk_source_engine_attach_buffer (buffer->priv->highlight_engine,
1411 							  GTK_TEXT_BUFFER (buffer));
1412 
1413 			if (buffer->priv->style_scheme)
1414 				_gtk_source_engine_set_style_scheme (buffer->priv->highlight_engine,
1415 								     buffer->priv->style_scheme);
1416 		}
1417 	}
1418 
1419 	g_object_notify (G_OBJECT (buffer), "language");
1420 }
1421 
1422 /**
1423  * gtk_source_buffer_get_language:
1424  * @buffer: a #GtkSourceBuffer.
1425  *
1426  * Returns the #GtkSourceLanguage associated with the buffer,
1427  * see gtk_source_buffer_set_language().  The returned object should not be
1428  * unreferenced by the user.
1429  *
1430  * Return value: #GtkSourceLanguage associated with the buffer, or %NULL.
1431  **/
1432 GtkSourceLanguage *
gtk_source_buffer_get_language(GtkSourceBuffer * buffer)1433 gtk_source_buffer_get_language (GtkSourceBuffer *buffer)
1434 {
1435 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
1436 
1437 	return buffer->priv->language;
1438 }
1439 
1440 /**
1441  * _gtk_source_buffer_update_highlight:
1442  * @buffer: a #GtkSourceBuffer.
1443  * @start: start of the area to highlight.
1444  * @end: end of the area to highlight.
1445  * @synchronous: whether the area should be highlighted synchronously.
1446  *
1447  * Asks the buffer to analyze and highlight given area.
1448  **/
1449 void
_gtk_source_buffer_update_highlight(GtkSourceBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gboolean synchronous)1450 _gtk_source_buffer_update_highlight (GtkSourceBuffer   *buffer,
1451 				     const GtkTextIter *start,
1452 				     const GtkTextIter *end,
1453 				     gboolean           synchronous)
1454 {
1455 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1456 
1457 	if (buffer->priv->highlight_engine != NULL)
1458 		_gtk_source_engine_update_highlight (buffer->priv->highlight_engine,
1459 						     start,
1460 						     end,
1461 						     synchronous);
1462 }
1463 
1464 /**
1465  * gtk_source_buffer_ensure_highlight:
1466  * @buffer: a #GtkSourceBuffer.
1467  * @start: start of the area to highlight.
1468  * @end: end of the area to highlight.
1469  *
1470  * Forces buffer to analyze and highlight the given area synchronously.
1471  *
1472  * <note>
1473  *   <para>
1474  *     This is a potentially slow operation and should be used only
1475  *     when you need to make sure that some text not currently
1476  *     visible is highlighted, for instance before printing.
1477  *   </para>
1478  * </note>
1479  **/
1480 void
gtk_source_buffer_ensure_highlight(GtkSourceBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end)1481 gtk_source_buffer_ensure_highlight (GtkSourceBuffer   *buffer,
1482 				    const GtkTextIter *start,
1483 				    const GtkTextIter *end)
1484 {
1485 	_gtk_source_buffer_update_highlight (buffer,
1486 					     start,
1487 					     end,
1488 					     TRUE);
1489 }
1490 
1491 /**
1492  * gtk_source_buffer_set_style_scheme:
1493  * @buffer: a #GtkSourceBuffer.
1494  * @scheme: style scheme.
1495  *
1496  * Sets style scheme used by the buffer. If @scheme is %NULL no
1497  * style scheme is used.
1498  **/
1499 void
gtk_source_buffer_set_style_scheme(GtkSourceBuffer * buffer,GtkSourceStyleScheme * scheme)1500 gtk_source_buffer_set_style_scheme (GtkSourceBuffer      *buffer,
1501 				    GtkSourceStyleScheme *scheme)
1502 {
1503 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
1504 	g_return_if_fail (GTK_IS_SOURCE_STYLE_SCHEME (scheme) || scheme == NULL);
1505 
1506 	if (buffer->priv->style_scheme == scheme)
1507 		return;
1508 
1509 	if (buffer->priv->style_scheme)
1510 		g_object_unref (buffer->priv->style_scheme);
1511 
1512 	buffer->priv->style_scheme = scheme ? g_object_ref (scheme) : NULL;
1513 	update_bracket_match_style (buffer);
1514 
1515 	if (buffer->priv->highlight_engine != NULL)
1516 		_gtk_source_engine_set_style_scheme (buffer->priv->highlight_engine,
1517 						     scheme);
1518 
1519 	g_object_notify (G_OBJECT (buffer), "style-scheme");
1520 }
1521 
1522 /**
1523  * gtk_source_buffer_get_style_scheme:
1524  * @buffer: a #GtkSourceBuffer.
1525  *
1526  * Returns the #GtkSourceStyleScheme currently used in @buffer.
1527  *
1528  * Returns: the #GtkSourceStyleScheme set by
1529  * gtk_source_buffer_set_style_scheme(), or %NULL.
1530  **/
1531 GtkSourceStyleScheme *
gtk_source_buffer_get_style_scheme(GtkSourceBuffer * buffer)1532 gtk_source_buffer_get_style_scheme (GtkSourceBuffer *buffer)
1533 {
1534 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
1535 	return buffer->priv->style_scheme;
1536 }
1537 
1538 /* Source Marks functionality */
1539 
1540 /* At the moment this is pretty dumb (O(N)), if it is a performance
1541  * problem we should change data struct.
1542  * Since it's used from mark_set when the mark was moved, we cannot bsearch.
1543  * Returns TRUE if the mark was found and removed */
1544 static gboolean
source_mark_remove(GtkSourceBuffer * buffer,GtkSourceMark * mark)1545 source_mark_remove (GtkSourceBuffer *buffer, GtkSourceMark *mark)
1546 {
1547 	guint i;
1548 
1549 	for (i = 0; i < buffer->priv->source_marks->len; ++i)
1550 	{
1551 		GtkSourceMark *m;
1552 
1553 		m = g_array_index (buffer->priv->source_marks, GtkSourceMark *, i);
1554 		if (mark == m)
1555 		{
1556 			g_array_remove_index (buffer->priv->source_marks, i);
1557 			g_object_unref (m);
1558 
1559 			return TRUE;
1560 		}
1561 	}
1562 
1563 	return FALSE;
1564 }
1565 
1566 /* Performs a binary search among the source marks in @buffer for the
1567  * position of the @iter.  Returns the nearest matching mark (its
1568  * index in the marks array) and optionally the value of the
1569  * comparision between the given iter and the iter at the returned mark.
1570  *
1571  * Return value: an index in the source marks array or -1 if the array is
1572  * empty.
1573  */
1574 static gint
source_mark_bsearch(GtkSourceBuffer * buffer,GtkTextIter * iter,gint * last_cmp)1575 source_mark_bsearch (GtkSourceBuffer *buffer, GtkTextIter *iter, gint *last_cmp)
1576 {
1577 	GtkTextIter check_iter;
1578 	GtkSourceMark **check, **p;
1579 	GArray *marks = buffer->priv->source_marks;
1580 	gint n_marks = marks->len;
1581 	gint cmp, i;
1582 
1583 	if (n_marks == 0)
1584 		return -1;
1585 
1586 	check = p = &g_array_index (marks, GtkSourceMark *, 0);
1587 	p--;
1588 	cmp = 0;
1589 	while (n_marks)
1590 	{
1591 		i = (n_marks + 1) >> 1;
1592 		check = p + i;
1593 		gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
1594 						  &check_iter,
1595 						  GTK_TEXT_MARK (*check));
1596 		cmp = gtk_text_iter_compare (iter, &check_iter);
1597 		if (cmp > 0)
1598 		{
1599 			n_marks -= i;
1600 			p = check;
1601 		}
1602 		else if (cmp < 0)
1603 			n_marks = i - 1;
1604 		else /* if (cmp == 0) */
1605 			break;
1606 	}
1607 
1608 	i = check - &g_array_index (marks, GtkSourceMark *, 0);
1609 	if (last_cmp)
1610 		*last_cmp = cmp;
1611 
1612 	return i;
1613 }
1614 
1615 static void
source_mark_insert(GtkSourceBuffer * buffer,GtkSourceMark * mark)1616 source_mark_insert (GtkSourceBuffer *buffer, GtkSourceMark *mark)
1617 {
1618 	GtkTextIter iter;
1619 	gint idx, cmp;
1620 
1621 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
1622 					  &iter,
1623 					  GTK_TEXT_MARK (mark));
1624 
1625 	idx = source_mark_bsearch (buffer, &iter, &cmp);
1626 	if (idx >= 0)
1627 	{
1628 		/* if the mark we found is at same iter or before
1629 		 * put our mark after that */
1630 		if (cmp >= 0)
1631 			idx++;
1632 	}
1633 	else
1634 	{
1635 		idx = 0;
1636 	}
1637 
1638 	g_object_ref (mark);
1639 	g_array_insert_val (buffer->priv->source_marks, idx, mark);
1640 }
1641 
1642 static void
gtk_source_buffer_real_apply_tag(GtkTextBuffer * buffer,GtkTextTag * tag,const GtkTextIter * start,const GtkTextIter * end)1643 gtk_source_buffer_real_apply_tag (GtkTextBuffer     *buffer,
1644                                   GtkTextTag        *tag,
1645                                   const GtkTextIter *start,
1646                                   const GtkTextIter *end)
1647 {
1648 	GtkSourceBuffer *source;
1649 
1650 	source = GTK_SOURCE_BUFFER (buffer);
1651 
1652 	/* We only allow the bracket match tag to be applied when we are doing
1653 	   it ourselves (i.e. when allow_bracket_match is TRUE). The reason for
1654 	   doing so is that when you copy/paste from the same buffer, the tags
1655 	   get pasted too. This is ok for highlighting because the region will
1656 	   get rehighlighted, but not for bracket matching. */
1657 	if (source->priv->allow_bracket_match || tag != get_bracket_match_tag (source))
1658 	{
1659 		GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->apply_tag (buffer, tag, start, end);
1660 	}
1661 }
1662 
1663 static void
gtk_source_buffer_real_mark_set(GtkTextBuffer * buffer,const GtkTextIter * location,GtkTextMark * mark)1664 gtk_source_buffer_real_mark_set	(GtkTextBuffer     *buffer,
1665 				 const GtkTextIter *location,
1666 				 GtkTextMark       *mark)
1667 {
1668 	if (GTK_IS_SOURCE_MARK (mark))
1669 	{
1670 		/* for now we simply remove and reinsert at
1671 		 * the right place every time */
1672 		source_mark_remove (GTK_SOURCE_BUFFER (buffer),
1673 				    GTK_SOURCE_MARK (mark));
1674 		source_mark_insert (GTK_SOURCE_BUFFER (buffer),
1675 				    GTK_SOURCE_MARK (mark));
1676 
1677 		g_signal_emit_by_name (buffer, "source_mark_updated", mark);
1678 	}
1679 
1680 	/* if the mark is the insert mark, update bracket matching */
1681 	else if (mark == gtk_text_buffer_get_insert (buffer))
1682 	{
1683 		gtk_source_buffer_move_cursor (buffer, location, mark);
1684 	}
1685 
1686 	GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->mark_set (buffer, location, mark);
1687 }
1688 
1689 static void
gtk_source_buffer_real_mark_deleted(GtkTextBuffer * buffer,GtkTextMark * mark)1690 gtk_source_buffer_real_mark_deleted (GtkTextBuffer *buffer,
1691 				     GtkTextMark *mark)
1692 {
1693 	if (GTK_IS_SOURCE_MARK (mark))
1694 	{
1695 		source_mark_remove (GTK_SOURCE_BUFFER (buffer),
1696 				    GTK_SOURCE_MARK (mark));
1697 
1698 		g_signal_emit_by_name (buffer, "source_mark_updated", mark);
1699 	}
1700 
1701 	if (GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->mark_deleted != NULL)
1702 		GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->mark_deleted (buffer, mark);
1703 }
1704 
1705 static void
gtk_source_buffer_real_undo(GtkSourceBuffer * buffer)1706 gtk_source_buffer_real_undo (GtkSourceBuffer *buffer)
1707 {
1708 	g_return_if_fail (gtk_source_undo_manager_can_undo (buffer->priv->undo_manager));
1709 
1710 	gtk_source_undo_manager_undo (buffer->priv->undo_manager);
1711 }
1712 
1713 static void
gtk_source_buffer_real_redo(GtkSourceBuffer * buffer)1714 gtk_source_buffer_real_redo (GtkSourceBuffer *buffer)
1715 {
1716 	g_return_if_fail (gtk_source_undo_manager_can_redo (buffer->priv->undo_manager));
1717 
1718 	gtk_source_undo_manager_redo (buffer->priv->undo_manager);
1719 }
1720 
1721 /**
1722  * gtk_source_buffer_create_source_mark:
1723  * @buffer: a #GtkSourceBuffer.
1724  * @name: the name of the mark, or %NULL.
1725  * @category: a string defining the mark category.
1726  * @where: location to place the mark.
1727  *
1728  * Creates a source mark in the @buffer of category @category.  A source mark is
1729  * a #GtkTextMark but organised into categories. Depending on the category
1730  * a pixbuf can be specified that will be displayed along the line of the mark.
1731  *
1732  * Like a #GtkTextMark, a #GtkSourceMark can be anonymous if the
1733  * passed @name is %NULL.  Also, the buffer owns the marks so you
1734  * shouldn't unreference it.
1735  *
1736  * Marks always have left gravity and are moved to the beginning of
1737  * the line when the user deletes the line they were in.
1738  *
1739  * Typical uses for a source mark are bookmarks, breakpoints, current
1740  * executing instruction indication in a source file, etc..
1741  *
1742  * Return value: a new #GtkSourceMark, owned by the buffer.
1743  *
1744  * Since: 2.2
1745  **/
1746 GtkSourceMark *
gtk_source_buffer_create_source_mark(GtkSourceBuffer * buffer,const gchar * name,const gchar * category,const GtkTextIter * where)1747 gtk_source_buffer_create_source_mark (GtkSourceBuffer   *buffer,
1748 				      const gchar       *name,
1749 				      const gchar       *category,
1750 				      const GtkTextIter *where)
1751 {
1752 	GtkSourceMark *mark;
1753 
1754 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
1755 	g_return_val_if_fail (category != NULL, NULL);
1756 	g_return_val_if_fail (where != NULL, NULL);
1757 
1758 	mark = gtk_source_mark_new (name, category);
1759 	gtk_text_buffer_add_mark (GTK_TEXT_BUFFER (buffer),
1760 				  GTK_TEXT_MARK (mark),
1761 				  where);
1762 
1763 	return mark;
1764 }
1765 
1766 GtkSourceMark *
_gtk_source_buffer_source_mark_next(GtkSourceBuffer * buffer,GtkSourceMark * mark,const gchar * category)1767 _gtk_source_buffer_source_mark_next (GtkSourceBuffer *buffer,
1768 				     GtkSourceMark   *mark,
1769 				     const gchar     *category)
1770 {
1771 	GtkTextIter iter;
1772 	gint idx, cmp;
1773 
1774 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
1775 
1776 	/* TODO: we could speed this up by caching the current
1777 	 * position in the mark and invalidating the cache when
1778 	 * the marks array changes. For now we always lookup. */
1779 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
1780 					  &iter,
1781 					  GTK_TEXT_MARK (mark));
1782 
1783 	idx = source_mark_bsearch (buffer, &iter, &cmp);
1784 
1785 	/* the array should already contain @mark */
1786 	g_return_val_if_fail (idx >= 0, NULL);
1787 	g_return_val_if_fail (cmp == 0, NULL);
1788 
1789 	/* move up to our mark among the ones at this position */
1790 	while (mark != g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx))
1791 	{
1792 		++idx;
1793 	}
1794 
1795 	while ((guint) ++idx < buffer->priv->source_marks->len)
1796 	{
1797 		GtkSourceMark *ret;
1798 
1799 		ret = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
1800 		if (category == NULL ||
1801 		    0 == strcmp (category, gtk_source_mark_get_category (ret)))
1802 		{
1803 			return ret;
1804 		}
1805 	}
1806 
1807 	return NULL;
1808 }
1809 
1810 GtkSourceMark *
_gtk_source_buffer_source_mark_prev(GtkSourceBuffer * buffer,GtkSourceMark * mark,const gchar * category)1811 _gtk_source_buffer_source_mark_prev (GtkSourceBuffer *buffer,
1812 				     GtkSourceMark   *mark,
1813 				     const gchar     *category)
1814 {
1815 	GtkTextIter iter;
1816 	gint idx, cmp;
1817 
1818 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
1819 
1820 	/* TODO: we could speed this up by caching the current
1821 	 * position in the mark and invalidating the cache when
1822 	 * the marks array changes. For now we always lookup. */
1823 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
1824 					  &iter,
1825 					  GTK_TEXT_MARK (mark));
1826 
1827 	idx = source_mark_bsearch (buffer, &iter, &cmp);
1828 
1829 	/* the array should already contain @mark */
1830 	g_return_val_if_fail (idx >= 0, NULL);
1831 	g_return_val_if_fail (cmp == 0, NULL);
1832 
1833 	/* move up to our mark among the ones at this position */
1834 	while (mark != g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx))
1835 	{
1836 		++idx;
1837 	}
1838 
1839 	while (--idx >= 0)
1840 	{
1841 		GtkSourceMark *ret;
1842 
1843 		ret = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
1844 		if (category == NULL ||
1845 		    0 == strcmp (category, gtk_source_mark_get_category (ret)))
1846 		{
1847 			return ret;
1848 		}
1849 	}
1850 
1851 	return NULL;
1852 }
1853 
1854 /**
1855  * gtk_source_buffer_forward_iter_to_source_mark:
1856  * @buffer: a #GtkSourceBuffer.
1857  * @iter: an iterator.
1858  * @category: category to search for or %NULL
1859  *
1860  * Moves @iter to the position of the next #GtkSourceMark of the given
1861  * @category. Returns #TRUE if @iter was moved. If @category is NULL, the
1862  * next source mark can be of any category.
1863  *
1864  * Returns: whether iter moved.
1865  *
1866  * Since: 2.2
1867  **/
1868 gboolean
gtk_source_buffer_forward_iter_to_source_mark(GtkSourceBuffer * buffer,GtkTextIter * iter,const gchar * category)1869 gtk_source_buffer_forward_iter_to_source_mark (GtkSourceBuffer *buffer,
1870 					       GtkTextIter     *iter,
1871 					       const gchar     *category)
1872 {
1873 	GtkTextIter i;
1874 	gint idx, cmp;
1875 
1876 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
1877 	g_return_val_if_fail (iter != NULL, FALSE);
1878 
1879 	i = *iter;
1880 
1881 	idx = source_mark_bsearch (buffer, &i, &cmp);
1882 	if (idx < 0)
1883 		return FALSE;
1884 
1885 	if (cmp >= 0)
1886 		++idx;
1887 
1888 	while ((guint) idx < buffer->priv->source_marks->len)
1889 	{
1890 		GtkSourceMark *mark;
1891 
1892 		mark = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
1893 		if (category == NULL ||
1894 		    0 == strcmp (category, gtk_source_mark_get_category (mark)))
1895 		{
1896 			/* update the iter */
1897 			gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
1898 							  &i, GTK_TEXT_MARK (mark));
1899 
1900 			if (gtk_text_iter_compare (&i, iter) > 0 )
1901 			{
1902 				*iter = i;
1903 				return TRUE;
1904 			}
1905 		}
1906 
1907 		++idx;
1908 	}
1909 
1910 	return FALSE;
1911 }
1912 
1913 /**
1914  * gtk_source_buffer_backward_iter_to_source_mark:
1915  * @buffer: a #GtkSourceBuffer.
1916  * @iter: an iterator.
1917  * @category: category to search for or %NULL
1918  *
1919  * Moves @iter to the position of the previous #GtkSourceMark of the given
1920  * category. Returns #TRUE if @iter was moved. If @category is NULL, the
1921  * previous source mark can be of any category.
1922  *
1923  * Returns: whether iter moved.
1924  *
1925  * Since: 2.2
1926  **/
1927 gboolean
gtk_source_buffer_backward_iter_to_source_mark(GtkSourceBuffer * buffer,GtkTextIter * iter,const gchar * category)1928 gtk_source_buffer_backward_iter_to_source_mark (GtkSourceBuffer *buffer,
1929 						GtkTextIter     *iter,
1930 						const gchar     *category)
1931 {
1932 	GtkTextIter i;
1933 	gint idx, cmp;
1934 
1935 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
1936 	g_return_val_if_fail (iter != NULL, FALSE);
1937 
1938 	i = *iter;
1939 
1940 	idx = source_mark_bsearch (buffer, &i, &cmp);
1941 	if (idx < 0)
1942 		return FALSE;
1943 
1944 	if (cmp <= 0)
1945 		--idx;
1946 
1947 	while (idx >= 0)
1948 	{
1949 		GtkSourceMark *mark;
1950 
1951 		mark = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
1952 		if (category == NULL ||
1953 		    0 == strcmp (category, gtk_source_mark_get_category (mark)))
1954 		{
1955 			gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
1956 							  &i, GTK_TEXT_MARK (mark));
1957 
1958 			if (gtk_text_iter_compare (&i, iter) < 0 )
1959 			{
1960 				*iter = i;
1961 				return TRUE;
1962 			}
1963 		}
1964 
1965 		--idx;
1966 	}
1967 
1968 	return FALSE;
1969 }
1970 
1971 /**
1972  * gtk_source_buffer_get_source_marks_at_iter:
1973  * @buffer: a #GtkSourceBuffer.
1974  * @iter: an iterator.
1975  * @category: category to search for or %NULL
1976  *
1977  * Returns the list of marks of the given category at @iter. If @category
1978  * is %NULL it returns all marks at @iter.
1979  *
1980  * Returns: a newly allocated #GSList.
1981  *
1982  * Since: 2.2
1983  **/
1984 GSList *
gtk_source_buffer_get_source_marks_at_iter(GtkSourceBuffer * buffer,GtkTextIter * iter,const gchar * category)1985 gtk_source_buffer_get_source_marks_at_iter (GtkSourceBuffer *buffer,
1986 					    GtkTextIter     *iter,
1987 					    const gchar     *category)
1988 {
1989 	GSList *marks, *l, *res;
1990 
1991 	res = NULL;
1992 	marks = gtk_text_iter_get_marks (iter);
1993 
1994 	for (l = marks; l != NULL; l = l->next)
1995 	{
1996 		GtkSourceMark *mark;
1997 
1998 		if (!GTK_IS_SOURCE_MARK (l->data))
1999 			continue;
2000 
2001 		mark = GTK_SOURCE_MARK (l->data);
2002 		if (category == NULL ||
2003 		    0 == strcmp (category, gtk_source_mark_get_category (mark)))
2004 		{
2005 			res = g_slist_prepend (res, l->data);
2006 		}
2007 	}
2008 
2009 	g_slist_free (marks);
2010 
2011 	return g_slist_reverse (res);
2012 }
2013 
2014 /**
2015  * gtk_source_buffer_get_source_marks_at_line:
2016  * @buffer: a #GtkSourceBuffer.
2017  * @line: a line number.
2018  * @category: category to search for or %NULL
2019  *
2020  * Returns the list of marks of the given category at @line.
2021  * If @category is NULL, all marks at @line are returned.
2022  *
2023  * Returns: a newly allocated #GSList.
2024  *
2025  * Since: 2.2
2026  **/
2027 GSList *
gtk_source_buffer_get_source_marks_at_line(GtkSourceBuffer * buffer,gint line,const gchar * category)2028 gtk_source_buffer_get_source_marks_at_line (GtkSourceBuffer *buffer,
2029 					    gint             line,
2030 					    const gchar     *category)
2031 {
2032 	GtkTextIter iter;
2033 	GSList *res;
2034 
2035  	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
2036 
2037 	gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
2038 					  &iter, line);
2039 
2040 	res = gtk_source_buffer_get_source_marks_at_iter (buffer,
2041 							  &iter,
2042 							  category);
2043 
2044 	while (gtk_source_buffer_forward_iter_to_source_mark (buffer,
2045 							      &iter,
2046 							      category))
2047 	{
2048 		if (gtk_text_iter_get_line (&iter) == line)
2049 		{
2050 			GSList *l;
2051 
2052 			l =  gtk_source_buffer_get_source_marks_at_iter (buffer,
2053 									 &iter,
2054 									 category);
2055 
2056 			res = g_slist_concat (res, l);
2057 		}
2058 		else
2059 		{
2060 			break;
2061 		}
2062 	}
2063 
2064 	return res;
2065 }
2066 
2067 /**
2068  * gtk_source_buffer_remove_source_marks:
2069  * @buffer: a #GtkSourceBuffer.
2070  * @start: a #GtkTextIter
2071  * @end: a #GtkTextIter
2072  * @category: category to search for or NULL
2073  *
2074  * Remove all marks of @category between @start and @end from the buffer.
2075  * If @category is NULL, all marks in the range will be removed.
2076  *
2077  * Since: 2.2
2078  **/
2079 void
gtk_source_buffer_remove_source_marks(GtkSourceBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,const gchar * category)2080 gtk_source_buffer_remove_source_marks (GtkSourceBuffer   *buffer,
2081 				       const GtkTextIter *start,
2082 				       const GtkTextIter *end,
2083 				       const gchar       *category)
2084 {
2085 	GtkTextIter iter;
2086 	GSList *list;
2087 	GSList *l;
2088 
2089  	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
2090  	g_return_if_fail (start != NULL);
2091  	g_return_if_fail (end != NULL);
2092 
2093 	iter = *start;
2094 
2095 	list = gtk_source_buffer_get_source_marks_at_iter (buffer,
2096 							   &iter,
2097 							   category);
2098 
2099 	while (gtk_source_buffer_forward_iter_to_source_mark (buffer,
2100 							      &iter,
2101 							      category))
2102 	{
2103 		if (gtk_text_iter_compare (&iter, end) <= 0)
2104 		{
2105 			l =  gtk_source_buffer_get_source_marks_at_iter (buffer,
2106 									 &iter,
2107 									 category);
2108 
2109 			list = g_slist_concat (list, l);
2110 		}
2111 		else
2112 		{
2113 			break;
2114 		}
2115 	}
2116 
2117 	for (l = list; l != NULL; l = l->next)
2118 	{
2119 		gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer),
2120 					     GTK_TEXT_MARK (l->data));
2121 	}
2122 
2123 	g_slist_free (list);
2124 }
2125 
2126 
2127 /**
2128  * gtk_source_buffer_iter_has_context_class:
2129  * @buffer: a #GtkSourceBuffer.
2130  * @iter: a #GtkTextIter
2131  * @context_class: class to search for
2132  *
2133  * Check if the class @context_klass is set on @iter.
2134  *
2135  * Since: 2.10
2136  **/
2137 gboolean
gtk_source_buffer_iter_has_context_class(GtkSourceBuffer * buffer,const GtkTextIter * iter,const gchar * context_class)2138 gtk_source_buffer_iter_has_context_class (GtkSourceBuffer   *buffer,
2139                                           const GtkTextIter *iter,
2140                                           const gchar       *context_class)
2141 {
2142 	GtkTextTag *tag;
2143 
2144 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
2145 	g_return_val_if_fail (iter != NULL, FALSE);
2146 	g_return_val_if_fail (context_class != NULL, FALSE);
2147 
2148 	if (buffer->priv->highlight_engine == NULL)
2149 	{
2150 		return FALSE;
2151 	}
2152 
2153 	tag = _gtk_source_engine_get_context_class_tag (buffer->priv->highlight_engine,
2154 							context_class);
2155 
2156 	if (tag != NULL)
2157 	{
2158 		return gtk_text_iter_has_tag (iter, tag);
2159 	}
2160 	else
2161 	{
2162 		return FALSE;
2163 	}
2164 }
2165 
2166 /**
2167  * gtk_source_buffer_get_context_classes_at_iter:
2168  * @buffer: a #GtkSourceBuffer.
2169  * @iter: a #GtkTextIter
2170  *
2171  * Get all defined context classes at @iter.
2172  *
2173  * Returns: a new %NULL terminated array of context class names. Use
2174  *          #g_strfreev to free the array if it is no longer needed.
2175  *
2176  * Since: 2.10
2177  **/
2178 gchar **
gtk_source_buffer_get_context_classes_at_iter(GtkSourceBuffer * buffer,const GtkTextIter * iter)2179 gtk_source_buffer_get_context_classes_at_iter (GtkSourceBuffer   *buffer,
2180                                                const GtkTextIter *iter)
2181 {
2182 	GSList *tags;
2183 	GSList *item;
2184 	GPtrArray *ret;
2185 
2186 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
2187 	g_return_val_if_fail (iter != NULL, NULL);
2188 
2189 	tags = gtk_text_iter_get_tags (iter);
2190 	ret = g_ptr_array_new ();
2191 
2192 	for (item = tags; item; item = g_slist_next (item))
2193 	{
2194 		gchar const *name = g_object_get_data (G_OBJECT (item->data),
2195 		                                       TAG_CONTEXT_CLASS_NAME);
2196 
2197 		if (name != NULL)
2198 		{
2199 			g_ptr_array_add (ret, g_strdup (name));
2200 		}
2201 	}
2202 
2203 	g_ptr_array_add (ret, NULL);
2204 	return (gchar **) g_ptr_array_free (ret, FALSE);
2205 }
2206 
2207 /**
2208  * gtk_source_buffer_iter_forward_to_context_class_toggle:
2209  * @buffer: a #GtkSourceBuffer.
2210  * @iter: a #GtkTextIter
2211  * @context_class: the context class
2212  *
2213  * Moves forward to the next toggle (on or off) of the context class. If no
2214  * matching context class toggles are found, returns %FALSE, otherwise %TRUE.
2215  * Does not return toggles located at @iter, only toggles after @iter. Sets
2216  * @iter to the location of the toggle, or to the end of the buffer if no
2217  * toggle is found.
2218  *
2219  * Returns: whether we found a context class toggle after @iter
2220  *
2221  * Since: 2.10
2222  **/
2223 gboolean
gtk_source_buffer_iter_forward_to_context_class_toggle(GtkSourceBuffer * buffer,GtkTextIter * iter,const gchar * context_class)2224 gtk_source_buffer_iter_forward_to_context_class_toggle (GtkSourceBuffer *buffer,
2225                                                         GtkTextIter     *iter,
2226                                                         const gchar     *context_class)
2227 {
2228 	GtkTextTag *tag;
2229 
2230 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
2231 	g_return_val_if_fail (iter != NULL, FALSE);
2232 	g_return_val_if_fail (context_class != NULL, FALSE);
2233 
2234 	if (buffer->priv->highlight_engine == NULL)
2235 	{
2236 		return FALSE;
2237 	}
2238 
2239 	tag = _gtk_source_engine_get_context_class_tag (buffer->priv->highlight_engine,
2240 							context_class);
2241 
2242 	if (tag == NULL)
2243 	{
2244 		return FALSE;
2245 	}
2246 	else
2247 	{
2248 		return gtk_text_iter_forward_to_tag_toggle (iter, tag);
2249 	}
2250 }
2251 
2252 /**
2253  * gtk_source_buffer_iter_backward_to_context_class_toggle:
2254  * @buffer: a #GtkSourceBuffer.
2255  * @iter: a #GtkTextIter
2256  * @context_class: the context class
2257  *
2258  * Moves backward to the next toggle (on or off) of the context class. If no
2259  * matching context class toggles are found, returns %FALSE, otherwise %TRUE.
2260  * Does not return toggles located at @iter, only toggles after @iter. Sets
2261  * @iter to the location of the toggle, or to the end of the buffer if no
2262  * toggle is found.
2263  *
2264  * Returns: whether we found a context class toggle before @iter
2265  *
2266  * Since: 2.10
2267  **/
2268 gboolean
gtk_source_buffer_iter_backward_to_context_class_toggle(GtkSourceBuffer * buffer,GtkTextIter * iter,const gchar * context_class)2269 gtk_source_buffer_iter_backward_to_context_class_toggle (GtkSourceBuffer *buffer,
2270                                                          GtkTextIter     *iter,
2271                                                          const gchar     *context_class)
2272 {
2273 	GtkTextTag *tag;
2274 
2275 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
2276 	g_return_val_if_fail (iter != NULL, FALSE);
2277 	g_return_val_if_fail (context_class != NULL, FALSE);
2278 
2279 	if (buffer->priv->highlight_engine == NULL)
2280 	{
2281 		return FALSE;
2282 	}
2283 
2284 	tag = _gtk_source_engine_get_context_class_tag (buffer->priv->highlight_engine,
2285 							context_class);
2286 
2287 	if (tag == NULL)
2288 	{
2289 		return FALSE;
2290 	}
2291 	else
2292 	{
2293 		return gtk_text_iter_backward_to_tag_toggle (iter, tag);
2294 	}
2295 }
2296 
2297 /**
2298  * gtk_source_buffer_set_undo_manager:
2299  * @buffer: A #GtkSourceBuffer
2300  * @manager: A #GtkSourceUndoManager
2301  *
2302  * Set the buffer undo manager. If @manager is %NULL the default undo manager
2303  * will be set.
2304  *
2305  **/
2306 void
gtk_source_buffer_set_undo_manager(GtkSourceBuffer * buffer,GtkSourceUndoManager * manager)2307 gtk_source_buffer_set_undo_manager (GtkSourceBuffer      *buffer,
2308                                     GtkSourceUndoManager *manager)
2309 {
2310 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
2311 	g_return_if_fail (manager == NULL || GTK_IS_SOURCE_UNDO_MANAGER (manager));
2312 
2313 	if (manager == NULL)
2314 	{
2315 		manager = g_object_new (GTK_TYPE_SOURCE_UNDO_MANAGER_DEFAULT,
2316 		                        "buffer", buffer,
2317 		                        "max-undo-levels", buffer->priv->max_undo_levels,
2318 		                        NULL);
2319 	}
2320 	else
2321 	{
2322 		g_object_ref (manager);
2323 	}
2324 
2325 	set_undo_manager (buffer, manager);
2326 	g_object_unref (manager);
2327 
2328 	g_object_notify (G_OBJECT (buffer), "undo-manager");
2329 }
2330 
2331 /**
2332  * gtk_source_buffer_get_undo_manager:
2333  * @buffer: A #GtkSourceBuffer
2334  *
2335  * Get the undo manager associated with the buffer.
2336  *
2337  * Returns: A #GtkSourceUndoManager
2338  *
2339  **/
2340 GtkSourceUndoManager *
gtk_source_buffer_get_undo_manager(GtkSourceBuffer * buffer)2341 gtk_source_buffer_get_undo_manager (GtkSourceBuffer *buffer)
2342 {
2343 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
2344 
2345 	return buffer->priv->undo_manager;
2346 }
2347