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