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