[gnome-builder/wip/gtk4-port: 108/736] libide/sourceview: archive old implementation
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port: 108/736] libide/sourceview: archive old implementation
- Date: Tue, 26 Apr 2022 01:46:12 +0000 (UTC)
commit f21ec8c2f1d69d6324317d9483bb8afb8d5a1653
Author: Christian Hergert <chergert redhat com>
Date: Sun Mar 27 00:45:36 2022 -0700
libide/sourceview: archive old implementation
We will instead just start selectively adding things back to the
sourceview as necessary. We really want to push out as much as we can
in the port.
.../{ide-cursor.h => archive/ide-cursor-private.h} | 0
src/libide/sourceview/{ => archive}/ide-cursor.c | 29 +-
src/libide/sourceview/{ => archive}/ide-gutter.c | 0
src/libide/sourceview/{ => archive}/ide-gutter.h | 0
src/libide/sourceview/archive/ide-source-view.c | 7749 ++++++++++++++++++++
src/libide/sourceview/archive/ide-source-view.h | 180 +
src/libide/sourceview/{gtk => archive}/menus.ui | 0
src/libide/sourceview/ide-source-view.c | 7720 +------------------
src/libide/sourceview/ide-source-view.h | 151 +-
.../sourceview/libide-sourceview.gresource.xml | 1 -
src/libide/sourceview/libide-sourceview.h | 24 +-
src/libide/sourceview/meson.build | 9 +-
12 files changed, 7987 insertions(+), 7876 deletions(-)
---
diff --git a/src/libide/sourceview/ide-cursor.h b/src/libide/sourceview/archive/ide-cursor-private.h
similarity index 100%
rename from src/libide/sourceview/ide-cursor.h
rename to src/libide/sourceview/archive/ide-cursor-private.h
diff --git a/src/libide/sourceview/ide-cursor.c b/src/libide/sourceview/archive/ide-cursor.c
similarity index 97%
rename from src/libide/sourceview/ide-cursor.c
rename to src/libide/sourceview/archive/ide-cursor.c
index ecf1ac772..37ddd4fed 100644
--- a/src/libide/sourceview/ide-cursor.c
+++ b/src/libide/sourceview/archive/ide-cursor.c
@@ -22,10 +22,10 @@
#include "config.h"
-#include <dazzle.h>
+#include <libide-core.h>
#include "ide-source-view.h"
-#include "ide-cursor.h"
+#include "ide-cursor-private.h"
#include "ide-text-util.h"
struct _IdeCursor
@@ -39,7 +39,7 @@ struct _IdeCursor
GtkTextTag *highlight_tag;
- DzlSignalGroup *operations_signals;
+ IdeSignalGroup *operations_signals;
guint overwrite : 1;
};
@@ -91,7 +91,7 @@ ide_cursor_dispose (GObject *object)
if (self->operations_signals != NULL)
{
- dzl_signal_group_set_target (self->operations_signals, NULL);
+ ide_signal_group_set_target (self->operations_signals, NULL);
g_clear_object (&self->operations_signals);
}
@@ -378,7 +378,8 @@ ide_cursor_add_cursor_by_match (IdeCursor *self)
gtk_text_buffer_select_range (buffer, &match_begin, &match_end);
- ide_source_view_scroll_mark_onscreen (self->source_view, vc->insert, TRUE, 0.5, 0.5);
+ gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (self->source_view),
+ vc->insert, TRUE, 0.5, 0.5);
}
void
@@ -745,7 +746,7 @@ ide_cursor_constructed (GObject *object)
self->overwrite = gtk_text_view_get_overwrite (text_view);
- dzl_signal_group_set_target (self->operations_signals, self->source_view);
+ ide_signal_group_set_target (self->operations_signals, self->source_view);
}
static void
@@ -813,39 +814,39 @@ ide_cursor_init (IdeCursor *self)
"underline", PANGO_UNDERLINE_SINGLE,
NULL);
- self->operations_signals = dzl_signal_group_new (IDE_TYPE_SOURCE_VIEW);
+ self->operations_signals = ide_signal_group_new (IDE_TYPE_SOURCE_VIEW);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"move-cursor",
G_CALLBACK (ide_cursor_move_cursor),
self,
G_CONNECT_AFTER);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"delete-from-cursor",
G_CALLBACK (ide_cursor_delete_from_cursor),
self,
G_CONNECT_AFTER);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"backspace",
G_CALLBACK (ide_cursor_backspace),
self,
G_CONNECT_AFTER);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"toggle-overwrite",
G_CALLBACK (ide_cursor_toggle_overwrite),
self,
G_CONNECT_AFTER);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"movement",
G_CALLBACK (ide_cursor_movement),
self,
G_CONNECT_AFTER);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"select-inner",
G_CALLBACK (ide_cursor_select_inner),
self,
G_CONNECT_AFTER);
- dzl_signal_group_connect_object (self->operations_signals,
+ ide_signal_group_connect_object (self->operations_signals,
"delete-selection",
G_CALLBACK (ide_cursor_delete_selection),
self,
diff --git a/src/libide/sourceview/ide-gutter.c b/src/libide/sourceview/archive/ide-gutter.c
similarity index 100%
rename from src/libide/sourceview/ide-gutter.c
rename to src/libide/sourceview/archive/ide-gutter.c
diff --git a/src/libide/sourceview/ide-gutter.h b/src/libide/sourceview/archive/ide-gutter.h
similarity index 100%
rename from src/libide/sourceview/ide-gutter.h
rename to src/libide/sourceview/archive/ide-gutter.h
diff --git a/src/libide/sourceview/archive/ide-source-view.c b/src/libide/sourceview/archive/ide-source-view.c
new file mode 100644
index 000000000..4edbd923e
--- /dev/null
+++ b/src/libide/sourceview/archive/ide-source-view.c
@@ -0,0 +1,7749 @@
+/* ide-source-view.c
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-source-view"
+
+#include "config.h"
+
+#include <cairo-gobject.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libide-code.h>
+#include <libide-plugins.h>
+#include <libide-threading.h>
+
+#include "ide-buffer-private.h"
+
+#include "ide-cursor.h"
+#include "ide-source-view.h"
+#include "ide-source-view-enums.h"
+#include "ide-text-util.h"
+
+#define INCLUDE_STATEMENTS "^#include[\\s]+[\\\"\\<][^\\s\\\"\\\'\\<\\>[:cntrl:]]+[\\\"\\>]"
+
+#define DEFAULT_FONT_DESC "Monospace 11"
+#define ANIMATION_X_GROW 50
+#define ANIMATION_Y_GROW 30
+#define SMALL_SCROLL_DURATION_MSEC 100
+#define LARGE_SCROLL_DURATION_MSEC 250
+#define FIXIT_LABEL_LEN_MAX 30
+#define SCROLL_REPLAY_DELAY 1000
+#define DEFAULT_OVERSCROLL_NUM_LINES 1
+#define TAG_DEFINITION "action::hover-definition"
+#define DEFINITION_HIGHLIGHT_MODIFIER GDK_CONTROL_MASK
+
+#define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)
+
+#define _GDK_RECTANGLE_X2(r) ((r)->x + (r)->width)
+#define _GDK_RECTANGLE_Y2(r) ((r)->y + (r)->height)
+#define _GDK_RECTANGLE_CONTAINS(rect,other) rectangle_contains_rectangle(rect,other)
+#define TRACE_RECTANGLE(name, rect) \
+ IDE_TRACE_MSG ("%s = Rectangle(x=%d, y=%d, width=%d, height=%d)", \
+ name, (rect)->x, (rect)->y, (rect)->width, (rect)->height)
+
+#define SCROLL_X(align) \
+ (((align) == IDE_SOURCE_SCROLL_BOTH) || ((align) == IDE_SOURCE_SCROLL_X))
+#define SCROLL_Y(align) \
+ (((align) == IDE_SOURCE_SCROLL_BOTH) || ((align) == IDE_SOURCE_SCROLL_Y))
+
+typedef struct
+{
+ IdeBuffer *buffer;
+ GtkCssProvider *css_provider;
+ PangoFontDescription *font_desc;
+ IdeExtensionAdapter *indenter_adapter;
+ IdeSourceViewCapture *capture;
+ gchar *display_name;
+ IdeSourceViewMode *mode;
+ GtkTextMark *scroll_mark;
+ GQueue *selections;
+ GQueue *snippets;
+ DzlAnimation *hadj_animation;
+ DzlAnimation *vadj_animation;
+ IdeGutter *gutter;
+
+ IdeCompletion *completion;
+ IdeHover *hover;
+
+ IdeBindingGroup *file_setting_bindings;
+ IdeSignalGroup *buffer_signals;
+
+ guint change_sequence;
+
+ guint target_line_column;
+ GString *command_str;
+ gunichar command;
+ gunichar modifier;
+ gunichar search_char;
+ gint count;
+ gunichar inner_left;
+ gunichar inner_right;
+
+ guint scroll_offset;
+ gint cached_char_height;
+ gint cached_char_width;
+
+ guint saved_line;
+ guint saved_line_column;
+ guint saved_selection_line;
+ guint saved_selection_line_column;
+
+ GdkRGBA snippet_area_background_rgba;
+
+ guint font_scale;
+
+ gint overscroll_num_lines;
+
+ guint delay_size_allocate_chainup;
+ GtkAllocation delay_size_allocation;
+
+ IdeLocation *definition_src_location;
+ GtkTextMark *definition_highlight_start_mark;
+ GtkTextMark *definition_highlight_end_mark;
+
+ GRegex *include_regex;
+
+ IdeCursor *cursor;
+
+ guint in_key_press;
+
+ guint auto_indent : 1;
+ guint completion_blocked : 1;
+ guint did_ctrl_opacity : 1;
+ guint highlight_current_line : 1;
+ guint in_replay_macro : 1;
+ guint insert_mark_cleared : 1;
+ guint insert_matching_brace : 1;
+ guint interactive_completion : 1;
+ guint overwrite_braces : 1;
+ guint recording_macro : 1;
+ guint scrolling_to_scroll_mark : 1;
+ guint show_grid_lines : 1;
+ guint snippet_completion : 1;
+ guint waiting_for_capture : 1;
+ guint waiting_for_symbol : 1;
+ guint show_line_changes : 1;
+ guint show_line_diagnostics : 1;
+ guint show_line_numbers : 1;
+ guint show_relative_line_numbers : 1;
+} IdeSourceViewPrivate;
+
+typedef struct
+{
+ IdeSourceView *self;
+ GtkTextMark *word_start_mark;
+ GtkTextMark *word_end_mark;
+} DefinitionHighlightData;
+
+typedef struct
+{
+ GPtrArray *resolvers;
+ IdeLocation *location;
+} FindReferencesTaskData;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeSourceView, ide_source_view, GTK_SOURCE_TYPE_VIEW)
+
+enum {
+ PROP_0,
+ PROP_COMPLETION_N_ROWS,
+ PROP_COUNT,
+ PROP_FILE_SETTINGS,
+ PROP_FONT_NAME,
+ PROP_FONT_DESC,
+ PROP_INDENTER,
+ PROP_INDENT_STYLE,
+ PROP_INSERT_MATCHING_BRACE,
+ PROP_INTERACTIVE_COMPLETION,
+ PROP_MODE_DISPLAY_NAME,
+ PROP_OVERWRITE_BRACES,
+ PROP_SCROLL_OFFSET,
+ PROP_SHOW_GRID_LINES,
+ PROP_SHOW_LINE_CHANGES,
+ PROP_SHOW_LINE_DIAGNOSTICS,
+ PROP_SHOW_RELATIVE_LINE_NUMBERS,
+ PROP_OVERSCROLL,
+ LAST_PROP,
+
+ /* These are overridden */
+ PROP_AUTO_INDENT,
+ PROP_HIGHLIGHT_CURRENT_LINE,
+ PROP_OVERWRITE,
+ PROP_SHOW_LINE_NUMBERS,
+};
+
+enum {
+ ACTION,
+ ADD_CURSOR,
+ APPEND_TO_COUNT,
+ AUTO_INDENT,
+ BEGIN_MACRO,
+ BEGIN_RENAME,
+ BEGIN_USER_ACTION,
+ CAPTURE_MODIFIER,
+ CLEAR_COUNT,
+ CLEAR_MODIFIER,
+ CLEAR_SEARCH,
+ CLEAR_SELECTION,
+ CLEAR_SNIPPETS,
+ COPY_CLIPBOARD_EXTENDED,
+ CYCLE_COMPLETION,
+ DECREASE_FONT_SIZE,
+ DELETE_SELECTION,
+ DRAW_BUBBLES,
+ DUPLICATE_ENTIRE_LINE,
+ END_MACRO,
+ END_USER_ACTION,
+ FOCUS_LOCATION,
+ FORMAT_SELECTION,
+ QUERY_CODE_ACTION,
+ FIND_REFERENCES,
+ GOTO_DEFINITION,
+ HIDE_COMPLETION,
+ INCREASE_FONT_SIZE,
+ INDENT_SELECTION,
+ INSERT_AT_CURSOR_AND_INDENT,
+ INSERT_MODIFIER,
+ JUMP,
+ MOVEMENT,
+ MOVE_ERROR,
+ MOVE_SEARCH,
+ PASTE_CLIPBOARD_EXTENDED,
+ POP_SELECTION,
+ POP_SNIPPET,
+ PUSH_SELECTION,
+ PUSH_SNIPPET,
+ REBUILD_HIGHLIGHT,
+ REINDENT,
+ REMOVE_CURSORS,
+ REPLAY_MACRO,
+ REQUEST_DOCUMENTATION,
+ RESET,
+ RESET_FONT_SIZE,
+ RESTORE_INSERT_MARK,
+ SAVE_COMMAND,
+ SAVE_INSERT_MARK,
+ SAVE_SEARCH_CHAR,
+ SELECT_INNER,
+ SELECT_TAG,
+ SELECTION_THEATRIC,
+ SET_MODE,
+ SET_OVERWRITE,
+ SET_SEARCH_TEXT,
+ SORT,
+ SWAP_SELECTION_BOUNDS,
+ LAST_SIGNAL
+};
+
+enum {
+ FONT_SCALE_XX_SMALL,
+ FONT_SCALE_X_SMALL,
+ FONT_SCALE_SMALL,
+ FONT_SCALE_NORMAL,
+ FONT_SCALE_LARGE,
+ FONT_SCALE_X_LARGE,
+ FONT_SCALE_XX_LARGE,
+ FONT_SCALE_XXX_LARGE,
+ LAST_FONT_SCALE
+};
+
+static GParamSpec *properties [LAST_PROP];
+static guint signals [LAST_SIGNAL];
+static gdouble fontScale [LAST_FONT_SCALE] = {
+ 0.57870, 0.69444, 0.83333,
+ 1.0,
+ 1.2, 1.44, 1.728, 2.48832,
+};
+
+static gboolean ide_source_view_do_size_allocate_hack_cb (gpointer data);
+static void ide_source_view_real_save_insert_mark (IdeSourceView *self);
+static void ide_source_view_real_restore_insert_mark (IdeSourceView *self);
+static void ide_source_view_real_set_mode (IdeSourceView *self,
+ const gchar *name,
+ IdeSourceViewModeType type);
+static void ide_source_view_save_column (IdeSourceView *self);
+static void ide_source_view_maybe_overwrite (IdeSourceView *self,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len);
+
+static inline cairo_bool_t
+rectangle_contains_rectangle (const cairo_rectangle_int_t *a,
+ const cairo_rectangle_int_t *b)
+{
+ return (a->x <= b->x &&
+ a->x + (int) a->width >= b->x + (int) b->width &&
+ a->y <= b->y &&
+ a->y + (int) a->height >= b->y + (int) b->height);
+}
+
+static gpointer
+get_selection_owner (IdeSourceView *self)
+{
+ return g_object_get_data (G_OBJECT (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+ "IDE_SOURCE_VIEW_SELECTION_OWNER");
+}
+
+static void
+set_selection_owner (IdeSourceView *self,
+ gpointer tag)
+{
+ g_object_set_data (G_OBJECT (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+ "IDE_SOURCE_VIEW_SELECTION_OWNER", tag);
+}
+
+static void
+find_references_task_data_free (FindReferencesTaskData *data)
+{
+ g_clear_pointer (&data->resolvers, g_ptr_array_unref);
+ g_clear_object (&data->location);
+ g_slice_free (FindReferencesTaskData, data);
+}
+
+static void
+block_interactive (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->completion)
+ ide_completion_block_interactive (priv->completion);
+}
+
+static void
+unblock_interactive (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->completion != NULL)
+ ide_completion_unblock_interactive (priv->completion);
+}
+
+static void
+ide_source_view_set_interactive_completion (IdeSourceView *self,
+ gboolean interactive_completion)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ interactive_completion = !!interactive_completion;
+
+ if (interactive_completion != priv->interactive_completion)
+ {
+ priv->interactive_completion = interactive_completion;
+
+ if (interactive_completion)
+ unblock_interactive (self);
+ else
+ block_interactive (self);
+ }
+}
+
+static void
+definition_highlight_data_free (DefinitionHighlightData *data)
+{
+ if (data != NULL)
+ {
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_mark_get_buffer (data->word_start_mark);
+
+ gtk_text_buffer_delete_mark (buffer, data->word_start_mark);
+ gtk_text_buffer_delete_mark (buffer, data->word_end_mark);
+
+ g_clear_object (&data->self);
+ g_clear_object (&data->word_start_mark);
+ g_clear_object (&data->word_end_mark);
+
+ g_slice_free (DefinitionHighlightData, data);
+ }
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (DefinitionHighlightData, definition_highlight_data_free)
+
+static gboolean
+ide_source_view_can_animate (IdeSourceView *self)
+{
+ GtkSettings *settings;
+ GdkScreen *screen;
+ gboolean can_animate = FALSE;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (self));
+ settings = gtk_settings_get_for_screen (screen);
+
+ g_object_get (settings, "gtk-enable-animations", &can_animate, NULL);
+
+ return can_animate;
+}
+
+void
+_ide_source_view_set_count (IdeSourceView *self,
+ gint count)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->count = count;
+}
+
+void
+_ide_source_view_set_modifier (IdeSourceView *self,
+ gunichar modifier)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->modifier = modifier;
+
+ if (priv->recording_macro && !priv->in_replay_macro)
+ ide_source_view_capture_record_modifier (priv->capture, modifier);
+}
+
+static IdeIndenter *
+ide_source_view_get_indenter (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->indenter_adapter != NULL)
+ return ide_extension_adapter_get_extension (priv->indenter_adapter);
+
+ return NULL;
+}
+
+static void
+ide_source_view_block_handlers (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_signal_group_block (priv->buffer_signals);
+}
+
+static void
+ide_source_view_unblock_handlers (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_signal_group_unblock (priv->buffer_signals);
+}
+
+static void
+get_rect_for_iters (GtkTextView *text_view,
+ const GtkTextIter *iter1,
+ const GtkTextIter *iter2,
+ GdkRectangle *rect,
+ GtkTextWindowType window_type)
+{
+ GdkRectangle area;
+ GdkRectangle tmp;
+ GtkTextIter begin;
+ GtkTextIter end;
+ GtkTextIter iter;
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (iter1 != NULL);
+ g_assert (iter2 != NULL);
+ g_assert (rect != NULL);
+ g_assert (gtk_text_iter_get_buffer (iter1) == gtk_text_iter_get_buffer (iter2));
+ g_assert (gtk_text_view_get_buffer (text_view) == gtk_text_iter_get_buffer (iter1));
+
+ begin = *iter1;
+ end = *iter2;
+
+ if (gtk_text_iter_equal (&begin, &end))
+ {
+ gtk_text_view_get_iter_location (text_view, &begin, &area);
+ goto finish;
+ }
+
+ gtk_text_iter_order (&begin, &end);
+
+ if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&end))
+ {
+ gtk_text_view_get_iter_location (text_view, &begin, &area);
+ gtk_text_view_get_iter_location (text_view, &end, &tmp);
+ gdk_rectangle_union (&area, &tmp, &area);
+ goto finish;
+ }
+
+ gtk_text_view_get_iter_location (text_view, &begin, &area);
+
+ iter = begin;
+
+ do
+ {
+ /* skip trailing newline */
+ if ((gtk_text_iter_starts_line (&iter) && gtk_text_iter_equal (&iter, &end)))
+ break;
+
+ gtk_text_view_get_iter_location (text_view, &iter, &tmp);
+ gdk_rectangle_union (&area, &tmp, &area);
+
+ gtk_text_iter_forward_to_line_end (&iter);
+ gtk_text_view_get_iter_location (text_view, &iter, &tmp);
+ gdk_rectangle_union (&area, &tmp, &area);
+
+ if (!gtk_text_iter_forward_char (&iter))
+ break;
+ }
+ while (gtk_text_iter_compare (&iter, &end) <= 0);
+
+finish:
+ gtk_text_view_buffer_to_window_coords (text_view, window_type, area.x, area.y, &area.x, &area.y);
+
+ *rect = area;
+}
+
+static void
+animate_expand (IdeSourceView *self,
+ const GtkTextIter *begin,
+ const GtkTextIter *end)
+{
+ DzlBoxTheatric *theatric;
+ GtkAllocation alloc;
+ GdkRectangle rect = { 0 };
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (begin != NULL);
+ g_assert (end != NULL);
+
+ get_rect_for_iters (GTK_TEXT_VIEW (self), begin, end, &rect, GTK_TEXT_WINDOW_WIDGET);
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+ rect.height = MIN (rect.height, alloc.height - rect.y);
+
+ theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
+ "alpha", 0.3,
+ "background", "#729fcf",
+ "height", rect.height,
+ "target", self,
+ "width", rect.width,
+ "x", rect.x,
+ "y", rect.y,
+ NULL);
+
+ dzl_object_animate_full (theatric,
+ DZL_ANIMATION_EASE_IN_CUBIC,
+ 250,
+ gtk_widget_get_frame_clock (GTK_WIDGET (self)),
+ g_object_unref,
+ theatric,
+ "x", rect.x - ANIMATION_X_GROW,
+ "width", rect.width + (ANIMATION_X_GROW * 2),
+ "y", rect.y - ANIMATION_Y_GROW,
+ "height", rect.height + (ANIMATION_Y_GROW * 2),
+ "alpha", 0.0,
+ NULL);
+}
+
+static void
+animate_shrink (IdeSourceView *self,
+ const GtkTextIter *begin,
+ const GtkTextIter *end)
+{
+ DzlBoxTheatric *theatric;
+ GtkAllocation alloc;
+ GdkRectangle rect = { 0 };
+ GdkRectangle char_rect = { 0 };
+ GtkTextIter copy_begin;
+ GtkTextIter copy_end;
+ gboolean is_whole_line;
+ gboolean is_single_line;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (begin);
+ g_assert (end);
+
+ get_rect_for_iters (GTK_TEXT_VIEW (self), begin, begin, &char_rect, GTK_TEXT_WINDOW_WIDGET);
+ get_rect_for_iters (GTK_TEXT_VIEW (self), begin, end, &rect, GTK_TEXT_WINDOW_WIDGET);
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+ rect.height = MIN (rect.height, alloc.height - rect.y);
+
+ copy_begin = *begin;
+ copy_end = *end;
+
+ gtk_text_iter_order (©_begin, ©_end);
+
+ is_single_line = (gtk_text_iter_get_line (©_begin) == gtk_text_iter_get_line (©_end));
+ is_whole_line = ((gtk_text_iter_get_line (©_begin) + 1 ==
+ gtk_text_iter_get_line (©_end)) &&
+ (gtk_text_iter_starts_line (©_begin) &&
+ gtk_text_iter_starts_line (©_end)));
+
+ theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
+ "alpha", 0.3,
+ "background", "#729fcf",
+ "height", rect.height,
+ "target", self,
+ "width", rect.width,
+ "x", rect.x,
+ "y", rect.y,
+ NULL);
+
+ if (is_whole_line)
+ dzl_object_animate_full (theatric,
+ DZL_ANIMATION_EASE_OUT_QUAD,
+ 150,
+ gtk_widget_get_frame_clock (GTK_WIDGET (self)),
+ g_object_unref,
+ theatric,
+ "x", rect.x,
+ "width", rect.width,
+ "y", rect.y,
+ "height", 0,
+ "alpha", 0.3,
+ NULL);
+ else if (is_single_line)
+ dzl_object_animate_full (theatric,
+ DZL_ANIMATION_EASE_OUT_QUAD,
+ 150,
+ gtk_widget_get_frame_clock (GTK_WIDGET (self)),
+ g_object_unref,
+ theatric,
+ "x", rect.x,
+ "width", 0,
+ "y", rect.y,
+ "height", rect.height,
+ "alpha", 0.3,
+ NULL);
+ else
+ dzl_object_animate_full (theatric,
+ DZL_ANIMATION_EASE_OUT_QUAD,
+ 150,
+ gtk_widget_get_frame_clock (GTK_WIDGET (self)),
+ g_object_unref,
+ theatric,
+ "x", rect.x,
+ "width", 0,
+ "y", rect.y,
+ "height", char_rect.height,
+ "alpha", 0.3,
+ NULL);
+}
+
+void
+ide_source_view_scroll_to_insert (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextMark *mark;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ _ide_buffer_cancel_cursor_restore (IDE_BUFFER (buffer));
+ mark = gtk_text_buffer_get_insert (buffer);
+ ide_source_view_scroll_mark_onscreen (self, mark, TRUE, 0.5, 1.0);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_invalidate_window (IdeSourceView *self)
+{
+ GdkWindow *window;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if ((window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_WIDGET)))
+ {
+ gdk_window_invalidate_rect (window, NULL, TRUE);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static gchar *
+text_iter_get_line_prefix (const GtkTextIter *iter)
+{
+ GtkTextIter begin;
+ GString *str;
+
+ g_assert (iter);
+
+ gtk_text_iter_assign (&begin, iter);
+ gtk_text_iter_set_line_offset (&begin, 0);
+
+ str = g_string_new (NULL);
+
+ if (gtk_text_iter_compare (&begin, iter) != 0)
+ {
+ do
+ {
+ gunichar c;
+
+ c = gtk_text_iter_get_char (&begin);
+
+ switch (c)
+ {
+ case '\t':
+ case ' ':
+ g_string_append_unichar (str, c);
+ break;
+ default:
+ g_string_append_c (str, ' ');
+ break;
+ }
+ }
+ while (gtk_text_iter_forward_char (&begin) &&
+ (gtk_text_iter_compare (&begin, iter) < 0));
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+ide_source_view_update_auto_indent_override (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkSourceLanguage *language;
+ const gchar *lang_id = NULL;
+ IdeIndenter *indenter;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /* Update the indenter if necessary */
+ if (priv->auto_indent &&
+ priv->indenter_adapter != NULL &&
+ priv->buffer != NULL &&
+ NULL != (language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (priv->buffer))))
+ lang_id = gtk_source_language_get_id (language);
+
+ if (priv->indenter_adapter != NULL)
+ ide_extension_adapter_set_value (priv->indenter_adapter, lang_id);
+
+ /* Fetch our indenter */
+ indenter = ide_source_view_get_indenter (self);
+
+ /*
+ * Updates our override of auto-indent from the GtkSourceView underneath us.
+ * Since we do our own mimicing of GtkSourceView, we always disable it. Also
+ * updates our mode which needs to know if we have an indenter to provide
+ * different CSS selectors.
+ */
+ gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (self), FALSE);
+ if (priv->mode != NULL)
+ ide_source_view_mode_set_has_indenter (priv->mode, !!indenter);
+}
+
+static void
+ide_source_view_set_file_settings (IdeSourceView *self,
+ IdeFileSettings *file_settings)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (!file_settings || IDE_IS_FILE_SETTINGS (file_settings));
+
+ if (file_settings != ide_source_view_get_file_settings (self))
+ {
+ ide_binding_group_set_source (priv->file_setting_bindings, file_settings);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILE_SETTINGS]);
+ }
+}
+
+static void
+ide_source_view__buffer_notify_file_settings_cb (IdeSourceView *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ ide_source_view_set_file_settings (self, ide_buffer_get_file_settings (buffer));
+}
+
+static void
+ide_source_view__buffer_notify_language_cb (IdeSourceView *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ const gchar *lang_id;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ lang_id = ide_buffer_get_language_id (buffer);
+
+ /* Update the indenter, which is provided by a plugin. */
+ if (priv->indenter_adapter != NULL)
+ ide_extension_adapter_set_value (priv->indenter_adapter, lang_id);
+ ide_source_view_update_auto_indent_override (self);
+
+ /* Reload hover providers by language */
+ _ide_hover_set_language (priv->hover, lang_id);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INDENTER]);
+}
+
+static void
+ide_source_view__buffer_notify_style_scheme_cb (IdeSourceView *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkSourceStyleScheme *scheme = NULL;
+ GtkSourceStyle *snippet_area_style = NULL;
+ g_autofree gchar *snippet_background = NULL;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer));
+ if (scheme != NULL)
+ snippet_area_style = gtk_source_style_scheme_get_style (scheme, "snippet::area");
+
+ if (snippet_area_style != NULL)
+ g_object_get (snippet_area_style, "background", &snippet_background, NULL);
+
+ if (snippet_background == NULL ||
+ !gdk_rgba_parse (&priv->snippet_area_background_rgba, snippet_background))
+ {
+ gdk_rgba_parse (&priv->snippet_area_background_rgba, "#204a87");
+ priv->snippet_area_background_rgba.alpha = 0.1;
+ }
+}
+
+static void
+ide_source_view__buffer_request_scroll_to_insert_cb (IdeSourceView *self,
+ IdeBuffer *buffer)
+{
+ GtkTextMark *mark;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+ gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (self), mark);
+}
+
+static void
+ide_source_view__buffer_changed_cb (IdeSourceView *self,
+ IdeBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ priv->change_sequence++;
+}
+
+static void
+ide_source_view_rebuild_css (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ if (!priv->css_provider)
+ {
+ GtkStyleContext *style_context;
+
+ priv->css_provider = gtk_css_provider_new ();
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_add_provider (style_context,
+ GTK_STYLE_PROVIDER (priv->css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+
+ if (priv->font_desc)
+ {
+ g_autofree gchar *str = NULL;
+ g_autofree gchar *css = NULL;
+ const PangoFontDescription *font_desc = priv->font_desc;
+ PangoFontDescription *copy = NULL;
+
+ if (priv->font_scale != FONT_SCALE_NORMAL)
+ font_desc = copy = ide_source_view_get_scaled_font_desc (self);
+
+ str = dzl_pango_font_description_to_css (font_desc);
+ css = g_strdup_printf ("textview { %s }", str ?: "");
+ gtk_css_provider_load_from_data (priv->css_provider, css, -1, NULL);
+
+ if (priv->gutter != NULL)
+ ide_gutter_style_changed (priv->gutter);
+
+ if (priv->completion != NULL)
+ _ide_completion_set_font_description (priv->completion, font_desc);
+
+ g_clear_pointer (©, pango_font_description_free);
+ }
+}
+
+static void
+ide_source_view_invalidate_range_mark (IdeSourceView *self,
+ GtkTextMark *mark_begin,
+ GtkTextMark *mark_end)
+{
+ GtkTextBuffer *buffer;
+ GdkRectangle rect;
+ GtkTextIter begin;
+ GtkTextIter end;
+ GdkWindow *window;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_MARK (mark_begin));
+ g_assert (GTK_IS_TEXT_MARK (mark_end));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_iter_at_mark (buffer, &begin, mark_begin);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);
+
+ get_rect_for_iters (GTK_TEXT_VIEW (self), &begin, &end, &rect, GTK_TEXT_WINDOW_TEXT);
+ window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_TEXT);
+ gdk_window_invalidate_rect (window, &rect, FALSE);
+}
+
+static void
+ide_source_view__buffer_insert_text_cb (IdeSourceView *self,
+ GtkTextIter *iter,
+ gchar *text,
+ gint len,
+ GtkTextBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSnippet *snippet;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (iter != NULL);
+ g_assert (text != NULL);
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
+ return;
+
+ gtk_text_buffer_begin_user_action (buffer);
+
+ if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
+ {
+ ide_source_view_block_handlers (self);
+ ide_snippet_before_insert_text (snippet, buffer, iter, text, len);
+ ide_source_view_unblock_handlers (self);
+ }
+}
+
+static void
+ide_source_view__buffer_insert_text_after_cb (IdeSourceView *self,
+ GtkTextIter *iter,
+ gchar *text,
+ gint len,
+ GtkTextBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSnippet *snippet;
+ GtkTextIter insert;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (iter != NULL);
+ g_assert (text != NULL);
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
+ return;
+
+ if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
+ {
+ GtkTextMark *begin;
+ GtkTextMark *end;
+
+ ide_source_view_block_handlers (self);
+ ide_snippet_after_insert_text (snippet, buffer, iter, text, len);
+ ide_source_view_unblock_handlers (self);
+
+ begin = ide_snippet_get_mark_begin (snippet);
+ end = ide_snippet_get_mark_end (snippet);
+ ide_source_view_invalidate_range_mark (self, begin, end);
+ }
+
+ if (priv->in_key_press)
+ {
+ /*
+ * If we are handling the key-press-event, we might have just inserted
+ * a character that indicates we should overwrite the next character.
+ * However, due to GtkIMContext constraints, we need to allow it to be
+ * inserted and then handle it here.
+ */
+ ide_source_view_maybe_overwrite (self, iter, text, len);
+ }
+
+ /* Ignore multiple cursors unless we have focus */
+ if (gtk_widget_has_focus (GTK_WIDGET (self)))
+ {
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert(buffer));
+
+ if (gtk_text_iter_equal (iter, &insert))
+ {
+ ide_source_view_block_handlers (self);
+ ide_cursor_insert_text (priv->cursor, text, len);
+ ide_source_view_unblock_handlers (self);
+ gtk_text_buffer_get_iter_at_mark (buffer, iter, gtk_text_buffer_get_insert (buffer));
+ }
+ }
+
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+static void
+ide_source_view__buffer_delete_range_cb (IdeSourceView *self,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ GtkTextBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSnippet *snippet;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+ if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
+ {
+ GtkTextMark *begin_mark;
+ GtkTextMark *end_mark;
+
+ ide_source_view_block_handlers (self);
+ ide_snippet_before_delete_range (snippet, buffer, begin, end);
+ ide_source_view_unblock_handlers (self);
+
+ begin_mark = ide_snippet_get_mark_begin (snippet);
+ end_mark = ide_snippet_get_mark_end (snippet);
+ ide_source_view_invalidate_range_mark (self, begin_mark, end_mark);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view__buffer_delete_range_after_cb (IdeSourceView *self,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ GtkTextBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSnippet *snippet;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+ ide_source_view_block_handlers (self);
+
+ if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
+ ide_snippet_after_delete_range (snippet, buffer, begin, end);
+
+ ide_source_view_unblock_handlers (self);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view__buffer_mark_set_cb (IdeSourceView *self,
+ GtkTextIter *iter,
+ GtkTextMark *mark,
+ GtkTextBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSnippet *snippet;
+ GtkTextMark *insert;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (iter != NULL);
+ g_assert (GTK_IS_TEXT_MARK (mark));
+ g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+ insert = gtk_text_buffer_get_insert (buffer);
+
+ if (mark == insert)
+ {
+ ide_source_view_block_handlers (self);
+ while (NULL != (snippet = g_queue_peek_head (priv->snippets)) &&
+ !ide_snippet_insert_set (snippet, mark))
+ ide_source_view_pop_snippet (self);
+ ide_source_view_unblock_handlers (self);
+ }
+
+#ifdef IDE_ENABLE_TRACE
+ if (mark == insert || mark == gtk_text_buffer_get_selection_bound (buffer))
+ {
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
+ {
+ gtk_text_iter_order (&begin, &end);
+ IDE_TRACE_MSG ("Selection is now %d:%d to %d:%d",
+ gtk_text_iter_get_line (&begin),
+ gtk_text_iter_get_line_offset (&begin),
+ gtk_text_iter_get_line (&end),
+ gtk_text_iter_get_line_offset (&end));
+ }
+ }
+#endif
+}
+
+static void
+ide_source_view__buffer_notify_has_selection_cb (IdeSourceView *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gboolean has_selection;
+
+ has_selection = gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer));
+ ide_source_view_mode_set_has_selection (priv->mode, has_selection);
+
+ if (has_selection)
+ set_selection_owner (self, G_OBJECT (self));
+ else if (get_selection_owner (self) == G_OBJECT (self))
+ set_selection_owner (self, NULL);
+}
+
+static void
+ide_source_view__buffer_line_flags_changed_cb (IdeSourceView *self,
+ IdeBuffer *buffer)
+{
+ GtkSourceGutter *gutter;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self), GTK_TEXT_WINDOW_LEFT);
+ gtk_source_gutter_queue_draw (gutter);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view__buffer_loaded_cb (IdeSourceView *self,
+ IdeBuffer *buffer)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextMark *insert;
+ GtkTextIter iter;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ if (priv->completion_blocked)
+ {
+ unblock_interactive (self);
+ priv->completion_blocked = FALSE;
+ }
+
+ /* Store the line column (visual offset) so movements are correct. */
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, insert);
+ priv->target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self), &iter);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_set_cursor_from_name (IdeSourceView *self,
+ const gchar *cursor_name)
+{
+ GdkDisplay *display;
+ GdkCursor *cursor;
+ GdkWindow *window = gtk_text_view_get_window (GTK_TEXT_VIEW (self),
+ GTK_TEXT_WINDOW_TEXT);
+
+ if (!window)
+ return;
+
+ display = gdk_window_get_display (window);
+ cursor = gdk_cursor_new_from_name (display, cursor_name);
+
+ gdk_window_set_cursor (window, cursor);
+}
+
+static void
+ide_source_view_reset_definition_highlight (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->definition_src_location)
+ g_clear_object (&priv->definition_src_location);
+
+ if (priv->buffer != NULL)
+ {
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (priv->buffer), &begin, &end);
+ gtk_text_buffer_remove_tag_by_name (GTK_TEXT_BUFFER (priv->buffer), TAG_DEFINITION, &begin, &end);
+ }
+
+ ide_source_view_set_cursor_from_name (self, "text");
+}
+
+static void
+ide_source_view__buffer__notify_can_redo (IdeSourceView *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ GActionGroup *group;
+ gboolean can_redo;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ g_object_get (buffer,
+ "can-redo", &can_redo,
+ NULL);
+
+ group = gtk_widget_get_action_group (GTK_WIDGET (self), "sourceview");
+ dzl_widget_action_group_set_action_enabled (DZL_WIDGET_ACTION_GROUP (group), "redo", can_redo);
+}
+
+static void
+ide_source_view__buffer__notify_can_undo (IdeSourceView *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ GActionGroup *group;
+ gboolean can_undo;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ g_object_get (buffer,
+ "can-undo", &can_undo,
+ NULL);
+
+ group = gtk_widget_get_action_group (GTK_WIDGET (self), "sourceview");
+ dzl_widget_action_group_set_action_enabled (DZL_WIDGET_ACTION_GROUP (group), "undo", can_undo);
+}
+
+static void
+ide_source_view_bind_buffer (IdeSourceView *self,
+ IdeBuffer *buffer,
+ IdeSignalGroup *group)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autoptr(IdeContext) context = NULL;
+ GtkTextMark *insert;
+ IdeObjectBox *box;
+ GtkTextIter iter;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (DZL_IS_SIGNAL_GROUP (group));
+
+ priv->buffer = buffer;
+
+ ide_source_view_reset_definition_highlight (self);
+
+ ide_buffer_hold (buffer);
+
+ if (ide_buffer_get_loading (buffer))
+ {
+ block_interactive (self);
+ priv->completion_blocked = TRUE;
+ }
+
+ context = ide_buffer_ref_context (buffer);
+
+ _ide_hover_set_context (priv->hover, context);
+
+ box = ide_object_box_from_object (G_OBJECT (buffer));
+
+ priv->indenter_adapter = ide_extension_adapter_new (IDE_OBJECT (box),
+ peas_engine_get_default (),
+ IDE_TYPE_INDENTER,
+ "Indenter-Languages",
+ NULL);
+
+ priv->cursor = g_object_new (IDE_TYPE_CURSOR,
+ "ide-source-view", self,
+ NULL);
+
+ /* Create scroll mark used by movements and our scrolling helper */
+ gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
+ priv->scroll_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
+
+ /* Marks used for definition highlights */
+ priv->definition_highlight_start_mark =
+ gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
+ priv->definition_highlight_end_mark =
+ gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
+
+ g_object_ref (priv->definition_highlight_start_mark);
+ g_object_ref (priv->definition_highlight_end_mark);
+
+ ide_source_view__buffer_notify_language_cb (self, NULL, buffer);
+ ide_source_view__buffer_notify_file_settings_cb (self, NULL, buffer);
+ ide_source_view__buffer_notify_style_scheme_cb (self, NULL, buffer);
+ ide_source_view__buffer__notify_can_redo (self, NULL, buffer);
+ ide_source_view__buffer__notify_can_undo (self, NULL, buffer);
+ ide_source_view_real_set_mode (self, NULL, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
+
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+ ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_unbind_buffer (IdeSourceView *self,
+ IdeSignalGroup *group)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (DZL_IS_SIGNAL_GROUP (group));
+
+ if (priv->buffer == NULL)
+ IDE_EXIT;
+
+ priv->scroll_mark = NULL;
+
+ if (priv->completion_blocked)
+ {
+ unblock_interactive (self);
+ priv->completion_blocked = FALSE;
+ }
+
+ if (priv->cursor != NULL)
+ {
+ g_object_run_dispose (G_OBJECT (priv->cursor));
+ g_clear_object (&priv->cursor);
+ }
+
+ ide_clear_and_destroy_object (&priv->indenter_adapter);
+ g_clear_object (&priv->definition_highlight_start_mark);
+ g_clear_object (&priv->definition_highlight_end_mark);
+
+ ide_buffer_release (priv->buffer);
+
+ IDE_EXIT;
+}
+
+static gboolean
+is_opening_char (gunichar ch)
+{
+ switch (ch)
+ {
+ case '{':
+ case '(':
+ case '"':
+ case '\'':
+ case '[':
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static guint
+count_chars_on_line (IdeSourceView *view,
+ gunichar expected_char,
+ const GtkTextIter *iter)
+{
+ GtkTextIter cur;
+ guint count = 0;
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (view), 0);
+ g_return_val_if_fail (iter, 0);
+
+ cur = *iter;
+
+ gtk_text_iter_set_line_offset (&cur, 0);
+
+ while (!gtk_text_iter_ends_line (&cur))
+ {
+ gunichar ch;
+
+ ch = gtk_text_iter_get_char (&cur);
+
+ if (ch == '\\')
+ {
+ gtk_text_iter_forward_chars (&cur, 2);
+ continue;
+ }
+
+ count += (ch == expected_char);
+ gtk_text_iter_forward_char (&cur);
+ }
+
+ return count;
+}
+
+static gboolean
+is_xmlish (const gchar *lang_id)
+{
+ return (g_strcmp0 (lang_id, "xml") == 0) ||
+ (g_strcmp0 (lang_id, "html") == 0);
+
+}
+
+static void
+ide_source_view_maybe_overwrite (IdeSourceView *self,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextIter insert;
+ GtkTextIter next;
+ gunichar ch;
+ gunichar next_ch;
+ gunichar match;
+ guint count_open;
+ guint count_close;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (iter != NULL);
+ g_assert (text != NULL);
+ g_assert (len > 0);
+
+ /*
+ * Some auto-indenters will perform triggers on certain key-press that we
+ * would hijack by otherwise "doing nothing" during this key-press. So to
+ * avoid that, we actually delete the previous value and then allow this
+ * key-press event to continue.
+ */
+
+ if (!priv->overwrite_braces)
+ return;
+
+ /*
+ * WORKAROUND:
+ *
+ * If we are inside of a snippet, then let's not do anything. It really
+ * messes with the position tracking. Once we can better integrate these
+ * things, go ahead and remove this.
+ */
+ if (priv->snippets->length)
+ return;
+
+ /*
+ * Ignore this if it wasn't a single character insertion.
+ */
+ if (len != 1)
+ return;
+
+ /*
+ * Short circuit if there is already a selection.
+ */
+ buffer = gtk_text_iter_get_buffer (iter);
+ if (gtk_text_buffer_get_has_selection (buffer))
+ return;
+
+ /*
+ * @iter is pointing at the location we just inserted text. Since we
+ * know we only inserted one character, lets move past it and compare
+ * to see if we want to overwrite.
+ */
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert (buffer));
+ ch = g_utf8_get_char (text);
+ next_ch = gtk_text_iter_get_char (&insert);
+
+ switch (ch)
+ {
+ case ')': case ']': case '}': case '"': case '\'': case ';':
+ if (ch == next_ch)
+ {
+ if (ch == '"' || ch == '\'')
+ break;
+
+ switch (ch)
+ {
+ case ']': match = '['; break;
+ case '}': match = '{'; break;
+ case ')': match = '('; break;
+ case '>': match = '<'; break;
+ default: match = 0; break;
+ }
+
+ count_open = count_chars_on_line (self, match, iter);
+ count_close = count_chars_on_line (self, ch, iter);
+ if (count_close != count_open)
+ break;
+ }
+ /* fall through */
+ default:
+ return;
+ }
+
+ next = insert;
+
+ gtk_text_iter_forward_char (&next);
+ gtk_text_buffer_delete (buffer, &insert, &next);
+ *iter = insert;
+}
+
+static gboolean
+ide_source_view_maybe_insert_match (IdeSourceView *self,
+ GdkEventKey *event)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkSourceBuffer *sbuf;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ const gchar *lang_id;
+ GtkTextIter iter;
+ GtkTextIter prev_iter;
+ gunichar next_ch = 0;
+ gchar ch[2] = { 0 };
+
+ /*
+ * TODO: I think we should put this into a base class for auto
+ * indenters. It would make some things a lot more convenient, like
+ * changing which characters we won't add matching characters for.
+ */
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (event);
+
+ if (priv->cursor != NULL && ide_cursor_is_enabled (priv->cursor))
+ return FALSE;
+
+ /*
+ * If we are disabled, then do nothing.
+ */
+ if (!priv->insert_matching_brace)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ sbuf = GTK_SOURCE_BUFFER (buffer);
+
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ next_ch = gtk_text_iter_get_char (&iter);
+
+ prev_iter = iter;
+ gtk_text_iter_backward_chars (&prev_iter, 2);
+
+ /*
+ * If the source language has marked this region as a string or comment,
+ * then do nothing.
+ */
+ if (gtk_source_buffer_iter_has_context_class (sbuf, &prev_iter, "string") ||
+ gtk_source_buffer_iter_has_context_class (sbuf, &prev_iter, "comment"))
+ return FALSE;
+
+ switch (event->keyval)
+ {
+ case GDK_KEY_braceleft:
+ ch[0] = '}';
+ break;
+
+ case GDK_KEY_parenleft:
+ ch[0] = ')';
+ break;
+
+ case GDK_KEY_bracketleft:
+ ch[0] = ']';
+ break;
+
+ case GDK_KEY_quotedbl:
+ ch[0] = '"';
+ break;
+
+ case GDK_KEY_apostrophe:
+ ch[0] = '\'';
+ break;
+
+ case GDK_KEY_less:
+ if (!(lang_id = ide_buffer_get_language_id (IDE_BUFFER (buffer))) || !is_xmlish (lang_id))
+ return FALSE;
+ ch[0] = '>';
+ break;
+
+#if 0
+ /*
+ * TODO: We should avoid this when we are in comments, etc. That will
+ * require some communication with the syntax engine.
+ */
+ case GDK_KEY_quoteleft:
+ case GDK_KEY_quoteright:
+ ch = '\'';
+ break;
+#endif
+
+ default:
+ return FALSE;
+ }
+
+ /*
+ * Insert the match if one of the following is true:
+ *
+ * - We are at EOF
+ * - The next character is whitespace
+ * - The next character is punctuation
+ * - The next character is not a opening brace.
+ * - If the char is ", then there must be an even number already on
+ * the current line.
+ */
+
+ if (!next_ch || g_unichar_isspace (next_ch) || (g_unichar_ispunct (next_ch) && !is_opening_char (next_ch)))
+ {
+ /*
+ * Special case for working with double quotes.
+ *
+ * Ignore double quote if we just added enough to make there be an
+ * even number on this line. However, if it was the first quote on
+ * the line, we still need to include a second.
+ */
+ if (ch[0] == '"' || ch[0] == '\'')
+ {
+ guint count;
+
+ count = count_chars_on_line (self, ch[0], &iter);
+ if ((count > 1) && ((count % 2) == 0))
+ return FALSE;
+ }
+
+ gtk_text_buffer_insert_at_cursor (buffer, ch, 1);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ gtk_text_iter_backward_char (&iter);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ide_source_view_maybe_delete_match (IdeSourceView *self,
+ GdkEventKey *event)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ GtkTextIter prev;
+ gunichar ch;
+ gunichar match;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (event);
+ g_assert (event->keyval == GDK_KEY_BackSpace);
+
+ if (!priv->insert_matching_brace)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ prev = iter;
+ if (!gtk_text_iter_backward_char (&prev))
+ return FALSE;
+
+ ch = gtk_text_iter_get_char (&prev);
+
+ switch (ch)
+ {
+ case '[': match = ']'; break;
+ case '{': match = '}'; break;
+ case '(': match = ')'; break;
+ case '"': match = '"'; break;
+ case '\'': match = '\''; break;
+ case '<': match = '>'; break;
+ default: match = 0; break;
+ }
+
+ if (match && (gtk_text_iter_get_char (&iter) == match))
+ {
+ gtk_text_iter_forward_char (&iter);
+ gtk_text_buffer_delete (buffer, &prev, &iter);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+ide_source_view_do_indent (IdeSourceView *self,
+ GdkEventKey *event,
+ IdeIndenter *indenter)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkWidget *widget = (GtkWidget *)self;
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ g_autofree gchar *indent = NULL;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gint cursor_offset = 0;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (priv->auto_indent == TRUE);
+ g_assert (event);
+ g_assert (!indenter || IDE_IS_INDENTER (indenter));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ /*
+ * Insert into the buffer so the auto-indenter can see it. If
+ * GtkSourceView:auto-indent is set, then we will end up with very
+ * unpredictable results.
+ */
+ GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_press_event (widget, event);
+
+ /*
+ * Set begin and end to the position of the new insertion point.
+ */
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->buffer));
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &begin, insert);
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &end, insert);
+
+ /*
+ * Let the formatter potentially set the replacement text. If we don't have a
+ * formatter, use our simple formatter which tries to mimic GtkSourceView.
+ */
+ indent = ide_indenter_format (indenter, text_view, &begin, &end, &cursor_offset, event);
+
+ if (indent != NULL)
+ {
+ /*
+ * Insert the indention text.
+ */
+ gtk_text_buffer_begin_user_action (buffer);
+ if (!gtk_text_iter_equal (&begin, &end))
+ gtk_text_buffer_delete (buffer, &begin, &end);
+ gtk_text_buffer_insert (buffer, &begin, indent, -1);
+ gtk_text_buffer_end_user_action (buffer);
+
+ /*
+ * Make sure we stay in the visible rect.
+ */
+ ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
+
+ /*
+ * Place the cursor, as it could be somewhere within our indent text.
+ */
+ gtk_text_buffer_get_iter_at_mark (buffer, &begin, insert);
+ if (cursor_offset > 0)
+ gtk_text_iter_forward_chars (&begin, cursor_offset);
+ else if (cursor_offset < 0)
+ gtk_text_iter_backward_chars (&begin, ABS (cursor_offset));
+ gtk_text_buffer_select_range (buffer, &begin, &begin);
+ }
+
+ IDE_EXIT;
+}
+
+static inline gboolean
+compare_keys (GdkKeymap *keymap,
+ GdkEventKey *event,
+ GtkBindingEntry *binding_entry,
+ guint *new_keyval,
+ GdkModifierType *state_consumed)
+{
+ gdk_keymap_translate_keyboard_state (keymap,
+ event->hardware_keycode, event->state, event->group,
+ new_keyval, NULL, NULL, state_consumed);
+
+ if (g_ascii_isupper (*new_keyval))
+ {
+ *new_keyval = gdk_keyval_to_lower (*new_keyval);
+ *state_consumed &= ~GDK_SHIFT_MASK;
+ }
+
+ return (*new_keyval == binding_entry->keyval &&
+ (event->state & ~(*state_consumed) & ALL_ACCELS_MASK) == (binding_entry->modifiers &
ALL_ACCELS_MASK));
+}
+
+static gboolean
+is_key_vim_binded (GtkWidget *widget,
+ GdkEventKey *event,
+ guint *new_keyval,
+ GdkModifierType *state_consumed)
+{
+ GdkKeymap *keymap;
+ GtkBindingSet *binding_set;
+ GtkBindingEntry *binding_entry;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GPtrArray *binding_set_array;
+
+
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (IDE_SOURCE_VIEW (widget));
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (priv->mode));
+ keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
+ state = gtk_widget_get_state_flags (GTK_WIDGET (priv->mode));
+
+ gtk_style_context_get (context, state, "gtk-key-bindings", &binding_set_array, NULL);
+ if (binding_set_array)
+ {
+ for (guint i = 0; i < binding_set_array->len; i++)
+ {
+ binding_set = g_ptr_array_index (binding_set_array, i);
+ if (g_str_has_prefix (binding_set->set_name, "builder-vim"))
+ {
+ binding_entry = binding_set->entries;
+ while (binding_entry)
+ {
+ if (compare_keys (keymap, event, binding_entry, new_keyval, state_consumed))
+ {
+ g_ptr_array_unref (binding_set_array);
+ return TRUE;
+ }
+
+ binding_entry = binding_entry->set_next;
+ }
+ }
+ }
+
+ g_ptr_array_unref (binding_set_array);
+ }
+
+ return FALSE;
+}
+
+static void
+command_string_append_to (GString *command_str,
+ guint keyval,
+ GdkModifierType state)
+{
+ if (state & GDK_CONTROL_MASK)
+ g_string_append (command_str, "<ctrl>");
+
+ if (state & GDK_SHIFT_MASK)
+ g_string_append (command_str, "<shift>");
+
+ if (state & GDK_MOD1_MASK)
+ g_string_append (command_str, "<alt>");
+
+ if ((keyval >= '!' && keyval <= '~' ) && keyval != GDK_KEY_bracketleft && keyval != GDK_KEY_bracketright)
+ g_string_append_c (command_str, keyval);
+ else if (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9)
+ g_string_append_c (command_str, keyval - GDK_KEY_KP_0 + '0');
+ else
+ {
+ g_string_append_c (command_str, '[');
+ g_string_append (command_str, gdk_keyval_name (keyval));
+ g_string_append_c (command_str, ']');
+ }
+}
+
+static gboolean
+ide_source_view_do_mode (IdeSourceView *self,
+ GdkEventKey *event)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autofree gchar *suggested_default = NULL;
+ guint new_keyval;
+ GdkModifierType state;
+ GdkModifierType state_consumed;
+ gboolean ret = FALSE;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->mode)
+ {
+ IdeSourceViewMode *mode;
+ gboolean handled;
+ gboolean remove = FALSE;
+
+#ifdef IDE_ENABLE_TRACE
+ {
+ gunichar ch = 0;
+ gchar *name = NULL;
+
+ g_object_get (priv->mode, "name", &name, NULL);
+ if (event->string)
+ ch = g_utf8_get_char (event->string);
+ IDE_TRACE_MSG ("dispatching to mode \"%s\": (%s)",
+ name, g_unichar_isprint (ch) ? event->string : "");
+ g_free (name);
+ }
+#endif
+
+ /* hold a reference incase binding changes mode */
+ mode = g_object_ref (priv->mode);
+
+ if (is_key_vim_binded (GTK_WIDGET (self), event, &new_keyval, &state_consumed))
+ {
+ state = event->state & ~(state_consumed);
+ command_string_append_to (priv->command_str, new_keyval, state);
+ }
+
+ /* lookup what this mode thinks our next default should be */
+ suggested_default = g_strdup (ide_source_view_mode_get_default_mode (priv->mode));
+
+ handled = _ide_source_view_mode_do_event (priv->mode, event, &remove);
+
+ if (remove)
+ {
+ /* only remove mode if it is still active */
+ if (priv->mode == mode)
+ g_clear_object (&priv->mode);
+ }
+
+ g_object_unref (mode);
+
+ if (handled)
+ ret = TRUE;
+ }
+
+ if (priv->mode == NULL)
+ ide_source_view_real_set_mode (self, suggested_default, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
+
+ g_assert (priv->mode != NULL);
+
+ if (ide_source_view_mode_get_mode_type (priv->mode) == IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT)
+ g_string_erase (priv->command_str, 0, -1);
+
+ if (ide_source_view_mode_get_keep_mark_on_char (priv->mode))
+ {
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextMark *selection;
+ GtkTextIter insert_iter;
+ GtkTextIter selection_iter;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+ selection = gtk_text_buffer_get_selection_bound (buffer);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert);
+ gtk_text_buffer_get_iter_at_mark (buffer, &selection_iter, selection);
+
+ if (gtk_text_iter_ends_line (&insert_iter) && !gtk_text_iter_starts_line (&insert_iter))
+ {
+ gtk_text_iter_backward_char (&insert_iter);
+ if (gtk_text_buffer_get_has_selection (buffer))
+ gtk_text_buffer_select_range (buffer, &insert_iter, &selection_iter);
+ else
+ gtk_text_buffer_select_range (buffer, &insert_iter, &insert_iter);
+ }
+ }
+
+ gtk_text_view_reset_cursor_blink (GTK_TEXT_VIEW (self));
+
+ return ret;
+}
+
+static gboolean
+is_modifier_key (GdkEventKey *event)
+{
+ static const guint modifier_keyvals[] = {
+ GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock,
+ GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock, GDK_KEY_Control_L,
+ GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R,
+ GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R,
+ GDK_KEY_Hyper_L, GDK_KEY_Hyper_R, GDK_KEY_ISO_Level3_Shift,
+ GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group,
+ GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group,
+ GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key,
+ GDK_KEY_Scroll_Lock,
+ 0
+ };
+ const guint *ac_val;
+
+ ac_val = modifier_keyvals;
+ while (*ac_val)
+ {
+ if (event->keyval == *ac_val++)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ide_source_view_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ IdeSnippet *snippet;
+ gboolean ret = FALSE;
+ guint change_sequence;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+
+ /*
+ * If we are waiting for input for a modifier key, dispatch it now.
+ */
+ if (priv->waiting_for_capture)
+ {
+ if (!is_modifier_key (event))
+ {
+ guint new_keyval;
+ GdkModifierType state_consumed;
+ GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
+
+ _ide_source_view_set_modifier (self, gdk_keyval_to_unicode (event->keyval));
+ gdk_keymap_translate_keyboard_state (keymap,
+ event->hardware_keycode, event->state, event->group,
+ &new_keyval, NULL, NULL, &state_consumed);
+
+ command_string_append_to (priv->command_str, new_keyval, event->state & ~(state_consumed));
+ }
+
+ return TRUE;
+ }
+
+ /*
+ * If we got Control alone, with no key, and the completion window is
+ * visible, then request that it make itself less visible.
+ */
+ if (event->keyval == GDK_KEY_Control_L &&
+ event->state == 0 &&
+ ide_completion_is_visible (priv->completion))
+ {
+ priv->did_ctrl_opacity = TRUE;
+ return GDK_EVENT_STOP;
+ }
+
+ priv->did_ctrl_opacity = FALSE;
+
+ /*
+ * Are we currently recording a macro? If so lets stash the event for later.
+ */
+ if (priv->recording_macro)
+ ide_source_view_capture_record_event (priv->capture, (GdkEvent *)event,
+ priv->count, priv->modifier);
+
+ /*
+ * Check our current change sequence. If the buffer has changed during the
+ * key-press handler, we'll refocus our selves at the insert caret.
+ */
+ change_sequence = priv->change_sequence;
+
+ priv->in_key_press++;
+
+ /*
+ * If we are in a non-default mode, dispatch the event to the mode. This allows custom
+ * keybindings like Emacs and Vim to be implemented using gtk-bindings CSS.
+ */
+ if (ide_source_view_do_mode (self, event))
+ {
+ ret = TRUE;
+ goto cleanup;
+ }
+
+ /*
+ * Handle movement through the tab stops of the current snippet if needed.
+ */
+ if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
+ {
+ switch ((gint) event->keyval)
+ {
+ case GDK_KEY_Escape:
+ ide_source_view_block_handlers (self);
+ ide_source_view_pop_snippet (self);
+ ide_source_view_scroll_to_insert (self);
+ ide_source_view_unblock_handlers (self);
+ ret = TRUE;
+ goto cleanup;
+
+ case GDK_KEY_KP_Tab:
+ case GDK_KEY_Tab:
+ if ((event->state & GDK_SHIFT_MASK) == 0)
+ {
+ ide_source_view_block_handlers (self);
+ if (!ide_snippet_move_next (snippet))
+ ide_source_view_pop_snippet (self);
+ ide_completion_cancel (priv->completion);
+ /* TODO: ask snippet if we should auto-display completion options? */
+ ide_source_view_scroll_to_insert (self);
+ ide_source_view_unblock_handlers (self);
+ ret = TRUE;
+ goto cleanup;
+ }
+ /* Fallthrough */
+ case GDK_KEY_ISO_Left_Tab:
+ ide_source_view_block_handlers (self);
+ ide_snippet_move_previous (snippet);
+ ide_source_view_scroll_to_insert (self);
+ ide_source_view_unblock_handlers (self);
+ ret = TRUE;
+ goto cleanup;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * If we are backspacing, and the next character is the matching brace,
+ * then we might want to delete it too.
+ */
+ if ((event->keyval == GDK_KEY_BackSpace) && !gtk_text_buffer_get_has_selection (buffer))
+ {
+ if (ide_source_view_maybe_delete_match (self, event))
+ {
+ ret = TRUE;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * If we have an auto-indenter and the event is for a trigger key, then we
+ * chain up to the parent class to insert the character, and then let the
+ * auto-indenter fix things up.
+ */
+ if (priv->buffer != NULL &&
+ priv->auto_indent &&
+ (priv->cursor == NULL || !ide_cursor_is_enabled (priv->cursor)))
+ {
+ IdeIndenter *indenter = ide_source_view_get_indenter (self);
+
+ /*
+ * Indenter may be NULL and that is okay, the IdeIdenter API
+ * knows how to deal with that situation by emulating GtkSourceView
+ * indentation style.
+ */
+
+ if (ide_indenter_is_trigger (indenter, event))
+ {
+ ide_source_view_do_indent (self, event, indenter);
+ ret = TRUE;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * If repeat-with-count is set, we need to repeat the insertion multiple times.
+ */
+ if (priv->count &&
+ priv->mode &&
+ ide_source_view_mode_get_repeat_insert_with_count (priv->mode))
+ {
+ for (gint i = MAX (1, priv->count); i > 0; i--)
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_press_event (widget, event);
+ priv->count = 0;
+ }
+ else
+ {
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_press_event (widget, event);
+ }
+
+ /*
+ * If we just inserted ({["', we might want to insert a matching close.
+ */
+ if (ret)
+ ide_source_view_maybe_insert_match (self, event);
+
+ /*
+ * Only scroll to the insert mark if we made a change.
+ */
+ if (priv->change_sequence != change_sequence)
+ ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
+
+cleanup:
+ priv->in_key_press--;
+
+ return ret;
+}
+
+static gboolean
+ide_source_view_key_release_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ IdeSourceView *self = (IdeSourceView *) widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gboolean ret;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_release_event (widget, event);
+
+ if (priv->did_ctrl_opacity)
+ {
+ IdeCompletionDisplay *display = ide_completion_get_display (priv->completion);
+
+ if (event->keyval == GDK_KEY_Control_L &&
+ event->state == GDK_CONTROL_MASK &&
+ ide_completion_is_visible (priv->completion))
+ {
+ if (gtk_widget_get_opacity (GTK_WIDGET (display)) == 1.0)
+ dzl_object_animate (display, DZL_ANIMATION_LINEAR, 250, NULL, "opacity", 0.1, NULL);
+ else
+ dzl_object_animate (display, DZL_ANIMATION_LINEAR, 250, NULL, "opacity", 1.0, NULL);
+ }
+
+ priv->did_ctrl_opacity = FALSE;
+ }
+
+ if (priv->definition_src_location)
+ ide_source_view_reset_definition_highlight (self);
+
+ return ret;
+}
+
+static gboolean
+ide_source_view_process_press_on_definition (IdeSourceView *self,
+ GdkEventButton *event)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextIter iter;
+ GtkTextWindowType window_type;
+ gint buffer_x;
+ gint buffer_y;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (event != NULL);
+
+ window_type = gtk_text_view_get_window_type (text_view, event->window);
+ gtk_text_view_window_to_buffer_coords (text_view,
+ window_type,
+ event->x,
+ event->y,
+ &buffer_x,
+ &buffer_y);
+ gtk_text_view_get_iter_at_location (text_view,
+ &iter,
+ buffer_x,
+ buffer_y);
+
+ if (priv->definition_src_location != NULL)
+ {
+ GtkTextIter definition_highlight_start;
+ GtkTextIter definition_highlight_end;
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
+ &definition_highlight_start,
+ priv->definition_highlight_start_mark);
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
+ &definition_highlight_end,
+ priv->definition_highlight_end_mark);
+
+ if (gtk_text_iter_in_range (&iter, &definition_highlight_start, &definition_highlight_end))
+ {
+ g_autoptr(IdeLocation) src_location = NULL;
+
+ src_location = g_object_ref (priv->definition_src_location);
+ ide_source_view_reset_definition_highlight (self);
+ g_signal_emit (self, signals [FOCUS_LOCATION], 0, src_location);
+ }
+
+ ide_source_view_reset_definition_highlight (self);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ide_source_view_real_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)widget;
+ gboolean ret;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+
+ if (ide_source_view_process_press_on_definition (self, event))
+ return TRUE;
+
+ if (event->button == GDK_BUTTON_PRIMARY)
+ {
+ if (event->state & GDK_CONTROL_MASK)
+ {
+ if (!ide_cursor_is_enabled (priv->cursor))
+ ide_cursor_add_cursor (priv->cursor, IDE_CURSOR_SELECT);
+ }
+ else if (ide_cursor_is_enabled (priv->cursor))
+ {
+ ide_cursor_remove_cursors (priv->cursor);
+ }
+ }
+
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->button_press_event (widget, event);
+
+ /*
+ * Keep mark on the last character if the sourceviewmode dictates such.
+ */
+ if (gtk_widget_has_focus (widget) &&
+ priv->mode &&
+ ide_source_view_mode_get_keep_mark_on_char (priv->mode))
+ {
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextMark *selection;
+ GtkTextIter iter;
+ GtkTextIter iter2;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ insert = gtk_text_buffer_get_insert (buffer);
+ selection = gtk_text_buffer_get_selection_bound (buffer);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter2, selection);
+
+ if (gtk_text_iter_ends_line (&iter) && !gtk_text_iter_starts_line (&iter))
+ {
+ GtkTextIter prev = iter;
+
+ gtk_text_iter_backward_char (&prev);
+ if (gtk_text_iter_equal (&iter, &iter2))
+ gtk_text_buffer_select_range (buffer, &prev, &prev);
+ }
+ }
+
+ /*
+ * Update our target column so movements don't cause us to revert
+ * to the previous column.
+ */
+ ide_source_view_save_column (self);
+
+ return ret;
+}
+
+static gboolean
+ide_source_view_real_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gboolean ret;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->button_release_event (widget, event);
+
+ if ((event->button == GDK_BUTTON_PRIMARY) && (event->state & GDK_CONTROL_MASK))
+ ide_cursor_add_cursor (priv->cursor, IDE_CURSOR_SELECT);
+
+ return ret;
+}
+
+static gboolean
+ide_source_get_word_from_iter (const GtkTextIter *iter,
+ GtkTextIter *word_start,
+ GtkTextIter *word_end)
+{
+ /* Just using forward/backward to word start/end is not enough
+ * because _ break words when using those functions while they
+ * are commonly used in the same word in code */
+ *word_start = *iter;
+ *word_end = *iter;
+
+ do
+ {
+ const gunichar c = gtk_text_iter_get_char (word_end);
+ if (!(g_unichar_isalnum (c) || c == '_'))
+ break;
+ }
+ while (gtk_text_iter_forward_char (word_end));
+
+ if (gtk_text_iter_equal (word_start, word_end))
+ {
+ /* Iter is not inside a word */
+ return FALSE;
+ }
+
+ while (gtk_text_iter_backward_char (word_start))
+ {
+ const gunichar c = gtk_text_iter_get_char (word_start);
+ if (!(g_unichar_isalnum (c) || c == '_'))
+ {
+ gtk_text_iter_forward_char (word_start);
+ break;
+ }
+ }
+
+ return (!gtk_text_iter_equal (word_start, word_end));
+}
+
+static void
+ide_source_view_get_definition_on_mouse_over_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(DefinitionHighlightData) data = user_data;
+ IdeSourceViewPrivate *priv;
+ IdeBuffer *buffer = (IdeBuffer *)object;
+ g_autoptr(IdeSymbol) symbol = NULL;
+ g_autoptr(GError) error = NULL;
+ IdeLocation *srcloc;
+ IdeSymbolKind kind;
+
+ IDE_ENTRY;
+
+ g_assert (data != NULL);
+ g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (IDE_IS_SOURCE_VIEW (data->self));
+
+ priv = ide_source_view_get_instance_private (data->self);
+
+ priv->waiting_for_symbol = FALSE;
+
+ symbol = ide_buffer_get_symbol_at_location_finish (buffer, result, &error);
+
+ if (symbol == NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ g_warning ("%s", error->message);
+ IDE_EXIT;
+ }
+
+ /* Short circuit if the async operation completed after we closed */
+ if (priv->buffer == NULL)
+ IDE_EXIT;
+
+ kind = ide_symbol_get_kind (symbol);
+
+ srcloc = ide_symbol_get_location (symbol);
+
+ if (srcloc == NULL)
+ srcloc = ide_symbol_get_header_location (symbol);
+
+ if (srcloc != NULL)
+ {
+ GtkTextIter word_start;
+ GtkTextIter word_end;
+
+ if (priv->definition_src_location != NULL && priv->definition_src_location != srcloc)
+ g_clear_object (&priv->definition_src_location);
+
+ if (priv->definition_src_location == NULL)
+ priv->definition_src_location = g_object_ref (srcloc);
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+ &word_start, data->word_start_mark);
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+ &word_end, data->word_end_mark);
+
+ if (kind == IDE_SYMBOL_KIND_HEADER)
+ {
+ GtkTextIter line_start = word_start;
+ GtkTextIter line_end = word_end;
+ g_autofree gchar *line_text = NULL;
+ g_autoptr (GMatchInfo) matchInfo = NULL;
+
+ gtk_text_iter_set_line_offset (&line_start, 0);
+ gtk_text_iter_forward_to_line_end (&line_end);
+
+ line_text = gtk_text_iter_get_visible_text (&line_start,&line_end);
+
+ g_regex_match (priv->include_regex, line_text, 0, &matchInfo);
+
+ if (g_match_info_matches (matchInfo))
+ {
+ gint start_pos;
+ gint end_pos;
+ g_match_info_fetch_pos (matchInfo,
+ 0,
+ &start_pos,
+ &end_pos);
+ word_start = line_start;
+ word_end = line_start;
+
+ gtk_text_iter_set_line_index (&word_start, start_pos);
+ gtk_text_iter_set_line_index (&word_end, end_pos);
+ }
+ }
+
+ gtk_text_buffer_apply_tag_by_name (GTK_TEXT_BUFFER (priv->buffer),
+ TAG_DEFINITION, &word_start, &word_end);
+
+ if (priv->definition_highlight_start_mark != NULL)
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (priv->buffer),
+ priv->definition_highlight_start_mark,
+ &word_start);
+
+ if (priv->definition_highlight_end_mark != NULL)
+ gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (priv->buffer),
+ priv->definition_highlight_end_mark,
+ &word_end);
+
+ ide_source_view_set_cursor_from_name (data->self, "pointer");
+ }
+ else
+ ide_source_view_reset_definition_highlight (data->self);
+
+ IDE_EXIT;
+}
+
+static gboolean
+ide_source_view_real_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextIter iter;
+ GtkTextIter start_iter;
+ GtkTextIter line_start_iter;
+ GtkTextIter end_iter;
+ gunichar ch;
+ gint buffer_x;
+ gint buffer_y;
+ GtkTextWindowType window_type;
+ DefinitionHighlightData *data;
+ gboolean word_found = FALSE;
+ gboolean ret;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->motion_notify_event (widget, event);
+
+ if ((event->state & ALL_ACCELS_MASK) != DEFINITION_HIGHLIGHT_MODIFIER)
+ {
+ if (priv->definition_src_location)
+ ide_source_view_reset_definition_highlight (self);
+
+ return ret;
+ }
+
+ window_type = gtk_text_view_get_window_type (GTK_TEXT_VIEW (self), event->window);
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (self),
+ window_type,
+ event->x,
+ event->y,
+ &buffer_x,
+ &buffer_y);
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (self),
+ &iter,
+ buffer_x,
+ buffer_y);
+
+ /* Workaround about a Clang bug where <> includes are not correctly reported */
+ line_start_iter = iter;
+ gtk_text_iter_set_line_offset (&line_start_iter, 0);
+
+ if (gtk_text_iter_ends_line (&line_start_iter))
+ goto cleanup;
+
+ while ((ch = gtk_text_iter_get_char (&line_start_iter)) &&
+ g_unichar_isspace (ch) &&
+ gtk_text_iter_forward_char (&line_start_iter))
+ ;
+
+ if (ch == '#')
+ {
+ g_autofree gchar *str = NULL;
+ GtkTextIter sharp_iter = line_start_iter;
+ GtkTextIter line_end_iter = iter;
+
+ gtk_text_iter_forward_char (&line_start_iter);
+ gtk_text_iter_forward_to_line_end (&line_end_iter);
+ str = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (priv->buffer), &line_start_iter, &line_end_iter,
FALSE);
+ g_strchug (str);
+ if (g_str_has_prefix (str, "include"))
+ {
+ iter = start_iter = sharp_iter;
+ end_iter = line_end_iter;
+ word_found = TRUE;
+ }
+ }
+
+ if (!word_found && !ide_source_get_word_from_iter (&iter, &start_iter, &end_iter))
+ goto cleanup;
+
+ if (priv->definition_src_location)
+ {
+ GtkTextIter definition_highlight_start;
+ GtkTextIter definition_highlight_end;
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
+ &definition_highlight_start,
+ priv->definition_highlight_start_mark);
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
+ &definition_highlight_end,
+ priv->definition_highlight_end_mark);
+
+ if (gtk_text_iter_equal (&definition_highlight_start, &start_iter) &&
+ gtk_text_iter_equal (&definition_highlight_end, &end_iter))
+ return ret;
+
+ ide_source_view_reset_definition_highlight (self);
+ }
+
+ /* Skip work if we're already active */
+ if (priv->waiting_for_symbol)
+ return ret;
+
+ priv->waiting_for_symbol = TRUE;
+
+ data = g_slice_new0 (DefinitionHighlightData);
+ data->self = self;
+ data->word_start_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (priv->buffer),
+ NULL, &start_iter, TRUE);
+ data->word_end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (priv->buffer),
+ NULL, &end_iter, TRUE);
+
+ g_object_ref (data->self);
+ g_object_ref (data->word_start_mark);
+ g_object_ref (data->word_end_mark);
+
+ ide_buffer_get_symbol_at_location_async (priv->buffer,
+ &iter,
+ NULL,
+ ide_source_view_get_definition_on_mouse_over_cb,
+ data);
+
+ return ret;
+
+cleanup:
+ ide_source_view_reset_definition_highlight (self);
+ return ret;
+}
+
+static void
+ide_source_view_real_add_cursor (IdeSourceView *self,
+ IdeCursorType type)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_cursor_add_cursor (priv->cursor, type);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_remove_cursors (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_cursor_remove_cursors (priv->cursor);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_style_updated (GtkWidget *widget)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ PangoContext *context;
+ PangoLayout *layout;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ GTK_WIDGET_CLASS (ide_source_view_parent_class)->style_updated (widget);
+
+ context = gtk_widget_get_pango_context (widget);
+ layout = pango_layout_new (context);
+ pango_layout_set_text (layout, "X", 1);
+ pango_layout_get_pixel_size (layout, &priv->cached_char_width, &priv->cached_char_height);
+ g_object_unref (layout);
+}
+
+static void
+ide_source_view_real_append_to_count (IdeSourceView *self,
+ gint digit)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ g_return_if_fail (digit >= 0);
+ g_return_if_fail (digit <= 9);
+
+ priv->count = (priv->count * 10) + digit;
+}
+
+static void
+ide_source_view_real_capture_modifier (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ priv->waiting_for_capture = TRUE;
+ while ((priv->modifier == 0) && gtk_widget_has_focus (GTK_WIDGET (self)))
+ gtk_main_iteration ();
+ priv->waiting_for_capture = FALSE;
+}
+
+static void
+ide_source_view_real_change_case (IdeSourceView *self,
+ GtkSourceChangeCaseType type)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_cursor_remove_cursors (priv->cursor);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+
+ if (GTK_SOURCE_IS_BUFFER (buffer))
+ gtk_source_buffer_change_case (GTK_SOURCE_BUFFER (buffer), type, &begin, &end);
+}
+
+static void
+ide_source_view_real_clear_count (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ priv->count = 0;
+}
+
+static void
+ide_source_view_real_clear_modifier (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ priv->modifier = 0;
+}
+
+static void
+ide_source_view_real_clear_selection (IdeSourceView *self)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
+}
+
+static void
+ide_source_view_real_cycle_completion (IdeSourceView *self,
+ GtkDirectionType direction)
+{
+ IdeCompletion *completion;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ completion = ide_source_view_get_completion (self);
+
+ if (!ide_completion_is_visible (completion))
+ {
+ ide_completion_show (completion);
+ return;
+ }
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ case GTK_DIR_DOWN:
+ ide_completion_move_cursor (completion, GTK_MOVEMENT_DISPLAY_LINES, 1);
+ break;
+
+ case GTK_DIR_TAB_BACKWARD:
+ case GTK_DIR_UP:
+ ide_completion_move_cursor (completion, GTK_MOVEMENT_DISPLAY_LINES, -1);
+ break;
+
+ case GTK_DIR_LEFT:
+ case GTK_DIR_RIGHT:
+ default:
+ break;
+ }
+}
+
+static void
+ide_source_view_real_delete_selection (IdeSourceView *self)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gboolean editable;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ editable = gtk_text_view_get_editable (text_view);
+
+ if (!editable)
+ return;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+
+ gtk_text_iter_order (&begin, &end);
+
+ if (gtk_text_iter_is_end (&end) && gtk_text_iter_starts_line (&begin))
+ {
+ gtk_text_buffer_begin_user_action (buffer);
+ gtk_text_iter_backward_char (&begin);
+ gtk_text_buffer_delete (buffer, &begin, &end);
+ gtk_text_buffer_end_user_action (buffer);
+ }
+ else
+ {
+ gtk_text_buffer_delete_selection (buffer, TRUE, editable);
+ }
+
+ ide_source_view_save_column (self);
+}
+
+static void
+ide_source_view_real_indent_selection (IdeSourceView *self,
+ gint level)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkSourceView *source_view = (GtkSourceView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextIter selection;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ /*
+ * Use count to increase direction.
+ */
+ if (priv->count && level)
+ level *= (gint)priv->count;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ if (level < 0)
+ {
+ for (; level < 0; level++)
+ {
+ if (gtk_text_buffer_get_selection_bounds (buffer, &iter, &selection))
+ gtk_source_view_unindent_lines (source_view, &iter, &selection);
+ }
+ }
+ else
+ {
+ for (; level > 0; level--)
+ {
+ if (gtk_text_buffer_get_selection_bounds (buffer, &iter, &selection))
+ gtk_source_view_indent_lines (source_view, &iter, &selection);
+ }
+ }
+}
+
+static void
+ide_source_view_real_insert_modifier (IdeSourceView *self,
+ gboolean use_count)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ gchar str[8] = { 0 };
+ gint count = 1;
+ gint len;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (!priv->modifier)
+ return;
+
+ if (use_count)
+ count = MAX (1, priv->count);
+
+ len = g_unichar_to_utf8 (priv->modifier, str);
+ str [len] = '\0';
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ gtk_text_buffer_begin_user_action (buffer);
+ for (gint i = 0; i < count; i++)
+ gtk_text_buffer_insert_at_cursor (buffer, str, len);
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+static void
+ide_source_view_real_duplicate_entire_line (IdeSourceView *self)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextIter begin, end;
+ gboolean selected;
+ g_autofree gchar *text = NULL;
+ g_autofree gchar *duplicate_line = NULL;
+ GtkTextMark *cursor;
+ GtkTextBuffer *buffer;
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ cursor = gtk_text_buffer_get_insert (buffer);
+
+ gtk_text_buffer_begin_user_action (buffer);
+
+ selected = gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+
+ if (selected)
+ {
+ duplicate_line = gtk_text_iter_get_text (&begin, &end);
+ gtk_text_buffer_insert (buffer, &begin, duplicate_line, -1);
+ }
+ else
+ {
+ gtk_text_buffer_get_iter_at_mark (buffer, &begin, cursor);
+ end = begin;
+
+ gtk_text_iter_set_line_offset (&begin, 0);
+
+ if (!gtk_text_iter_ends_line (&end))
+ gtk_text_iter_forward_to_line_end (&end);
+
+ if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&end))
+ {
+ text = gtk_text_iter_get_text (&begin, &end);
+ duplicate_line = g_strconcat (text, "\n", NULL);
+ gtk_text_buffer_insert (buffer, &begin, duplicate_line, -1);
+ }
+ }
+
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+static void
+ide_source_view_real_join_lines (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextMark *mark;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ if (!GTK_SOURCE_IS_BUFFER (buffer))
+ return;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+ gtk_text_iter_order (&begin, &end);
+
+ /*
+ * We want to leave the cursor inbetween the joined lines, so lets create an
+ * insert mark and delete it later after we reposition the cursor.
+ */
+ mark = gtk_text_buffer_create_mark (buffer, NULL, &end, TRUE);
+
+ /* join lines and restore the insert mark inbetween joined lines. */
+ gtk_text_buffer_begin_user_action (buffer);
+ gtk_source_buffer_join_lines (GTK_SOURCE_BUFFER (buffer), &begin, &end);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end, mark);
+ gtk_text_buffer_select_range (buffer, &end, &end);
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* Remove our temporary mark. */
+ gtk_text_buffer_delete_mark (buffer, mark);
+}
+
+static void
+ide_source_view_real_copy_clipboard_extended (IdeSourceView *self)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkClipboard *clipboard;
+ GtkTextBuffer *buffer;
+ GtkTextIter begin, end;
+ g_autofree gchar *text = NULL;
+ g_autofree gchar *new_text = NULL;
+ gsize len;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self), GDK_SELECTION_CLIPBOARD);
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+ if (gtk_text_iter_is_end (&end))
+ {
+ text = gtk_text_buffer_get_text (buffer, &begin, &end, FALSE);
+ len = strlen (text);
+ new_text = g_malloc (len + 1);
+ memcpy (new_text, text, len);
+ new_text[len] = '\n';
+
+ gtk_clipboard_set_text (clipboard, new_text, len + 1);
+ }
+ else
+ gtk_text_buffer_copy_clipboard (buffer, clipboard);
+}
+
+static void
+ide_source_view_real_paste_clipboard_extended (IdeSourceView *self,
+ gboolean smart_lines,
+ gboolean after_cursor,
+ gboolean place_cursor_at_original)
+
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)self;
+ g_autofree gchar *text = NULL;
+ GtkClipboard *clipboard;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ guint target_line;
+ guint target_line_column;
+
+ /*
+ * NOTE:
+ *
+ * In this function, we try to improve how pasting works in GtkTextView. There are some
+ * semantics that make things easier by tracking the paste of an entire line versus small
+ * snippets of text.
+ *
+ * Basically, we are implementing something close to Vim. However that is not a strict
+ * requirement, just what we are starting with. In fact, the rest of the handling to be like vim
+ * is handled within vim.css (for example, what character to leave the insert mark on).
+ */
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ insert = gtk_text_buffer_get_insert (buffer);
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self), GDK_SELECTION_CLIPBOARD);
+ text = gtk_clipboard_wait_for_text (clipboard);
+ /* Possible mismatch between the clipboard content and the utf-8 converted text
+ * so we set back the utf-8 text in the clipboard to be sure.
+ */
+ gtk_clipboard_set_text (clipboard, text, -1);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ target_line = gtk_text_iter_get_line (&iter);
+ target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self), &iter);
+
+ if (priv->count == 0)
+ priv->count = 1;
+
+ gtk_text_buffer_begin_user_action (buffer);
+
+ for (; priv->count > 0; priv->count--)
+ {
+ /*
+ * If we are pasting an entire line, we don't want to paste it at the current location. We want
+ * to insert a new line after the current line, and then paste it there (so move the insert mark
+ * first).
+ */
+ if (smart_lines && text && g_str_has_suffix (text, "\n"))
+ {
+ g_autofree gchar *trimmed = NULL;
+
+ /*
+ * WORKAROUND:
+ *
+ * This is a hack so that we can continue to use the paste code from within GtkTextBuffer.
+ *
+ * We needed to keep the trailing \n in the text so that we know when we are selecting whole
+ * lines. We also need to insert a new line manually based on the context. Furthermore, we
+ * need to remove the trailing line since we already added one.
+ *
+ * Terribly annoying, but the result is something that feels very nice, similar to Vim.
+ */
+ trimmed = g_strndup (text, strlen (text) - 1);
+
+ if (after_cursor)
+ {
+ if (!gtk_text_iter_ends_line (&iter))
+ gtk_text_iter_forward_to_line_end (&iter);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ g_signal_emit_by_name (self, "insert-at-cursor", "\n");
+ }
+ else
+ {
+ gtk_text_iter_set_line_offset (&iter, 0);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ g_signal_emit_by_name (self, "insert-at-cursor", "\n");
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ gtk_text_iter_backward_line (&iter);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ }
+
+ if (!place_cursor_at_original)
+ {
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ target_line = gtk_text_iter_get_line (&iter);
+ target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self),
+ &iter);
+ }
+
+ gtk_clipboard_set_text (clipboard, trimmed, -1);
+ GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->paste_clipboard (text_view);
+ gtk_clipboard_set_text (clipboard, text, -1);
+ }
+ else
+ {
+ if (after_cursor)
+ {
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ if (!gtk_text_iter_ends_line (&iter))
+ gtk_text_iter_forward_char (&iter);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ }
+
+ GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->paste_clipboard (text_view);
+
+ if (!place_cursor_at_original)
+ {
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ target_line = gtk_text_iter_get_line (&iter);
+ target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self),
+ &iter);
+ }
+ }
+
+ /* Revalidate the position on our next attempt through the paste */
+ gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, target_line, 0);
+ ide_source_view_get_iter_at_visual_column (self, target_line_column, &iter);
+ }
+
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ gtk_text_buffer_end_user_action (buffer);
+
+ if (priv->cursor != NULL)
+ ide_cursor_clear_highlight (priv->cursor);
+}
+
+static void
+ide_source_view_real_selection_theatric (IdeSourceView *self,
+ IdeSourceViewTheatric theatric)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert ((theatric == IDE_SOURCE_VIEW_THEATRIC_EXPAND) ||
+ (theatric == IDE_SOURCE_VIEW_THEATRIC_SHRINK));
+
+ if (!ide_source_view_can_animate (self))
+ return;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+ gtk_text_iter_order (&begin, &end);
+
+ if (gtk_text_iter_equal (&begin, &end))
+ return;
+
+ if (gtk_text_iter_starts_line (&end))
+ gtk_text_iter_backward_char (&end);
+
+ switch (theatric)
+ {
+ case IDE_SOURCE_VIEW_THEATRIC_EXPAND:
+ animate_expand (self, &begin, &end);
+ break;
+
+ case IDE_SOURCE_VIEW_THEATRIC_SHRINK:
+ animate_shrink (self, &begin, &end);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+ide_source_view_save_column (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ priv->target_line_column = ide_source_view_get_visual_column (self, &iter);
+}
+
+static void
+ide_source_view_update_display_name (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ const gchar *display_name = NULL;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->mode != NULL)
+ display_name = ide_source_view_mode_get_display_name (priv->mode);
+
+ if (g_strcmp0 (display_name, priv->display_name) != 0)
+ {
+ g_free (priv->display_name);
+ priv->display_name = g_strdup (display_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODE_DISPLAY_NAME]);
+ }
+}
+
+static void
+ide_source_view_real_set_overwrite (IdeSourceView *self,
+ gboolean overwrite)
+{
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ IDE_TRACE_MSG ("Setting overwrite to %s", overwrite ? "TRUE" : "FALSE");
+
+ gtk_text_view_set_overwrite (GTK_TEXT_VIEW (self), overwrite);
+}
+
+static void
+ide_source_view_real_swap_selection_bounds (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter insert;
+ GtkTextIter selection_bound;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_selection_bounds (buffer, &insert, &selection_bound);
+ gtk_text_buffer_select_range (buffer, &selection_bound, &insert);
+}
+
+static void
+ide_source_view_real_move_error (IdeSourceView *self,
+ GtkDirectionType dir)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeDiagnostics *diagnostics;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GFile *file;
+ GtkTextIter iter;
+ gboolean wrap_around = TRUE;
+ gboolean (*movement) (GtkTextIter *) = NULL;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (!priv->buffer)
+ return;
+
+ if (!(diagnostics = ide_buffer_get_diagnostics (priv->buffer)))
+ return;
+
+ file = ide_buffer_get_file (priv->buffer);
+
+ if (dir == GTK_DIR_RIGHT)
+ dir = GTK_DIR_DOWN;
+ else if (dir == GTK_DIR_LEFT)
+ dir = GTK_DIR_UP;
+
+ /*
+ * TODO: This is not particularly very efficient. But I didn't feel like
+ * plumbing access to the diagnostics set and duplicating most of
+ * the code for getting a diagnostic at a line. Once the diagnostics
+ * get support for fast lookups (bloom filter or something) then
+ * we should change to that.
+ */
+
+ if (dir == GTK_DIR_DOWN)
+ movement = gtk_text_iter_forward_line;
+ else
+ movement = gtk_text_iter_backward_line;
+
+ buffer = GTK_TEXT_BUFFER (priv->buffer);
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+
+wrapped:
+ while (movement (&iter))
+ {
+ IdeDiagnostic *diag;
+ guint line = gtk_text_iter_get_line (&iter);
+
+ if ((diag = ide_diagnostics_get_diagnostic_at_line (diagnostics, file, line)))
+ {
+ IdeLocation *location;
+
+ location = ide_diagnostic_get_location (diag);
+
+ if (location)
+ {
+ guint line_offset;
+
+ line_offset = ide_location_get_line_offset (location);
+ gtk_text_iter_set_line_offset (&iter, 0);
+ for (; line_offset; line_offset--)
+ if (gtk_text_iter_ends_line (&iter) || !gtk_text_iter_forward_char (&iter))
+ break;
+
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
+ return;
+ }
+
+ break;
+ }
+ }
+
+ if (wrap_around)
+ {
+ if (dir == GTK_DIR_DOWN)
+ gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (priv->buffer), &iter);
+ else
+ gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (priv->buffer), &iter);
+ wrap_around = FALSE;
+ goto wrapped;
+ }
+}
+
+static gboolean
+is_same_range (GtkTextIter *new_start,
+ GtkTextIter *old_start,
+ GtkTextIter *new_sel,
+ GtkTextIter *old_sel)
+{
+ if (gtk_text_iter_equal (new_start, old_start))
+ return gtk_text_iter_equal (old_sel, new_sel);
+
+ if (gtk_text_iter_equal (new_start, old_sel))
+ return gtk_text_iter_equal (old_start, new_sel);
+
+ return FALSE;
+}
+
+static void
+ide_source_view_restore_insert_mark (IdeSourceView *self,
+ gboolean move_mark)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextIter selection;
+ GtkTextIter old_iter;
+ GtkTextIter old_selection;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->insert_mark_cleared)
+ {
+ priv->insert_mark_cleared = FALSE;
+ return;
+ }
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, priv->saved_line, 0);
+ ide_source_view_get_iter_at_visual_column (self, priv->saved_line_column, &iter);
+ gtk_text_buffer_get_iter_at_line_offset (buffer,
+ &selection,
+ priv->saved_selection_line,
+ 0);
+ ide_source_view_get_iter_at_visual_column (self,
+ priv->saved_selection_line_column,
+ &selection);
+
+ gtk_text_buffer_get_selection_bounds (buffer, &old_iter, &old_selection);
+
+ if (!is_same_range (&iter, &old_iter, &selection, &old_selection))
+ gtk_text_buffer_select_range (buffer, &iter, &selection);
+
+ if (move_mark)
+ {
+ GtkTextMark *insert;
+
+ insert = gtk_text_buffer_get_insert (buffer);
+ ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
+ }
+}
+
+void
+_ide_source_view_clear_saved_mark (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->insert_mark_cleared = TRUE;
+}
+
+static void
+ide_source_view_real_save_insert_mark (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextMark *selection_bound;
+ GtkTextIter iter;
+ GtkTextIter selection;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ priv->insert_mark_cleared = FALSE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+ selection_bound = gtk_text_buffer_get_selection_bound (buffer);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ gtk_text_buffer_get_iter_at_mark (buffer, &selection, selection_bound);
+
+ priv->saved_line = gtk_text_iter_get_line (&iter);
+ priv->saved_line_column = ide_source_view_get_visual_column (self, &iter);
+ priv->saved_selection_line = gtk_text_iter_get_line (&selection);
+ priv->saved_selection_line_column = ide_source_view_get_visual_column (self, &selection);
+
+ priv->target_line_column = priv->saved_line_column;
+}
+
+static void
+ide_source_view_real_save_command (IdeSourceView *self)
+{
+ GdkEvent *event;
+ guint keyval;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ event = gtk_get_current_event ();
+ if (event && gdk_event_get_keyval (event, &keyval))
+ priv->command = (gunichar)keyval;
+}
+
+static void
+ide_source_view_real_save_search_char (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->modifier)
+ priv->search_char = priv->modifier;
+}
+
+/* In string mode, the search act only on the current line,
+ * search a string to the right if we are not already in one,
+ * and only inner_left is used ( inner_right is set to it )
+ */
+static void
+ide_source_view_real_select_inner (IdeSourceView *self,
+ const gchar *inner_left,
+ const gchar *inner_right,
+ gboolean exclusive,
+ gboolean string_mode)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gunichar unichar_inner_left;
+ gunichar unichar_inner_right;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ unichar_inner_left = g_utf8_get_char (inner_left);
+ unichar_inner_right = g_utf8_get_char (inner_right);
+
+ _ide_source_view_select_inner (self,
+ unichar_inner_left,
+ unichar_inner_right,
+ priv->count,
+ exclusive,
+ string_mode);
+}
+
+static void
+ide_source_view_real_select_tag (IdeSourceView *self,
+ gboolean exclusive)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ _ide_source_view_select_tag (self, priv->count, exclusive);
+}
+
+static void
+ide_source_view_real_pop_selection (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextMark *selection_bound;
+ GtkTextIter insert_iter;
+ GtkTextIter selection_bound_iter;
+ gpointer *data;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ data = g_queue_pop_head (priv->selections);
+
+ if (!data)
+ {
+ g_warning ("request to pop selection that does not exist!");
+ return;
+ }
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ insert = gtk_text_buffer_get_insert (buffer);
+ selection_bound = gtk_text_buffer_get_selection_bound (buffer);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, data [0]);
+ gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound_iter, data [1]);
+
+ gtk_text_buffer_move_mark (buffer, insert, &insert_iter);
+ gtk_text_buffer_move_mark (buffer, selection_bound, &selection_bound_iter);
+
+ gtk_text_buffer_delete_mark (buffer, data [0]);
+ gtk_text_buffer_delete_mark (buffer, data [1]);
+
+ g_object_unref (data [0]);
+ g_object_unref (data [1]);
+ g_free (data);
+}
+
+static void
+ide_source_view_real_push_selection (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextMark *selection_bound;
+ GtkTextIter insert_iter;
+ GtkTextIter selection_bound_iter;
+ gpointer *data;
+ gboolean left_gravity;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert);
+
+ selection_bound = gtk_text_buffer_get_selection_bound (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound_iter, selection_bound);
+
+ left_gravity = (gtk_text_iter_compare (&insert_iter, &selection_bound_iter) <= 0);
+ insert = gtk_text_buffer_create_mark (buffer, NULL, &insert_iter, left_gravity);
+
+ left_gravity = (gtk_text_iter_compare (&selection_bound_iter, &insert_iter) < 0);
+ selection_bound = gtk_text_buffer_create_mark (buffer, NULL, &selection_bound_iter, left_gravity);
+
+ data = g_new0 (gpointer, 2);
+ data [0] = g_object_ref (insert);
+ data [1] = g_object_ref (selection_bound);
+
+ g_queue_push_head (priv->selections, data);
+}
+
+static void
+ide_source_view_real_push_snippet (IdeSourceView *self,
+ IdeSnippet *snippet,
+ const GtkTextIter *location)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autoptr(GFile) gparentfile = NULL;
+ g_autoptr(IdeContext) ide_context = NULL;
+ IdeSnippetContext *context;
+ GFile *file = NULL;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (IDE_IS_SNIPPET (snippet));
+ g_assert (location != NULL);
+
+ context = ide_snippet_get_context (snippet);
+
+ if (priv->buffer != NULL)
+ {
+ if ((file = ide_buffer_get_file (priv->buffer)))
+ {
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *dirname = NULL;
+
+ name = g_file_get_basename (file);
+ gparentfile = g_file_get_parent (file);
+ dirname = g_file_get_path (gparentfile);
+ path = g_file_get_path (file);
+ ide_snippet_context_add_variable (context, "filename", name);
+ ide_snippet_context_add_variable (context, "dirname", dirname);
+ ide_snippet_context_add_variable (context, "path", path);
+ }
+
+ if ((ide_context = ide_buffer_ref_context (priv->buffer)))
+ {
+ g_autoptr(GFile) workdir = NULL;
+
+ workdir = ide_context_ref_workdir (ide_context);
+ if (workdir && file)
+ {
+ g_autofree gchar *relative_path = NULL;
+ relative_path = g_file_get_relative_path (workdir, file);
+ ide_snippet_context_add_variable (context, "relative_path", relative_path);
+ }
+ if (workdir && gparentfile)
+ {
+ g_autofree gchar *relative_dirname = NULL;
+ relative_dirname = g_file_get_relative_path (workdir, gparentfile);
+ ide_snippet_context_add_variable (context, "relative_dirname", relative_dirname);
+ }
+ }
+ }
+}
+
+static void
+ide_source_view_real_reindent (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ IdeIndenter *indenter;
+ GtkTextIter begin;
+ GtkTextIter end;
+ GdkWindow *window;
+ GtkTextIter iter;
+ guint i;
+ guint first_line;
+ g_autoptr(GPtrArray) lines = NULL;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->buffer == NULL)
+ return;
+
+ /* indenter may be NULL and that is okay */
+ indenter = ide_source_view_get_indenter (self);
+
+ buffer = GTK_TEXT_BUFFER (priv->buffer);
+ window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_TEXT);
+
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+ gtk_text_iter_order (&begin, &end);
+
+ gtk_text_iter_set_line_offset (&begin, 0);
+ first_line = gtk_text_iter_get_line (&begin);
+
+ /* if the end position is at index 0 of the next line (common with
+ * line mode in vim), then move it back to the end of the previous
+ * line, since we don't really care about that next line.
+ */
+ if (gtk_text_iter_starts_line (&end) &&
+ gtk_text_iter_get_line (&begin) != gtk_text_iter_get_line (&end))
+ gtk_text_iter_backward_char (&end);
+
+ if (!gtk_text_iter_ends_line (&end))
+ gtk_text_iter_forward_to_line_end (&end);
+
+ lines = g_ptr_array_new_with_free_func (g_free);
+
+ if (gtk_text_iter_compare (&begin, &end) == 0)
+ g_ptr_array_add (lines, g_strdup (""));
+ else
+ for (iter = begin;
+ gtk_text_iter_compare (&iter, &end) < 0;
+ gtk_text_iter_forward_line (&iter))
+ {
+ GtkTextIter line_end = iter;
+ gchar *line;
+
+ if (!gtk_text_iter_ends_line (&line_end))
+ gtk_text_iter_forward_to_line_end (&line_end);
+
+ line = gtk_text_iter_get_slice (&iter, &line_end);
+ g_ptr_array_add (lines, g_strstrip (line));
+ }
+
+ gtk_text_buffer_begin_user_action (buffer);
+
+ gtk_text_buffer_delete (buffer, &begin, &end);
+
+ for (i = 0; i < lines->len; i++)
+ {
+ g_autofree gchar *indent = NULL;
+ const gchar *line;
+ GdkEventKey *event;
+ gint cursor_offset;
+
+ line = g_ptr_array_index (lines, i);
+ event = dzl_gdk_synthesize_event_key (window, '\n');
+ indent = ide_indenter_format (indenter, GTK_TEXT_VIEW (self), &begin, &end, &cursor_offset, event);
+ gdk_event_free ((GdkEvent *)event);
+
+ if (indent != NULL)
+ {
+ if (!gtk_text_iter_equal (&begin, &end))
+ gtk_text_buffer_delete (buffer, &begin, &end);
+
+ gtk_text_buffer_insert (buffer, &begin, indent, -1);
+ gtk_text_buffer_insert (buffer, &begin, line, -1);
+
+ if (i != lines->len - 1)
+ gtk_text_buffer_insert (buffer, &begin, "\n", -1);
+ }
+
+ end = begin;
+ }
+
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* Advance to first non-whitespace */
+ gtk_text_iter_set_line (&begin, first_line);
+ while (!gtk_text_iter_ends_line (&begin) &&
+ g_unichar_isspace (gtk_text_iter_get_char (&begin)))
+ gtk_text_iter_forward_char (&begin);
+
+ gtk_text_buffer_select_range (buffer, &begin, &begin);
+}
+
+static void
+ide_source_view_set_overscroll_num_lines (IdeSourceView *self,
+ gint num_lines)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gint height;
+ gint new_margin;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ priv->overscroll_num_lines = num_lines;
+
+ /* Do nothing if there is no height yet */
+ if (!(height = gtk_widget_get_allocated_height (GTK_WIDGET (self))))
+ return;
+
+ new_margin = priv->overscroll_num_lines * priv->cached_char_height;
+
+ if (new_margin < 0)
+ {
+ new_margin = height + new_margin;
+ if (new_margin < 0)
+ new_margin = height - priv->cached_char_height;
+ }
+
+ new_margin = CLAMP (new_margin, 0, MAX (height - priv->cached_char_height, 0));
+
+ /* ensure enough space for the overlay scrollbars and their interactive
+ * trough when scrolled to the end.
+ */
+ if (new_margin < 16)
+ new_margin = 16;
+
+ g_object_set (self,
+ "bottom-margin", new_margin,
+ NULL);
+}
+
+static void
+ide_source_view_constructed (GObject *object)
+{
+ IdeSourceView *self = (IdeSourceView *)object;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ G_OBJECT_CLASS (ide_source_view_parent_class)->constructed (object);
+
+ _ide_source_view_init_shortcuts (self);
+
+ ide_source_view_real_set_mode (self, NULL, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
+
+ priv->definition_src_location = NULL;
+ ide_source_view_reset_definition_highlight (self);
+
+ priv->completion = _ide_completion_new (GTK_SOURCE_VIEW (self));
+
+ /* Disable sourceview completion always */
+ gtk_source_completion_block_interactive (gtk_source_view_get_completion (GTK_SOURCE_VIEW (self)));
+
+ /* Disable completion until focus-in-event */
+ block_interactive (self);
+}
+
+static void
+ide_source_view_real_insert_at_cursor (GtkTextView *text_view,
+ const gchar *str)
+{
+ IdeSourceView *self = (IdeSourceView *)text_view;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (str);
+
+ GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->insert_at_cursor (text_view, str);
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ insert = gtk_text_buffer_get_insert (buffer);
+ ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
+}
+
+static void
+ide_source_view_real_sort (IdeSourceView *self,
+ gboolean ignore_case,
+ gboolean reverse)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+ GtkSourceSortFlags sort_flags = GTK_SOURCE_SORT_FLAGS_NONE;
+
+ g_assert (GTK_TEXT_VIEW (self));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+
+ if (gtk_text_iter_equal (&begin, &end))
+ gtk_text_buffer_get_bounds (buffer, &begin, &end);
+
+ if (!ignore_case)
+ sort_flags |= GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE;
+
+ if (reverse)
+ sort_flags |= GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER;
+
+ gtk_source_buffer_sort_lines (GTK_SOURCE_BUFFER (buffer),
+ &begin,
+ &end,
+ sort_flags,
+ 0);
+}
+
+static void
+ide_source_view_draw_snippet_background (IdeSourceView *self,
+ cairo_t *cr,
+ IdeSnippet *snippet,
+ gint width)
+{
+ GtkTextBuffer *buffer;
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextIter begin;
+ GtkTextIter end;
+ GtkTextMark *mark_begin;
+ GtkTextMark *mark_end;
+ GdkRectangle r;
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (cr);
+ g_assert (IDE_IS_SNIPPET (snippet));
+
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ mark_begin = ide_snippet_get_mark_begin (snippet);
+ mark_end = ide_snippet_get_mark_end (snippet);
+
+ if (!mark_begin || !mark_end)
+ return;
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &begin, mark_begin);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);
+
+ get_rect_for_iters (text_view, &begin, &end, &r, GTK_TEXT_WINDOW_TEXT);
+
+ gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_TEXT, r.x, r.y, &r.x, &r.y);
+
+ dzl_cairo_rounded_rectangle (cr, &r, 5, 5);
+
+ cairo_fill (cr);
+}
+
+static void
+ide_source_view_draw_snippets_background (IdeSourceView *self,
+ cairo_t *cr)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = GTK_TEXT_VIEW (self);
+ GdkWindow *window;
+ gint width;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (cr);
+
+ window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT);
+ width = gdk_window_get_width (window);
+
+ cairo_save (cr);
+
+ gdk_cairo_set_source_rgba (cr, &priv->snippet_area_background_rgba);
+
+ for (guint i = 0; i < priv->snippets->length; i++)
+ {
+ IdeSnippet *snippet = g_queue_peek_nth (priv->snippets, i);
+
+ ide_source_view_draw_snippet_background (self,
+ cr,
+ snippet,
+ width - ((priv->snippets->length - i) * 10));
+ }
+
+ cairo_restore (cr);
+}
+
+static void
+ide_source_view_real_draw_layer (GtkTextView *text_view,
+ GtkTextViewLayer layer,
+ cairo_t *cr)
+{
+ IdeSourceView *self = (IdeSourceView *)text_view;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (cr);
+
+ GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->draw_layer (text_view, layer, cr);
+
+ if (layer == GTK_TEXT_VIEW_LAYER_BELOW_TEXT)
+ {
+ if (priv->snippets->length)
+ ide_source_view_draw_snippets_background (self, cr);
+
+ if (g_signal_has_handler_pending (self, signals [DRAW_BUBBLES], 0, FALSE))
+ {
+ GdkRectangle rect;
+
+ gtk_text_view_get_visible_rect (text_view, &rect);
+
+ cairo_save (cr);
+ cairo_translate (cr, rect.x, rect.y);
+ g_signal_emit (self, signals [DRAW_BUBBLES], 0, cr);
+ cairo_restore (cr);
+ }
+ }
+}
+
+static gboolean
+ide_source_view_focus_in_event (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gboolean ret;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /* Restore the completion window now that we have regained focus. */
+ unblock_interactive (self);
+
+ /* Force size allocation immediately if we have something queued. */
+ if (priv->delay_size_allocate_chainup)
+ {
+ g_clear_handle_id (&priv->delay_size_allocate_chainup, g_source_remove);
+ ide_source_view_do_size_allocate_hack_cb (self);
+ }
+
+ /*
+ * Restore the insert mark, but ignore selections (since we cant ensure they
+ * will stay looking selected, as the other frame could be a view into our
+ * own buffer).
+ */
+ if (get_selection_owner (self) != self)
+ {
+ priv->saved_selection_line = priv->saved_line;
+ priv->saved_selection_line_column = priv->saved_line_column;
+ }
+
+ ide_source_view_restore_insert_mark (self, FALSE);
+
+ /* restore line highlight if enabled */
+ if (priv->highlight_current_line)
+ gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (self), TRUE);
+
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->focus_in_event (widget, event);
+
+ return ret;
+}
+
+static gboolean
+ide_source_view_focus_out_event (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gboolean ret;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /* save our insert mark for when we focus back in. it could have moved if
+ * another view into the same buffer has caused the insert mark to jump.
+ */
+ ide_source_view_real_save_insert_mark (self);
+
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->focus_out_event (widget, event);
+
+ /*
+ * Block the completion window while we are not focused. It confuses text
+ * insertion and such.
+ */
+ block_interactive (self);
+
+ /* We don't want highlight-current-line unless the widget is in focus, so
+ * disable it until we get re-focused.
+ */
+ gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (self), FALSE);
+
+ if (priv->cursor != NULL)
+ ide_cursor_remove_cursors (priv->cursor);
+
+ return ret;
+}
+
+static void
+ide_source_view_real_begin_macro (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSourceViewModeType mode_type;
+ GdkEvent *event;
+ const gchar *mode_name;
+ gunichar modifier;
+ gint count;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->in_replay_macro)
+ IDE_GOTO (in_replay);
+
+ priv->recording_macro = TRUE;
+
+ mode_type = ide_source_view_mode_get_mode_type (priv->mode);
+ mode_name = ide_source_view_mode_get_name (priv->mode);
+ modifier = priv->modifier;
+ count = priv->count;
+ event = gtk_get_current_event ();
+
+ g_clear_object (&priv->capture);
+
+ priv->capture = ide_source_view_capture_new (self, mode_name, mode_type, count, modifier);
+ ide_source_view_capture_record_event (priv->capture, event, count, modifier);
+ gdk_event_free (event);
+
+in_replay:
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_end_macro (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->in_replay_macro)
+ IDE_GOTO (in_replay);
+
+ priv->recording_macro = FALSE;
+
+in_replay:
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_goto_definition_symbol_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(IdeSourceView) self = user_data;
+ g_autoptr(IdeSymbol) symbol = NULL;
+ IdeBuffer *buffer = (IdeBuffer *)object;
+ g_autoptr(GError) error = NULL;
+ IdeLocation *srcloc;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_BUFFER (buffer));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ symbol = ide_buffer_get_symbol_at_location_finish (buffer, result, &error);
+
+ if (symbol == NULL)
+ {
+ g_warning ("%s", error->message);
+ IDE_EXIT;
+ }
+
+ srcloc = ide_symbol_get_location (symbol);
+
+ if (srcloc == NULL)
+ srcloc = ide_symbol_get_header_location (symbol);
+
+ if (srcloc != NULL)
+ {
+ guint line = ide_location_get_line (srcloc);
+ guint line_offset = ide_location_get_line_offset (srcloc);
+ GFile *file = ide_location_get_file (srcloc);
+ GFile *our_file = ide_buffer_get_file (buffer);
+
+#ifdef IDE_ENABLE_TRACE
+ const gchar *filename = g_file_peek_path (file);
+
+ IDE_TRACE_MSG ("%s => %s +%u:%u",
+ ide_symbol_get_name (symbol),
+ filename, line+1, line_offset+1);
+#endif
+
+ /* Stash our current position for jump-back */
+ ide_source_view_jump (self, NULL, NULL);
+
+ /*
+ * If we are navigating within this file, just stay captive instead of
+ * potentially allowing jumping to the file in another editor.
+ */
+ if (g_file_equal (file, our_file))
+ {
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (buffer),
+ &iter, line, line_offset);
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+ ide_source_view_scroll_to_insert (self);
+ IDE_EXIT;
+ }
+
+ g_signal_emit (self, signals [FOCUS_LOCATION], 0, srcloc);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_goto_definition (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->buffer != NULL)
+ {
+ GtkTextMark *insert;
+ GtkTextIter iter;
+
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->buffer));
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &iter, insert);
+
+ ide_buffer_get_symbol_at_location_async (priv->buffer,
+ &iter,
+ NULL,
+ ide_source_view_goto_definition_symbol_cb,
+ g_object_ref (self));
+ }
+}
+
+static void
+ide_source_view_real_hide_completion (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_completion_hide (priv->completion);
+}
+
+static void
+ide_source_view_real_replay_macro (IdeSourceView *self,
+ gboolean use_count)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ IdeSourceViewCapture *capture;
+ gint count = 1;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->recording_macro)
+ {
+ g_warning ("Cannot playback macro while recording.");
+ IDE_EXIT;
+ }
+
+ if (priv->in_replay_macro)
+ {
+ g_warning ("Cannot playback macro while playing back macro.");
+ IDE_EXIT;
+ }
+
+ if (priv->capture == NULL)
+ return;
+
+ if (use_count)
+ count = MAX (1, priv->count);
+
+ IDE_TRACE_MSG ("Replaying capture %d times.", count);
+
+ priv->in_replay_macro = TRUE;
+ capture = priv->capture, priv->capture = NULL;
+ for (gint i = 0; i < count; i++)
+ ide_source_view_capture_replay (capture);
+ g_clear_object (&priv->capture);
+ priv->capture = capture, capture = NULL;
+ priv->in_replay_macro = FALSE;
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_begin_user_action (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_begin_user_action (buffer);
+}
+
+static void
+ide_source_view_end_user_action (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+gboolean
+ide_source_view_get_overwrite (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ if (gtk_text_view_get_overwrite (GTK_TEXT_VIEW (self)))
+ {
+ if (!priv->mode || !ide_source_view_mode_get_block_cursor (priv->mode))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+ide_source_view_get_fixit_label (IdeSourceView *self,
+ IdeTextEdit *fixit)
+{
+ IdeLocation *begin_loc;
+ IdeLocation *end_loc;
+ IdeRange *range;
+ GtkTextBuffer *buffer;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gchar *old_text = NULL;
+ gchar *new_text = NULL;
+ gchar *tmp;
+ gchar *ret = NULL;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (fixit != NULL);
+
+ range = ide_text_edit_get_range (fixit);
+ if (range == NULL)
+ goto cleanup;
+
+ new_text = g_strdup (ide_text_edit_get_text (fixit));
+ if (new_text == NULL)
+ goto cleanup;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ if (!IDE_IS_BUFFER (buffer))
+ goto cleanup;
+
+ begin_loc = ide_range_get_begin (range);
+ end_loc = ide_range_get_end (range);
+
+ ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &begin, begin_loc);
+ ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &end, end_loc);
+
+ old_text = gtk_text_iter_get_slice (&begin, &end);
+
+ if (strlen (old_text) > FIXIT_LABEL_LEN_MAX)
+ {
+ tmp = old_text;
+ old_text = g_strndup (tmp, FIXIT_LABEL_LEN_MAX);
+ g_free (tmp);
+ }
+
+ if (strlen (new_text) > FIXIT_LABEL_LEN_MAX)
+ {
+ tmp = new_text;
+ new_text = g_strndup (tmp, FIXIT_LABEL_LEN_MAX);
+ g_free (tmp);
+ }
+
+ tmp = old_text;
+ old_text = g_markup_escape_text (old_text, -1);
+ g_free (tmp);
+
+ tmp = new_text;
+ new_text = g_markup_escape_text (new_text, -1);
+ g_free (tmp);
+
+ if (old_text [0] == '\0')
+ ret = g_strdup_printf (_("Insert “%s”"), new_text);
+ else
+ ret = g_strdup_printf (_("Replace “%s” with “%s”"), old_text, new_text);
+
+cleanup:
+ g_free (old_text);
+ g_free (new_text);
+
+ return ret;
+}
+
+static void
+ide_source_view__fixit_activate (IdeSourceView *self,
+ GtkMenuItem *menu_item)
+{
+ IdeTextEdit *fixit;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_MENU_ITEM (menu_item));
+
+ fixit = g_object_get_data (G_OBJECT (menu_item), "IDE_FIXIT");
+
+ if (fixit != NULL)
+ {
+ IdeLocation *srcloc;
+ IdeRange *range;
+ GtkTextBuffer *buffer;
+ const gchar *text;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ if (!IDE_IS_BUFFER (buffer))
+ return;
+
+ text = ide_text_edit_get_text (fixit);
+ range = ide_text_edit_get_range (fixit);
+
+ srcloc = ide_range_get_begin (range);
+ ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &begin, srcloc);
+
+ srcloc = ide_range_get_end (range);
+ ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &end, srcloc);
+
+ gtk_text_buffer_begin_user_action (buffer);
+ gtk_text_buffer_delete (buffer, &begin, &end);
+ gtk_text_buffer_insert (buffer, &begin, text, -1);
+ gtk_text_buffer_end_user_action (buffer);
+ }
+}
+
+static void
+ide_source_view_real_populate_popup (GtkTextView *text_view,
+ GtkWidget *popup)
+{
+ IdeSourceView *self = (IdeSourceView *)text_view;
+ g_autoptr(GPtrArray) line_diags = NULL;
+ GtkSeparatorMenuItem *sep;
+ IdeDiagnostics *diagnostics;
+ GtkTextBuffer *buffer;
+ GtkMenuItem *menu_item;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ GtkTextIter begin;
+ GtkTextIter end;
+ GMenu *model;
+
+ g_assert (GTK_IS_TEXT_VIEW (text_view));
+ g_assert (GTK_IS_WIDGET (popup));
+
+ GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->populate_popup (text_view, popup);
+
+ if (!GTK_IS_MENU (popup))
+ return;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ if (!IDE_IS_BUFFER (buffer))
+ return;
+
+ model = dzl_application_get_menu_by_id (DZL_APPLICATION_DEFAULT, "ide-source-view-popup-menu");
+ gtk_menu_shell_bind_model (GTK_MENU_SHELL (popup), G_MENU_MODEL (model), NULL, TRUE);
+
+ gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+
+ /*
+ * TODO: I'm pretty sure we don't want to use the insert mark, but the
+ * location of the button-press-event (if there was one).
+ */
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+
+ /*
+ * Check if we have a diagnostic at this position and if there are fixits associated with it.
+ * If so, display the "Apply TextEdit" menu item with available fixits.
+ */
+ if ((diagnostics = ide_buffer_get_diagnostics (IDE_BUFFER (buffer))))
+ {
+ g_assert (IDE_IS_DIAGNOSTICS (diagnostics));
+
+ line_diags = ide_diagnostics_get_diagnostics_at_line (diagnostics,
+ ide_buffer_get_file (IDE_BUFFER (buffer)),
+ gtk_text_iter_get_line (&iter));
+ IDE_PTR_ARRAY_SET_FREE_FUNC (line_diags, g_object_unref);
+ }
+
+ if (line_diags != NULL)
+ {
+ for (guint j = 0; j < line_diags->len; j++)
+ {
+ IdeDiagnostic *diag = g_ptr_array_index (line_diags, j);
+ guint num_fixits;
+
+ num_fixits = ide_diagnostic_get_n_fixits (diag);
+
+ if (num_fixits > 0)
+ {
+ GtkWidget *parent;
+ GtkWidget *submenu;
+ guint i;
+
+ sep = g_object_new (GTK_TYPE_SEPARATOR_MENU_ITEM,
+ "visible", TRUE,
+ NULL);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (popup), GTK_WIDGET (sep));
+
+ submenu = gtk_menu_new ();
+
+ parent = g_object_new (GTK_TYPE_MENU_ITEM,
+ "label", _("Apply Fix-It"),
+ "submenu", submenu,
+ "visible", TRUE,
+ NULL);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (popup), parent);
+
+ for (i = 0; i < num_fixits; i++)
+ {
+ IdeTextEdit *fixit;
+ gchar *label;
+
+ fixit = ide_diagnostic_get_fixit (diag, i);
+ label = ide_source_view_get_fixit_label (self, fixit);
+
+ menu_item = g_object_new (GTK_TYPE_MENU_ITEM,
+ "label", label,
+ "visible", TRUE,
+ NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (submenu), GTK_WIDGET (menu_item));
+
+ g_object_set_data_full (G_OBJECT (menu_item),
+ "IDE_FIXIT",
+ g_object_ref (fixit),
+ (GDestroyNotify)g_object_unref);
+
+ g_signal_connect_object (menu_item,
+ "activate",
+ G_CALLBACK (ide_source_view__fixit_activate),
+ self,
+ G_CONNECT_SWAPPED);
+ }
+ }
+ }
+ }
+}
+
+static void
+ide_source_view_real_rebuild_highlight (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->buffer != NULL)
+ ide_buffer_rehighlight (priv->buffer);
+
+ IDE_EXIT;
+}
+
+static gboolean
+ignore_invalid_buffers (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ if (G_VALUE_HOLDS (from_value, GTK_TYPE_TEXT_BUFFER))
+ {
+ GtkTextBuffer *buffer;
+
+ buffer = g_value_get_object (from_value);
+
+ if (IDE_IS_BUFFER (buffer))
+ {
+ g_value_set_object (to_value, buffer);
+ return TRUE;
+ }
+ }
+
+ g_value_set_object (to_value, NULL);
+
+ return TRUE;
+}
+
+static void
+ide_source_view_set_indent_style (IdeSourceView *self,
+ IdeIndentStyle indent_style)
+{
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert ((indent_style == IDE_INDENT_STYLE_SPACES) ||
+ (indent_style == IDE_INDENT_STYLE_TABS));
+
+ if (indent_style == IDE_INDENT_STYLE_SPACES)
+ gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (self), TRUE);
+ else
+ gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (self), FALSE);
+}
+
+static gboolean
+ide_source_view_do_size_allocate_hack_cb (gpointer data)
+{
+ IdeSourceView *self = data;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkAllocation alloc = priv->delay_size_allocation;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ priv->delay_size_allocate_chainup = 0;
+
+ GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (GTK_WIDGET (self), &alloc);
+
+ ide_source_view_set_overscroll_num_lines (self, priv->overscroll_num_lines);
+
+ return G_SOURCE_REMOVE;
+}
+
+/*
+ * HACK:
+ *
+ * We really want the panels in Builder to be as smooth as possible when
+ * animating in and out of the scene. However, since these are not floating
+ * panels, we have the challenge of trying to go through the entire relayout,
+ * pixelcache, draw cycle many times per-second. Most systems are simply not
+ * up to the task.
+ *
+ * We can, however, take a shortcut when shrinking the allocation. We can
+ * simply defer the allocation request that would normally be chained up
+ * to GtkTextView and finish that work after the animation has completed.
+ * We use a simple heuristic to determine this, simply "missing" a size
+ * allocation from the typical frame clock cycle.
+ */
+static gboolean
+ide_source_view_do_size_allocate_hack (IdeSourceView *self,
+ GtkAllocation *allocation)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkWidget *widget = (GtkWidget *)self;
+ GtkAllocation old;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (allocation != NULL);
+
+ /*
+ * If we are shrinking the allocation, we can go forward with the hack.
+ * If not, we will abort our request and do the normal chainup cycle.
+ */
+ gtk_widget_get_allocation (widget, &old);
+ if ((old.width < allocation->width) || (old.height < allocation->height))
+ return FALSE;
+
+ /*
+ * Save the allocation for later. We'll need it to apply after our timeout
+ * which will occur just after the last frame (or sooner if we stall the
+ * drawing pipeline).
+ */
+ priv->delay_size_allocation = *allocation;
+
+ /*
+ * Register our timeout to occur just after a normal frame interval.
+ * If we are animating at 60 FPS, we should get another size-allocate within
+ * the frame cycle, typically 17 msec.
+ */
+ g_clear_handle_id (&priv->delay_size_allocate_chainup, g_source_remove);
+ priv->delay_size_allocate_chainup = g_timeout_add (30,
+ ide_source_view_do_size_allocate_hack_cb,
+ self);
+
+ return TRUE;
+}
+
+static void
+ide_source_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (GTK_IS_WIDGET (widget));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (allocation != NULL);
+
+ if (!ide_source_view_do_size_allocate_hack (self, allocation))
+ {
+ GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (GTK_WIDGET (self), allocation);
+ ide_source_view_set_overscroll_num_lines (self, priv->overscroll_num_lines);
+ }
+}
+
+static gboolean
+ide_source_view_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ gboolean ret = GDK_EVENT_PROPAGATE;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /*
+ * If the user started a manual scroll while we were attempting to scroll to
+ * the target position, just abort our delayed scroll.
+ */
+ priv->scrolling_to_scroll_mark = FALSE;
+
+ /*
+ * Be forward-portable against changes underneath us.
+ */
+ if (GTK_WIDGET_CLASS (ide_source_view_parent_class)->scroll_event)
+ ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->scroll_event (widget, event);
+
+ return ret;
+}
+
+static void
+ide_source_view_real_reset_font_size (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->font_scale != FONT_SCALE_NORMAL)
+ {
+ priv->font_scale = FONT_SCALE_NORMAL;
+ ide_source_view_rebuild_css (self);
+ }
+}
+
+static void
+ide_source_view_real_increase_font_size (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->font_scale < LAST_FONT_SCALE - 1)
+ {
+ priv->font_scale++;
+ ide_source_view_rebuild_css (self);
+ }
+}
+
+static void
+ide_source_view_real_decrease_font_size (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->font_scale > 0)
+ {
+ priv->font_scale--;
+ ide_source_view_rebuild_css (self);
+ }
+}
+
+static void
+ide_source_view_real_delete_from_cursor (GtkTextView *text_view,
+ GtkDeleteType delete_type,
+ gint count)
+{
+ if (delete_type == GTK_DELETE_PARAGRAPHS)
+ ide_text_util_delete_line (text_view, count);
+ else
+ GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->delete_from_cursor (text_view,
+ delete_type,
+ count);
+}
+
+static void
+ide_source_view_real_select_all (IdeSourceView *self,
+ gboolean select_)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ g_signal_chain_from_overridden_handler (self, select_);
+
+ priv->insert_mark_cleared = TRUE;
+}
+
+static void
+ide_source_view_rename_changed (IdeSourceView *self,
+ DzlSimplePopover *popover)
+{
+ const gchar *text;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (DZL_IS_SIMPLE_POPOVER (popover));
+
+ text = dzl_simple_popover_get_text (popover);
+ dzl_simple_popover_set_ready (popover, text != NULL);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_rename_edits_applied (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(IdeSourceView) self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_BUFFER_MANAGER (object));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /*
+ * The completion window can sometimes popup when performing the replacements
+ * so we manually hide that window here.
+ */
+ ide_source_view_real_hide_completion (self);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_rename_edits_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeRenameProvider *provider = (IdeRenameProvider *)object;
+ g_autoptr(IdeSourceView) self = user_data;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autoptr(GPtrArray) edits = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(IdeContext) context = NULL;
+ IdeBufferManager *buffer_manager;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_RENAME_PROVIDER (provider));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (!ide_rename_provider_rename_finish (provider, result, &edits, &error))
+ {
+ /* TODO: Propagate error to UI */
+ g_warning ("%s", error->message);
+ IDE_EXIT;
+ }
+
+ g_assert (edits != NULL);
+
+ IDE_PTR_ARRAY_SET_FREE_FUNC (edits, g_object_unref);
+
+ context = ide_buffer_ref_context (priv->buffer);
+ buffer_manager = ide_buffer_manager_from_context (context);
+
+ ide_buffer_manager_apply_edits_async (buffer_manager,
+ IDE_PTR_ARRAY_STEAL_FULL (&edits),
+ NULL,
+ ide_source_view_rename_edits_applied,
+ g_steal_pointer (&self));
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_rename_activate (IdeSourceView *self,
+ const gchar *text,
+ DzlSimplePopover *popover)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autoptr(IdeLocation) location = NULL;
+ IdeRenameProvider *provider;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (text != NULL);
+ g_assert (DZL_IS_SIMPLE_POPOVER (popover));
+
+ if (NULL == (provider = ide_buffer_get_rename_provider (priv->buffer)))
+ IDE_EXIT;
+
+ location = ide_buffer_get_insert_location (priv->buffer);
+
+ ide_rename_provider_rename_async (provider,
+ location,
+ text,
+ NULL,
+ ide_source_view_rename_edits_cb,
+ g_object_ref (self));
+
+ /*
+ * TODO: We should probably lock all buffers so that we can ensure
+ * that our edit points are correct by time we get called back.
+ */
+
+ gtk_popover_popdown (GTK_POPOVER (popover));
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_begin_rename (IdeSourceView *self)
+{
+ IdeRenameProvider *provider;
+ DzlSimplePopover *popover;
+ g_autofree gchar *title = NULL;
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ GtkTextIter word_start;
+ GtkTextIter word_end;
+ g_autofree gchar *symbol = NULL;
+ GdkRectangle loc;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ provider = ide_buffer_get_rename_provider (IDE_BUFFER (buffer));
+
+ if (provider == NULL)
+ {
+ g_message ("Cannot rename, operation requires an IdeRenameProvider");
+ return;
+ }
+
+ insert = gtk_text_buffer_get_insert (buffer);
+ title = ide_buffer_dup_title (IDE_BUFFER (buffer));
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+
+ g_debug ("Renaming symbol from %s +%d:%d",
+ title,
+ gtk_text_iter_get_line (&iter) + 1,
+ gtk_text_iter_get_line_offset (&iter) + 1);
+
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &loc);
+ gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (self),
+ GTK_TEXT_WINDOW_WIDGET,
+ loc.x, loc.y, &loc.x, &loc.y);
+
+ // get symbol to rename (for the popup)
+ if (gtk_text_iter_inside_word (&iter) && !gtk_text_iter_starts_word (&iter))
+ {
+ word_start = iter;
+ word_end = iter;
+ gtk_text_iter_backward_word_start (&word_start);
+ gtk_text_iter_forward_word_end (&word_end);
+ }
+ else if (gtk_text_iter_starts_word (&iter))
+ {
+ word_start = iter;
+ word_end = iter;
+ gtk_text_iter_forward_word_end (&word_end);
+ }
+ else if (gtk_text_iter_ends_word (&iter))
+ {
+ word_start = iter;
+ word_end = iter;
+ gtk_text_iter_backward_word_start (&word_start);
+ }
+
+ symbol = gtk_text_iter_get_text (&word_start, &word_end);
+
+ popover = g_object_new (DZL_TYPE_SIMPLE_POPOVER,
+ "title", _("Rename symbol"),
+ "button-text", _("Rename"),
+ "text", symbol,
+ "relative-to", self,
+ "pointing-to", &loc,
+ NULL);
+
+ g_signal_connect_object (popover,
+ "changed",
+ G_CALLBACK (ide_source_view_rename_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (popover,
+ "activate",
+ G_CALLBACK (ide_source_view_rename_activate),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_popover_popup (GTK_POPOVER (popover));
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_format_selection_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeBuffer *buffer = (IdeBuffer *)object;
+ g_autoptr(IdeSourceView) self = user_data;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!ide_buffer_format_selection_finish (buffer, result, &error))
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ g_warning ("%s", error->message);
+ }
+
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (self), TRUE);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_format_selection (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autoptr(IdeFormatterOptions) options = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ options = ide_formatter_options_new ();
+ ide_formatter_options_set_tab_width (options,
+ gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (self)));
+ ide_formatter_options_set_insert_spaces (options,
+ gtk_source_view_get_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (self)));
+
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (self), FALSE);
+ ide_buffer_format_selection_async (priv->buffer,
+ options,
+ NULL,
+ ide_source_view_format_selection_cb,
+ g_object_ref (self));
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_code_action_execute_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IDE_TRACE_MSG ("ide_source_view_code_action_execute_cb done");
+}
+
+static void
+execute_code_action_cb (IdeCodeAction *code_action)
+{
+ ide_code_action_execute_async(code_action,
+ NULL,
+ ide_source_view_code_action_execute_cb,
+ g_object_ref (code_action));
+}
+
+static void
+popup_menu_detach (GtkWidget *attach_widget,
+ GtkMenu *menu)
+{
+}
+
+static void
+ide_source_view_code_action_query_cb(GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeBuffer *buffer = (IdeBuffer *)object;
+
+ g_autoptr(IdeSourceView) self = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) code_actions = NULL;
+
+ IDE_ENTRY;
+
+ g_assert(IDE_IS_SOURCE_VIEW(self));
+ g_assert(G_IS_ASYNC_RESULT(result));
+
+ code_actions = ide_buffer_code_action_query_finish(buffer, result, &error);
+
+ if (!code_actions)
+ {
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ g_warning("%s", error->message);
+ IDE_EXIT;
+ }
+
+ if (code_actions->len)
+ {
+ IdeContext* context;
+ GtkTextView* text_view;
+ GtkTextIter iter;
+ GdkRectangle iter_location;
+ GdkRectangle visible_rect;
+ gboolean is_visible;
+ GtkWidget* popup_menu;
+
+ context = ide_buffer_ref_context(buffer);
+
+ popup_menu = gtk_menu_new();
+
+ gtk_style_context_add_class(gtk_widget_get_style_context(popup_menu),
+ GTK_STYLE_CLASS_CONTEXT_MENU);
+
+
+ gtk_menu_attach_to_widget(GTK_MENU(popup_menu),
+ GTK_WIDGET(self),
+ popup_menu_detach);
+
+ for (gsize i = 0; i < code_actions->len; i++)
+ {
+ IdeCodeAction* code_action;
+ GtkWidget* menu_item;
+
+ code_action = g_ptr_array_index(code_actions, i);
+ ide_object_append(IDE_OBJECT(context), IDE_OBJECT(code_action));
+ menu_item = gtk_menu_item_new_with_label(ide_code_action_get_title(code_action));
+
+ g_signal_connect_swapped(menu_item,
+ "activate",
+ G_CALLBACK(execute_code_action_cb),
+ code_action);
+
+
+ gtk_widget_show(menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), menu_item);
+ }
+
+ gtk_menu_shell_select_first(GTK_MENU_SHELL(popup_menu), FALSE);
+
+ text_view = GTK_TEXT_VIEW(self);
+
+ gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(text_view),
+ &iter,
+ gtk_text_buffer_get_insert(gtk_text_view_get_buffer(text_view)));
+
+ gtk_text_view_get_iter_location(text_view, &iter, &iter_location);
+ gtk_text_view_get_visible_rect(text_view, &visible_rect);
+
+ is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
+ iter_location.x < visible_rect.x + visible_rect.width &&
+ iter_location.y + iter_location.height > visible_rect.y &&
+ iter_location.y < visible_rect.y + visible_rect.height);
+
+ if (is_visible)
+ {
+ gtk_text_view_buffer_to_window_coords(text_view,
+ GTK_TEXT_WINDOW_WIDGET,
+ iter_location.x,
+ iter_location.y,
+ &iter_location.x,
+ &iter_location.y);
+
+ gtk_menu_popup_at_rect(GTK_MENU(popup_menu),
+ gtk_widget_get_window(GTK_WIDGET(text_view)),
+ &iter_location,
+ GDK_GRAVITY_SOUTH_EAST,
+ GDK_GRAVITY_NORTH_WEST,
+ NULL);
+ }
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_code_action_query (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ ide_buffer_code_action_query_async (priv->buffer,
+ NULL,
+ ide_source_view_code_action_query_cb,
+ g_object_ref (self));
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_find_references_jump (IdeSourceView *self,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ IdeLocation *location;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_LIST_BOX_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ location = g_object_get_data (G_OBJECT (row), "IDE_LOCATION");
+
+ if (location != NULL)
+ g_signal_emit (self, signals [FOCUS_LOCATION], 0, location);
+
+ IDE_EXIT;
+}
+
+static gboolean
+insert_mark_within_range (IdeBuffer *buffer,
+ IdeRange *range)
+{
+ GtkTextMark *insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+ IdeLocation *begin = ide_range_get_begin (range);
+ IdeLocation *end = ide_range_get_end (range);
+ GtkTextIter iter;
+ GtkTextIter begin_iter;
+ GtkTextIter end_iter;
+
+ if (!begin || !end)
+ return FALSE;
+
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, insert);
+ ide_buffer_get_iter_at_location (buffer, &begin_iter, begin);
+ ide_buffer_get_iter_at_location (buffer, &end_iter, end);
+
+ return gtk_text_iter_compare (&begin_iter, &iter) <= 0 &&
+ gtk_text_iter_compare (&end_iter, &iter) >= 0;
+
+}
+
+static void
+ide_source_view_find_references_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSymbolResolver *symbol_resolver = (IdeSymbolResolver *)object;
+ g_autoptr(GPtrArray) references = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(IdeTask) task = user_data;
+ FindReferencesTaskData *data;
+ IdeSourceView *self;
+ IdeSourceViewPrivate *priv;
+ GtkScrolledWindow *scroller;
+ GtkPopover *popover;
+ GtkListBox *list_box;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ GdkRectangle loc;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SYMBOL_RESOLVER (symbol_resolver));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ references = ide_symbol_resolver_find_references_finish (symbol_resolver, result, &error);
+
+ IDE_PTR_ARRAY_SET_FREE_FUNC (references, g_object_unref);
+
+ self = ide_task_get_source_object (task);
+ priv = ide_source_view_get_instance_private (self);
+ data = ide_task_get_task_data (task);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (data != NULL);
+ g_assert (data->resolvers != NULL);
+ g_assert (data->resolvers->len > 0);
+
+ g_ptr_array_remove_index (data->resolvers, data->resolvers->len - 1);
+
+ /* If references are not found and symbol resolvers are left try those */
+ if (references == NULL && data->resolvers->len > 0)
+ {
+ GCancellable *cancellable;
+ IdeSymbolResolver *resolver;
+
+ cancellable = ide_task_get_cancellable (task);
+ resolver = g_ptr_array_index (data->resolvers, data->resolvers->len - 1);
+
+ ide_symbol_resolver_find_references_async (resolver,
+ data->location,
+ ide_buffer_get_language_id (priv->buffer),
+ cancellable,
+ ide_source_view_find_references_cb,
+ g_steal_pointer (&task));
+ return;
+ }
+
+ /* Ignore popover if we are no longer visible or not top-most */
+ if (!gtk_widget_get_visible (GTK_WIDGET (self)) ||
+ !gtk_widget_get_child_visible (GTK_WIDGET (self)))
+ IDE_EXIT;
+
+ insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->buffer));
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &iter, insert);
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (priv->buffer), &iter, &iter);
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &loc);
+ gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (self),
+ GTK_TEXT_WINDOW_WIDGET,
+ loc.x, loc.y, &loc.x, &loc.y);
+
+ popover = g_object_new (GTK_TYPE_POPOVER,
+ "modal", TRUE,
+ "position", GTK_POS_TOP,
+ "relative-to", self,
+ "pointing-to", &loc,
+ NULL);
+
+ scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "min-content-height", 35,
+ "max-content-height", 200,
+ "propagate-natural-height", TRUE,
+ "propagate-natural-width", TRUE,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (popover), GTK_WIDGET (scroller));
+
+ list_box = g_object_new (GTK_TYPE_LIST_BOX,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (list_box));
+
+ if (references != NULL && references->len > 0)
+ {
+ g_autoptr(IdeContext) context = ide_buffer_ref_context (priv->buffer);
+ g_autoptr(GFile) workdir = ide_context_ref_workdir (context);
+
+ for (guint i = 0; i < references->len; i++)
+ {
+ IdeRange *range = g_ptr_array_index (references, i);
+ IdeLocation *begin = ide_range_get_begin (range);
+ GFile *file = ide_location_get_file (begin);
+ guint line = ide_location_get_line (begin);
+ guint line_offset = ide_location_get_line_offset (begin);
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *text = NULL;
+ GtkListBoxRow *row;
+ GtkLabel *label;
+
+ if (g_file_has_prefix (file, workdir))
+ name = g_file_get_relative_path (workdir, file);
+ else if (g_file_is_native (file))
+ name = g_file_get_path (file);
+ else
+ name = g_file_get_uri (file);
+
+ /* translators: %s is the filename, then line number, column number. <> are pango markup */
+ text = g_strdup_printf (_("<b>%s</b> — <small>Line %u, Column %u</small>"),
+ name, line + 1, line_offset + 1);
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "xalign", 0.0f,
+ "label", text,
+ "use-markup", TRUE,
+ "visible", TRUE,
+ NULL);
+ row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+ "child", label,
+ "visible", TRUE,
+ NULL);
+ g_object_set_data_full (G_OBJECT (row),
+ "IDE_LOCATION",
+ g_object_ref (begin),
+ g_object_unref);
+ gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (row));
+
+ if (insert_mark_within_range (priv->buffer, range))
+ gtk_list_box_select_row (list_box, row);
+ }
+ }
+ else
+ {
+ GtkLabel *label = g_object_new (GTK_TYPE_LABEL,
+ "label", _("No references were found"),
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (label));
+ }
+
+ g_signal_connect_object (list_box,
+ "row-activated",
+ G_CALLBACK (ide_source_view_real_find_references_jump),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_popover_popup (popover);
+
+ g_signal_connect (popover, "hide", G_CALLBACK (gtk_widget_destroy), NULL);
+
+ ide_task_return_boolean (task, TRUE);
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_find_references (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) resolvers = NULL;
+ FindReferencesTaskData *data;
+ IdeSymbolResolver *resolver;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ task = ide_task_new (self, NULL, NULL, NULL);
+ ide_task_set_source_tag (task, ide_source_view_real_find_references);
+
+ resolvers = ide_buffer_get_symbol_resolvers (priv->buffer);
+ IDE_PTR_ARRAY_SET_FREE_FUNC (resolvers, g_object_unref);
+
+ if (resolvers->len == 0)
+ {
+ g_debug ("No symbol resolver is available");
+ IDE_EXIT;
+ }
+
+ data = g_slice_new0 (FindReferencesTaskData);
+ data->resolvers = g_steal_pointer (&resolvers);
+ data->location = ide_buffer_get_insert_location (priv->buffer);
+ ide_task_set_task_data (task, data, find_references_task_data_free);
+
+ resolver = g_ptr_array_index (data->resolvers, data->resolvers->len - 1);
+
+ /* Try each symbol resolver one by one to find references. */
+ ide_symbol_resolver_find_references_async (resolver,
+ data->location,
+ ide_buffer_get_language_id (priv->buffer),
+ NULL,
+ ide_source_view_find_references_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+static void
+ide_source_view_real_request_documentation (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextIter iter;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ if (priv->buffer == NULL)
+ return;
+
+ ide_buffer_get_selection_bounds (priv->buffer, &iter, NULL);
+
+ _ide_hover_display (priv->hover, &iter);
+}
+
+static void
+ide_source_view_real_undo (GtkSourceView *view)
+{
+ IdeSourceView *self = (IdeSourceView *)view;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /*
+ * Disable things that could confuse undo. For example, we need to bail on
+ * any in-flight snippets because they just can't deal with the buffer
+ * changes correctly given the GtkTextMark vs run-length design.
+ */
+ ide_source_view_clear_snippets (self);
+
+ GTK_SOURCE_VIEW_CLASS (ide_source_view_parent_class)->undo (view);
+}
+
+static void
+ide_source_view_real_reset (IdeSourceView *self)
+{
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ g_signal_emit (self, signals [CLEAR_SEARCH], 0);
+ g_signal_emit (self, signals [CLEAR_MODIFIER], 0);
+ g_signal_emit (self, signals [CLEAR_SELECTION], 0);
+ g_signal_emit (self, signals [CLEAR_COUNT], 0);
+ g_signal_emit (self, signals [CLEAR_SNIPPETS], 0);
+ g_signal_emit (self, signals [HIDE_COMPLETION], 0);
+ g_signal_emit (self, signals [REMOVE_CURSORS], 0);
+ g_signal_emit (self, signals [SET_MODE], 0, NULL, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
+}
+
+static void
+ide_source_view_destroy (GtkWidget *widget)
+{
+ IdeSourceView *self = (IdeSourceView *)widget;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /* Ensure we release the buffer immediately */
+ if (priv->buffer_signals != NULL)
+ ide_signal_group_set_target (priv->buffer_signals, NULL);
+
+ GTK_WIDGET_CLASS (ide_source_view_parent_class)->destroy (widget);
+}
+
+static void
+ide_source_view_dispose (GObject *object)
+{
+ IdeSourceView *self = (IdeSourceView *)object;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ if (priv->hadj_animation)
+ {
+ dzl_animation_stop (priv->hadj_animation);
+ g_clear_weak_pointer (&priv->hadj_animation);
+ }
+
+ if (priv->vadj_animation)
+ {
+ dzl_animation_stop (priv->vadj_animation);
+ g_clear_weak_pointer (&priv->vadj_animation);
+ }
+
+ ide_source_view_clear_snippets (self);
+
+ g_clear_handle_id (&priv->delay_size_allocate_chainup, g_source_remove);
+
+ g_clear_object (&priv->hover);
+ g_clear_object (&priv->completion);
+ g_clear_object (&priv->capture);
+ ide_clear_and_destroy_object (&priv->indenter_adapter);
+ g_clear_object (&priv->css_provider);
+ g_clear_object (&priv->mode);
+ g_clear_object (&priv->buffer_signals);
+ g_clear_object (&priv->file_setting_bindings);
+ g_clear_object (&priv->gutter);
+
+ if (priv->command_str != NULL)
+ {
+ g_string_free (priv->command_str, TRUE);
+ priv->command_str = NULL;
+ }
+
+ G_OBJECT_CLASS (ide_source_view_parent_class)->dispose (object);
+}
+
+static void
+ide_source_view_finalize (GObject *object)
+{
+ IdeSourceView *self = (IdeSourceView *)object;
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_clear_pointer (&priv->display_name, g_free);
+ g_clear_pointer (&priv->font_desc, pango_font_description_free);
+ g_clear_pointer (&priv->selections, g_queue_free);
+ g_clear_pointer (&priv->snippets, g_queue_free);
+ g_clear_pointer (&priv->include_regex, g_regex_unref);
+
+ G_OBJECT_CLASS (ide_source_view_parent_class)->finalize (object);
+}
+
+static void
+ide_source_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeSourceView *self = IDE_SOURCE_VIEW (object);
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_AUTO_INDENT:
+ g_value_set_boolean (value, priv->auto_indent);
+ break;
+
+ case PROP_COMPLETION_N_ROWS:
+ g_value_set_uint (value, ide_completion_get_n_rows (priv->completion));
+ break;
+
+ case PROP_COUNT:
+ g_value_set_int (value, ide_source_view_get_count (self));
+ break;
+
+ case PROP_FILE_SETTINGS:
+ g_value_set_object (value, ide_source_view_get_file_settings (self));
+ break;
+
+ case PROP_FONT_DESC:
+ g_value_set_boxed (value, ide_source_view_get_font_desc (self));
+ break;
+
+ case PROP_HIGHLIGHT_CURRENT_LINE:
+ g_value_set_boolean (value, ide_source_view_get_highlight_current_line (self));
+ break;
+
+ case PROP_INDENTER:
+ g_value_set_object (value, ide_source_view_get_indenter (self));
+ break;
+
+ case PROP_INSERT_MATCHING_BRACE:
+ g_value_set_boolean (value, ide_source_view_get_insert_matching_brace (self));
+ break;
+
+ case PROP_INTERACTIVE_COMPLETION:
+ g_value_set_boolean (value, priv->interactive_completion);
+ break;
+
+ case PROP_MODE_DISPLAY_NAME:
+ g_value_set_string (value, ide_source_view_get_mode_display_name (self));
+ break;
+
+ case PROP_OVERWRITE:
+ g_value_set_boolean (value, ide_source_view_get_overwrite (self));
+ break;
+
+ case PROP_OVERWRITE_BRACES:
+ g_value_set_boolean (value, ide_source_view_get_overwrite_braces (self));
+ break;
+
+ case PROP_SCROLL_OFFSET:
+ g_value_set_uint (value, ide_source_view_get_scroll_offset (self));
+ break;
+
+ case PROP_SHOW_GRID_LINES:
+ g_value_set_boolean (value, ide_source_view_get_show_grid_lines (self));
+ break;
+
+ case PROP_SHOW_LINE_CHANGES:
+ g_value_set_boolean (value, ide_source_view_get_show_line_changes (self));
+ break;
+
+ case PROP_SHOW_LINE_DIAGNOSTICS:
+ g_value_set_boolean (value, ide_source_view_get_show_line_diagnostics (self));
+ break;
+
+ case PROP_SHOW_LINE_NUMBERS:
+ g_value_set_boolean (value, ide_source_view_get_show_line_numbers (self));
+ break;
+
+ case PROP_SHOW_RELATIVE_LINE_NUMBERS:
+ g_value_set_boolean (value, ide_source_view_get_show_relative_line_numbers (self));
+ break;
+
+ case PROP_OVERSCROLL:
+ g_value_set_int (value, priv->overscroll_num_lines);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_source_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeSourceView *self = IDE_SOURCE_VIEW (object);
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_AUTO_INDENT:
+ priv->auto_indent = !!g_value_get_boolean (value);
+ ide_source_view_update_auto_indent_override (self);
+ break;
+
+ case PROP_COMPLETION_N_ROWS:
+ if (priv->completion != NULL)
+ ide_completion_set_n_rows (priv->completion, g_value_get_uint (value));
+ break;
+
+ case PROP_COUNT:
+ ide_source_view_set_count (self, g_value_get_int (value));
+ break;
+
+ case PROP_FONT_NAME:
+ ide_source_view_set_font_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_FONT_DESC:
+ ide_source_view_set_font_desc (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_INDENT_STYLE:
+ ide_source_view_set_indent_style (self, g_value_get_enum (value));
+ break;
+
+ case PROP_INSERT_MATCHING_BRACE:
+ ide_source_view_set_insert_matching_brace (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_INTERACTIVE_COMPLETION:
+ ide_source_view_set_interactive_completion (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_OVERWRITE:
+ gtk_text_view_set_overwrite (GTK_TEXT_VIEW (self), g_value_get_boolean (value));
+ break;
+
+ case PROP_OVERWRITE_BRACES:
+ ide_source_view_set_overwrite_braces (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SCROLL_OFFSET:
+ ide_source_view_set_scroll_offset (self, g_value_get_uint (value));
+ break;
+
+ case PROP_SHOW_GRID_LINES:
+ ide_source_view_set_show_grid_lines (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_LINE_CHANGES:
+ ide_source_view_set_show_line_changes (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_LINE_DIAGNOSTICS:
+ ide_source_view_set_show_line_diagnostics (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_LINE_NUMBERS:
+ ide_source_view_set_show_line_numbers (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_RELATIVE_LINE_NUMBERS:
+ ide_source_view_set_show_relative_line_numbers (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_OVERSCROLL:
+ ide_source_view_set_overscroll_num_lines (self, g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_source_view_class_init (IdeSourceViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass);
+ GtkSourceViewClass *gsv_class = GTK_SOURCE_VIEW_CLASS (klass);
+ GtkBindingSet *binding_set;
+
+ object_class->constructed = ide_source_view_constructed;
+ object_class->dispose = ide_source_view_dispose;
+ object_class->finalize = ide_source_view_finalize;
+ object_class->get_property = ide_source_view_get_property;
+ object_class->set_property = ide_source_view_set_property;
+
+ widget_class->button_press_event = ide_source_view_real_button_press_event;
+ widget_class->button_release_event = ide_source_view_real_button_release_event;
+ widget_class->motion_notify_event = ide_source_view_real_motion_notify_event;
+ widget_class->focus_in_event = ide_source_view_focus_in_event;
+ widget_class->focus_out_event = ide_source_view_focus_out_event;
+ widget_class->key_press_event = ide_source_view_key_press_event;
+ widget_class->key_release_event = ide_source_view_key_release_event;
+ widget_class->scroll_event = ide_source_view_scroll_event;
+ widget_class->size_allocate = ide_source_view_size_allocate;
+ widget_class->style_updated = ide_source_view_real_style_updated;
+ widget_class->destroy = ide_source_view_destroy;
+
+ text_view_class->delete_from_cursor = ide_source_view_real_delete_from_cursor;
+ text_view_class->draw_layer = ide_source_view_real_draw_layer;
+ text_view_class->insert_at_cursor = ide_source_view_real_insert_at_cursor;
+ text_view_class->populate_popup = ide_source_view_real_populate_popup;
+
+ gsv_class->undo = ide_source_view_real_undo;
+
+ klass->add_cursor = ide_source_view_real_add_cursor;
+ klass->remove_cursors = ide_source_view_real_remove_cursors;
+ klass->append_to_count = ide_source_view_real_append_to_count;
+ klass->begin_macro = ide_source_view_real_begin_macro;
+ klass->begin_rename = ide_source_view_real_begin_rename;
+ klass->capture_modifier = ide_source_view_real_capture_modifier;
+ klass->clear_count = ide_source_view_real_clear_count;
+ klass->clear_modifier = ide_source_view_real_clear_modifier;
+ klass->clear_selection = ide_source_view_real_clear_selection;
+ klass->clear_snippets = ide_source_view_clear_snippets;
+ klass->copy_clipboard_extended = ide_source_view_real_copy_clipboard_extended;
+ klass->cycle_completion = ide_source_view_real_cycle_completion;
+ klass->decrease_font_size = ide_source_view_real_decrease_font_size;
+ klass->delete_selection = ide_source_view_real_delete_selection;
+ klass->end_macro = ide_source_view_real_end_macro;
+ klass->goto_definition = ide_source_view_real_goto_definition;
+ klass->hide_completion = ide_source_view_real_hide_completion;
+ klass->increase_font_size = ide_source_view_real_increase_font_size;
+ klass->indent_selection = ide_source_view_real_indent_selection;
+ klass->insert_modifier = ide_source_view_real_insert_modifier;
+ klass->move_error = ide_source_view_real_move_error;
+ klass->movement = ide_source_view_real_movement;
+ klass->paste_clipboard_extended = ide_source_view_real_paste_clipboard_extended;
+ klass->pop_selection = ide_source_view_real_pop_selection;
+ klass->push_selection = ide_source_view_real_push_selection;
+ klass->rebuild_highlight = ide_source_view_real_rebuild_highlight;
+ klass->replay_macro = ide_source_view_real_replay_macro;
+ klass->request_documentation = ide_source_view_real_request_documentation;
+ klass->reset_font_size = ide_source_view_real_reset_font_size;
+ klass->restore_insert_mark = ide_source_view_real_restore_insert_mark;
+ klass->save_command = ide_source_view_real_save_command;
+ klass->save_insert_mark = ide_source_view_real_save_insert_mark;
+ klass->save_search_char = ide_source_view_real_save_search_char;
+ klass->select_inner = ide_source_view_real_select_inner;
+ klass->select_tag = ide_source_view_real_select_tag;
+ klass->selection_theatric = ide_source_view_real_selection_theatric;
+ klass->set_mode = ide_source_view_real_set_mode;
+ klass->set_overwrite = ide_source_view_real_set_overwrite;
+ klass->sort = ide_source_view_real_sort;
+ klass->swap_selection_bounds = ide_source_view_real_swap_selection_bounds;
+
+ g_object_class_override_property (object_class, PROP_AUTO_INDENT, "auto-indent");
+
+ properties [PROP_COMPLETION_N_ROWS] =
+ g_param_spec_uint ("completion-n-rows",
+ "Completion N Rows",
+ "The number of completion rows to display to the user",
+ 1, 32, 5,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_COUNT] =
+ g_param_spec_int ("count",
+ "Count",
+ "The count for movements.",
+ -1,
+ G_MAXINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FILE_SETTINGS] =
+ g_param_spec_object ("file-settings",
+ "File Settings",
+ "The file settings that have been loaded for the file.",
+ IDE_TYPE_FILE_SETTINGS,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FONT_DESC] =
+ g_param_spec_boxed ("font-desc",
+ "Font Description",
+ "The Pango font description to use for rendering source.",
+ PANGO_TYPE_FONT_DESCRIPTION,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FONT_NAME] =
+ g_param_spec_string ("font-name",
+ "Font Name",
+ "The Pango font name to use for rendering source.",
+ "Monospace",
+ (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (object_class,
+ PROP_HIGHLIGHT_CURRENT_LINE,
+ "highlight-current-line");
+
+ properties [PROP_INDENTER] =
+ g_param_spec_object ("indenter",
+ "Indenter",
+ "Indenter",
+ IDE_TYPE_INDENTER,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_INDENT_STYLE] =
+ g_param_spec_enum ("indent-style",
+ "Indent Style",
+ "Indent Style",
+ IDE_TYPE_INDENT_STYLE,
+ IDE_INDENT_STYLE_TABS,
+ (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_INTERACTIVE_COMPLETION] =
+ g_param_spec_boolean ("interactive-completion",
+ "Interactive Completion",
+ "If completion should be completed interactively",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_INSERT_MATCHING_BRACE] =
+ g_param_spec_boolean ("insert-matching-brace",
+ "Insert Matching Brace",
+ "Insert a matching brace/bracket/quotation/parenthesis.",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (object_class, PROP_OVERWRITE, "overwrite");
+
+ properties [PROP_MODE_DISPLAY_NAME] =
+ g_param_spec_string ("mode-display-name",
+ "Mode Display Name",
+ "The display name of the keybinding mode.",
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_OVERWRITE_BRACES] =
+ g_param_spec_boolean ("overwrite-braces",
+ "Overwrite Braces",
+ "Overwrite a matching brace/bracket/quotation/parenthesis.",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SCROLL_OFFSET] =
+ g_param_spec_uint ("scroll-offset",
+ "Scroll Offset",
+ "The number of lines between the insertion cursor and screen boundary.",
+ 0,
+ G_MAXUINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SHOW_GRID_LINES] =
+ g_param_spec_boolean ("show-grid-lines",
+ "Show Grid Lines",
+ "If the background grid should be shown.",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SHOW_LINE_CHANGES] =
+ g_param_spec_boolean ("show-line-changes",
+ "Show Line Changes",
+ "If line changes should be shown in the left gutter.",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * IdeSourceView:show-line-diagnostics:
+ *
+ * If the diagnostics gutter should be visible.
+ *
+ * This also requires that IdeBuffer:highlight-diagnostics is set to %TRUE
+ * to generate diagnostics.
+ *
+ * Since: 3.32
+ */
+ properties [PROP_SHOW_LINE_DIAGNOSTICS] =
+ g_param_spec_boolean ("show-line-diagnostics",
+ "Show Line Diagnostics",
+ "If line changes diagnostics should be shown in the left gutter.",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_override_property (object_class, PROP_SHOW_LINE_NUMBERS, "show-line-numbers");
+
+ properties [PROP_SHOW_RELATIVE_LINE_NUMBERS] =
+ g_param_spec_boolean ("show-relative-line-numbers",
+ "Show Relative Line Numbers",
+ "Show line numbers relative to the cursor line",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_OVERSCROLL] =
+ g_param_spec_int ("overscroll",
+ "Overscroll",
+ "The number of lines to scroll beyond the end of the "
+ "buffer. A negative number of lines will scroll until "
+ "only that number of lines is visible",
+ G_MININT,
+ G_MAXINT,
+ DEFAULT_OVERSCROLL_NUM_LINES,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ signals [ACTION] =
+ g_signal_new_class_handler ("action",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (dzl_gtk_widget_action_with_string),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ signals [APPEND_TO_COUNT] =
+ g_signal_new ("append-to-count",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, append_to_count),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ /**
+ * IdeSourceView::begin-macro:
+ *
+ * This signal will begin recording input to the #IdeSourceView. This includes the current
+ * #IdeSourceViewMode, #IdeSourceView:count and #IdeSourceView:modifier which will be used
+ * to replay the sequence starting from the correct state.
+ *
+ * Pair this with an emission of #IdeSourceView::end-macro to complete the sequence.
+ *
+ * Since: 3.32
+ */
+ signals [BEGIN_MACRO] =
+ g_signal_new ("begin-macro",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, begin_macro),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView::begin-rename:
+ *
+ * This signal is emitted when the source view should begin a rename
+ * operation using the #IdeRenameProvider from the underlying buffer. The
+ * cursor position will be used as the location when sending the request to
+ * the provider.
+ *
+ * Since: 3.32
+ */
+ signals [BEGIN_RENAME] =
+ g_signal_new ("begin-rename",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, begin_rename),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ signals [BEGIN_USER_ACTION] =
+ g_signal_new_class_handler ("begin-user-action",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_begin_user_action),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [SAVE_COMMAND] =
+ g_signal_new ("save-command",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, save_command),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals [SAVE_SEARCH_CHAR] =
+ g_signal_new ("save-search-char",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, save_search_char),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView::capture-modifier:
+ *
+ * This signal will block the main loop in a similar fashion to how
+ * gtk_dialog_run() performs until a key-press has occurred that can be
+ * captured for use in movements.
+ *
+ * Pressing Escape or unfocusing the widget will break from this loop.
+ *
+ * Use of this signal is not recommended except in very specific cases.
+ *
+ * Since: 3.32
+ */
+ signals [CAPTURE_MODIFIER] =
+ g_signal_new ("capture-modifier",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, capture_modifier),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ g_signal_override_class_handler ("change-case",
+ G_TYPE_FROM_CLASS (klass),
+ G_CALLBACK (ide_source_view_real_change_case));
+
+ signals [CLEAR_COUNT] =
+ g_signal_new ("clear-count",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, clear_count),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CLEAR_MODIFIER] =
+ g_signal_new ("clear-modifier",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, clear_modifier),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CLEAR_SEARCH] =
+ g_signal_new ("clear-search",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, clear_search),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CLEAR_SELECTION] =
+ g_signal_new ("clear-selection",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, clear_selection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CLEAR_SNIPPETS] =
+ g_signal_new ("clear-snippets",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, clear_snippets),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [COPY_CLIPBOARD_EXTENDED] =
+ g_signal_new ("copy-clipboard-extended",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, copy_clipboard_extended),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CYCLE_COMPLETION] =
+ g_signal_new ("cycle-completion",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, cycle_completion),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GTK_TYPE_DIRECTION_TYPE);
+
+ signals [DECREASE_FONT_SIZE] =
+ g_signal_new ("decrease-font-size",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, decrease_font_size),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [DELETE_SELECTION] =
+ g_signal_new ("delete-selection",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, delete_selection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [DRAW_BUBBLES] =
+ g_signal_new ("draw-bubbles",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ CAIRO_GOBJECT_TYPE_CONTEXT);
+ g_signal_set_va_marshaller (signals [DRAW_BUBBLES],
+ G_TYPE_FROM_CLASS (klass),
+ g_cclosure_marshal_VOID__BOXEDv);
+
+ /**
+ * IdeSourceView::end-macro:
+ *
+ * You should call #IdeSourceView::begin-macro before emitting this signal.
+ *
+ * Complete a macro recording sequence. This may be called more times than is necessary,
+ * since #IdeSourceView will only keep the most recent macro recording. This can be
+ * helpful when implementing recording sequences such as in Vim.
+ *
+ * Since: 3.32
+ */
+ signals [END_MACRO] =
+ g_signal_new ("end-macro",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, end_macro),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [END_USER_ACTION] =
+ g_signal_new_class_handler ("end-user-action",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_end_user_action),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [FIND_REFERENCES] =
+ g_signal_new_class_handler ("find-references",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_real_find_references),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ signals [FOCUS_LOCATION] =
+ g_signal_new ("focus-location",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdeSourceViewClass, focus_location),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ IDE_TYPE_LOCATION);
+
+ signals [FORMAT_SELECTION] =
+ g_signal_new_class_handler ("format-selection",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_real_format_selection),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ signals [QUERY_CODE_ACTION] =
+ g_signal_new_class_handler ("query-code-action",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_real_code_action_query),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ signals [GOTO_DEFINITION] =
+ g_signal_new ("goto-definition",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, goto_definition),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [HIDE_COMPLETION] =
+ g_signal_new ("hide-completion",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, hide_completion),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [INCREASE_FONT_SIZE] =
+ g_signal_new ("increase-font-size",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, increase_font_size),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [INDENT_SELECTION] =
+ g_signal_new ("indent-selection",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, indent_selection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ /**
+ * IdeSourceView::insert-modifier:
+ * @self: An #IdeSourceView
+ * @use_count: If the count property should be used to repeat.
+ *
+ * Inserts the current modifier character at the insert mark in the buffer.
+ * If @use_count is %TRUE, then the character will be inserted
+ * #IdeSourceView:count times.
+ *
+ * Since: 3.32
+ */
+ signals [INSERT_MODIFIER] =
+ g_signal_new ("insert-modifier",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, insert_modifier),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+
+ g_signal_override_class_handler ("join-lines",
+ G_TYPE_FROM_CLASS (klass),
+ G_CALLBACK (ide_source_view_real_join_lines));
+
+ signals [JUMP] =
+ g_signal_new ("jump",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdeSourceViewClass, jump),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ signals [MOVEMENT] =
+ g_signal_new ("movement",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, movement),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 4,
+ IDE_TYPE_SOURCE_VIEW_MOVEMENT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * IdeSourceView::move-error:
+ * @self: An #IdeSourceView.
+ * @dir: The direction to move.
+ *
+ * Moves to the next search result either forwards or backwards.
+ *
+ * Since: 3.32
+ */
+ signals [MOVE_ERROR] =
+ g_signal_new ("move-error",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, move_error),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GTK_TYPE_DIRECTION_TYPE);
+
+ signals [MOVE_SEARCH] =
+ g_signal_new ("move-search",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, move_search),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 6,
+ GTK_TYPE_DIRECTION_TYPE,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INT);
+
+ signals [PASTE_CLIPBOARD_EXTENDED] =
+ g_signal_new ("paste-clipboard-extended",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, paste_clipboard_extended),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * IdeSourceView::pop-selection:
+ *
+ * Reselects a previousl selected range of text that was saved using
+ * IdeSourceView::push-selection.
+ *
+ * Since: 3.32
+ */
+ signals [POP_SELECTION] =
+ g_signal_new ("pop-selection",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, pop_selection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView::pop-snippet:
+ * @self: An #IdeSourceView
+ * @snippet: An #IdeSnippet.
+ *
+ * Pops the current snippet from the sourceview if there is one.
+ *
+ * Since: 3.32
+ */
+ signals [POP_SNIPPET] =
+ g_signal_new_class_handler ("pop-snippet",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ NULL,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView::push-selection:
+ *
+ * Saves the current selection away to be restored by a call to
+ * IdeSourceView::pop-selection. You must pop the selection to keep
+ * the selection stack in consistent order.
+ *
+ * Since: 3.32
+ */
+ signals [PUSH_SELECTION] =
+ g_signal_new ("push-selection",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, push_selection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView::push-snippet:
+ * @self: An #IdeSourceView
+ * @snippet: An #IdeSnippet.
+ * @iter: (allow-none): The location for the snippet, or %NULL.
+ *
+ * Pushes @snippet onto the snippet stack at either @iter or the insertion
+ * mark if @iter is not provided.
+ *
+ * Since: 3.32
+ */
+ signals [PUSH_SNIPPET] =
+ g_signal_new_class_handler ("push-snippet",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK (ide_source_view_real_push_snippet),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ IDE_TYPE_SNIPPET,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ signals [REBUILD_HIGHLIGHT] =
+ g_signal_new ("rebuild-highlight",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, rebuild_highlight),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [DUPLICATE_ENTIRE_LINE] =
+ g_signal_new_class_handler ("duplicate-entire-line",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_real_duplicate_entire_line),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [REINDENT] =
+ g_signal_new_class_handler ("reindent",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_real_reindent),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView:replay-macro:
+ * @self: an #IdeSourceView.
+ *
+ * Replays the last series of captured events that were captured between calls
+ * to #IdeSourceView::begin-macro and #IdeSourceView::end-macro.
+ *
+ * Since: 3.32
+ */
+ signals [REPLAY_MACRO] =
+ g_signal_new ("replay-macro",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, replay_macro),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+
+ signals [REQUEST_DOCUMENTATION] =
+ g_signal_new ("request-documentation",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, request_documentation),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * IdeSourceView::reset:
+ *
+ * This is a helper signal that will try to reset keyboard input
+ * and various stateful settings of the sourceview. This is a good
+ * signal to map to the "Escape" key.
+ *
+ * Since: 3.32
+ */
+ signals [RESET] =
+ g_signal_new_class_handler ("reset",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_source_view_real_reset),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ signals [RESET_FONT_SIZE] =
+ g_signal_new ("reset-font-size",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, reset_font_size),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [RESTORE_INSERT_MARK] =
+ g_signal_new ("restore-insert-mark",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, restore_insert_mark),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [SAVE_INSERT_MARK] =
+ g_signal_new ("save-insert-mark",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, save_insert_mark),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ g_signal_override_class_handler ("select-all",
+ G_TYPE_FROM_CLASS (klass),
+ G_CALLBACK (ide_source_view_real_select_all));
+
+ signals [SELECT_INNER] =
+ g_signal_new ("select-inner",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, select_inner),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 4,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+
+ signals [SELECT_TAG] =
+ g_signal_new ("select-tag",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, select_tag),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+
+ signals [SELECTION_THEATRIC] =
+ g_signal_new ("selection-theatric",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, selection_theatric),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ IDE_TYPE_SOURCE_VIEW_THEATRIC);
+
+ signals [SET_MODE] =
+ g_signal_new ("set-mode",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, set_mode),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ IDE_TYPE_SOURCE_VIEW_MODE_TYPE);
+
+ signals [SET_OVERWRITE] =
+ g_signal_new ("set-overwrite",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, set_overwrite),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+
+ signals [SET_SEARCH_TEXT] =
+ g_signal_new ("set-search-text",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, set_search_text),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * IdeSourceView::sort:
+ * @self: an #IdeSourceView.
+ * @ignore_case: If character case should be ignored.
+ * @reverse: If the lines should be sorted in reverse order
+ *
+ * This signal is meant to be activated from keybindings to sort the currently selected lines.
+ * The lines are sorted using qsort() and either strcmp() or strcasecmp().
+ *
+ * Since: 3.32
+ */
+ signals [SORT] =
+ g_signal_new ("sort",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, sort),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+
+ signals [SWAP_SELECTION_BOUNDS] =
+ g_signal_new ("swap-selection-bounds",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, swap_selection_bounds),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ signals [ADD_CURSOR] =
+ g_signal_new ("add-cursor",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, add_cursor),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ IDE_TYPE_CURSOR_TYPE);
+
+ signals [REMOVE_CURSORS] =
+ g_signal_new ("remove-cursors",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (IdeSourceViewClass, remove_cursors),
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_r,
+ GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+ "begin-rename", 0);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_q,
+ GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+ "query-code-action", 0);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_space,
+ GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+ "find-references", 0);
+
+ /* Override "Home" and "<Shift>Home" to use our movements
+ * instead of the smart home feature of GtkSourceView.
+ */
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_Home,
+ 0,
+ "movement", 4,
+ IDE_TYPE_SOURCE_VIEW_MOVEMENT, IDE_SOURCE_VIEW_MOVEMENT_SMART_HOME,
+ G_TYPE_BOOLEAN, FALSE,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_Home,
+ GDK_MOD1_MASK | GDK_SHIFT_MASK,
+ "movement", 4,
+ IDE_TYPE_SOURCE_VIEW_MOVEMENT, IDE_SOURCE_VIEW_MOVEMENT_SMART_HOME,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_BOOLEAN, FALSE);
+
+ /* Remove Emoji from GtkTextView, we'll add back in various keybinding modes */
+ binding_set = gtk_binding_set_by_class (g_type_class_peek (GTK_TYPE_TEXT_VIEW));
+ gtk_binding_entry_remove (binding_set, GDK_KEY_period, GDK_CONTROL_MASK);
+ gtk_binding_entry_remove (binding_set, GDK_KEY_semicolon, GDK_CONTROL_MASK);
+}
+
+static void
+ide_source_view_init (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ gtk_widget_add_events (GTK_WIDGET (self), GDK_ENTER_NOTIFY_MASK);
+ gtk_widget_set_has_tooltip (GTK_WIDGET (self), FALSE);
+
+ priv->include_regex = g_regex_new (INCLUDE_STATEMENTS,
+ G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+
+ priv->show_line_numbers = TRUE;
+ priv->show_line_changes = TRUE;
+ priv->show_line_diagnostics = TRUE;
+ priv->interactive_completion = TRUE;
+ priv->target_line_column = 0;
+ priv->snippets = g_queue_new ();
+ priv->selections = g_queue_new ();
+ priv->font_scale = FONT_SCALE_NORMAL;
+ priv->command_str = g_string_sized_new (32);
+ priv->overscroll_num_lines = DEFAULT_OVERSCROLL_NUM_LINES;
+
+ priv->hover = _ide_hover_new (self);
+
+ priv->file_setting_bindings = ide_binding_group_new ();
+ ide_binding_group_bind (priv->file_setting_bindings, "auto-indent",
+ self, "auto-indent", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "indent-width",
+ self, "indent-width", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "tab-width",
+ self, "tab-width", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "right-margin-position",
+ self, "right-margin-position", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "indent-style",
+ self, "indent-style", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "show-right-margin",
+ self, "show-right-margin", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "insert-matching-brace",
+ self, "insert-matching-brace", G_BINDING_SYNC_CREATE);
+ ide_binding_group_bind (priv->file_setting_bindings, "overwrite-braces",
+ self, "overwrite-braces", G_BINDING_SYNC_CREATE);
+
+ priv->buffer_signals = ide_signal_group_new (IDE_TYPE_BUFFER);
+
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "changed",
+ G_CALLBACK (ide_source_view__buffer_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "request-scroll-to-insert",
+ G_CALLBACK (ide_source_view__buffer_request_scroll_to_insert_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "line-flags-changed",
+ G_CALLBACK (ide_source_view__buffer_line_flags_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "notify::can-redo",
+ G_CALLBACK (ide_source_view__buffer__notify_can_redo),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "notify::can-undo",
+ G_CALLBACK (ide_source_view__buffer__notify_can_undo),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "notify::file-settings",
+ G_CALLBACK (ide_source_view__buffer_notify_file_settings_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "notify::language",
+ G_CALLBACK (ide_source_view__buffer_notify_language_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "notify::style-scheme",
+ G_CALLBACK (ide_source_view__buffer_notify_style_scheme_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "insert-text",
+ G_CALLBACK (ide_source_view__buffer_insert_text_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "insert-text",
+ G_CALLBACK (ide_source_view__buffer_insert_text_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "delete-range",
+ G_CALLBACK (ide_source_view__buffer_delete_range_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "delete-range",
+ G_CALLBACK (ide_source_view__buffer_delete_range_after_cb),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "mark-set",
+ G_CALLBACK (ide_source_view__buffer_mark_set_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "loaded",
+ G_CALLBACK (ide_source_view__buffer_loaded_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_signal_group_connect_object (priv->buffer_signals,
+ "notify::has-selection",
+ G_CALLBACK (ide_source_view__buffer_notify_has_selection_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->buffer_signals,
+ "bind",
+ G_CALLBACK (ide_source_view_bind_buffer),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->buffer_signals,
+ "unbind",
+ G_CALLBACK (ide_source_view_unbind_buffer),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_object_bind_property_full (self, "buffer", priv->buffer_signals, "target", 0,
+ ignore_invalid_buffers, NULL, NULL, NULL);
+
+ dzl_widget_action_group_attach (self, "sourceview");
+}
+
+const PangoFontDescription *
+ide_source_view_get_font_desc (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
+
+ return priv->font_desc;
+}
+
+/**
+ * ide_source_view_get_scaled_font_desc:
+ * @self: a #IdeSourceView
+ *
+ * Like ide_source_view_get_font_desc() but takes the editor zoom into
+ * account. You must free the result with pango_font_description_free().
+ *
+ * Returns: (transfer full): a #PangoFontDescription
+ *
+ * Since: 3.32
+ */
+PangoFontDescription *
+ide_source_view_get_scaled_font_desc (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ PangoFontDescription *copy;
+ gdouble font_scale;
+ guint font_size;
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
+
+ copy = pango_font_description_copy (priv->font_desc);
+ font_size = pango_font_description_get_size (priv->font_desc);
+ font_scale = fontScale [priv->font_scale];
+ pango_font_description_set_size (copy, font_size * font_scale);
+
+ return g_steal_pointer (©);
+}
+
+void
+ide_source_view_set_font_desc (IdeSourceView *self,
+ const PangoFontDescription *font_desc)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ if (font_desc != priv->font_desc)
+ {
+ g_clear_pointer (&priv->font_desc, pango_font_description_free);
+
+ if (font_desc)
+ priv->font_desc = pango_font_description_copy (font_desc);
+ else
+ priv->font_desc = pango_font_description_from_string (DEFAULT_FONT_DESC);
+
+ priv->font_scale = FONT_SCALE_NORMAL;
+
+ ide_source_view_rebuild_css (self);
+ }
+}
+
+void
+ide_source_view_set_font_name (IdeSourceView *self,
+ const gchar *font_name)
+{
+ PangoFontDescription *font_desc = NULL;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ if (font_name)
+ font_desc = pango_font_description_from_string (font_name);
+ ide_source_view_set_font_desc (self, font_desc);
+ if (font_desc)
+ pango_font_description_free (font_desc);
+}
+
+gboolean
+ide_source_view_get_show_line_changes (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->show_line_changes;
+}
+
+void
+ide_source_view_set_show_line_changes (IdeSourceView *self,
+ gboolean show_line_changes)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->show_line_changes = !!show_line_changes;
+
+ if (priv->gutter)
+ {
+ ide_gutter_set_show_line_changes (priv->gutter, show_line_changes);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_LINE_CHANGES]);
+ }
+}
+
+gboolean
+ide_source_view_get_show_line_diagnostics (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->show_line_diagnostics;
+}
+
+void
+ide_source_view_set_show_line_diagnostics (IdeSourceView *self,
+ gboolean show_line_diagnostics)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->show_line_diagnostics = !!show_line_diagnostics;
+
+ if (priv->gutter)
+ {
+ ide_gutter_set_show_line_diagnostics (priv->gutter, show_line_diagnostics);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_LINE_DIAGNOSTICS]);
+ }
+}
+
+gboolean
+ide_source_view_get_show_grid_lines (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->show_grid_lines;
+}
+
+void
+ide_source_view_set_show_grid_lines (IdeSourceView *self,
+ gboolean show_grid_lines)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ show_grid_lines = !!show_grid_lines;
+
+ if (show_grid_lines != priv->show_grid_lines)
+ {
+ priv->show_grid_lines = show_grid_lines;
+ if (show_grid_lines)
+ gtk_source_view_set_background_pattern (GTK_SOURCE_VIEW (self),
+ GTK_SOURCE_BACKGROUND_PATTERN_TYPE_GRID);
+ else
+ gtk_source_view_set_background_pattern (GTK_SOURCE_VIEW (self),
+ GTK_SOURCE_BACKGROUND_PATTERN_TYPE_NONE);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_GRID_LINES]);
+ }
+}
+
+gboolean
+ide_source_view_get_insert_matching_brace (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->insert_matching_brace;
+}
+
+void
+ide_source_view_get_iter_at_visual_column (IdeSourceView *self,
+ guint column,
+ GtkTextIter *location)
+{
+ gunichar tab_char;
+ guint visual_col = 0;
+ guint tab_width;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ tab_char = g_utf8_get_char ("\t");
+ tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (self));
+ gtk_text_iter_set_line_offset (location, 0);
+
+ while (!gtk_text_iter_ends_line (location))
+ {
+ if (gtk_text_iter_get_char (location) == tab_char)
+ visual_col += (tab_width - (visual_col % tab_width));
+ else
+ ++visual_col;
+
+ if (visual_col > column)
+ break;
+
+ /* FIXME: this does not handle invisible text correctly, but
+ * * gtk_text_iter_forward_visible_cursor_position is too
+ * slow */
+ if (!gtk_text_iter_forward_char (location))
+ break;
+ }
+}
+
+gboolean
+ide_source_view_get_overwrite_braces (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->overwrite_braces;
+}
+
+void
+ide_source_view_set_insert_matching_brace (IdeSourceView *self,
+ gboolean insert_matching_brace)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ insert_matching_brace = !!insert_matching_brace;
+
+ if (insert_matching_brace != priv->insert_matching_brace)
+ {
+ priv->insert_matching_brace = insert_matching_brace;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INSERT_MATCHING_BRACE]);
+ }
+}
+
+void
+ide_source_view_set_overwrite_braces (IdeSourceView *self,
+ gboolean overwrite_braces)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ overwrite_braces = !!overwrite_braces;
+
+ if (overwrite_braces != priv->overwrite_braces)
+ {
+ priv->overwrite_braces = overwrite_braces;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_OVERWRITE_BRACES]);
+ }
+}
+
+/**
+ * ide_source_view_get_scroll_offset:
+ *
+ * Gets the #IdeSourceView:scroll-offset property. This property contains the number of lines
+ * that should be kept above or below the line containing the insertion cursor relative to the
+ * top and bottom of the visible text window.
+ *
+ * Since: 3.32
+ */
+guint
+ide_source_view_get_scroll_offset (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), 0);
+
+ return priv->scroll_offset;
+}
+
+/**
+ * ide_source_view_set_scroll_offset:
+ *
+ * Sets the #IdeSourceView:scroll-offset property. See ide_source_view_get_scroll_offset() for
+ * more information. Set to 0 to unset this property.
+ *
+ * Since: 3.32
+ */
+void
+ide_source_view_set_scroll_offset (IdeSourceView *self,
+ guint scroll_offset)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ if (scroll_offset != priv->scroll_offset)
+ {
+ priv->scroll_offset = scroll_offset;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SCROLL_OFFSET]);
+ }
+}
+
+/**
+ * ide_source_view_get_visible_rect:
+ * @self: An #IdeSourceView.
+ * @visible_rect: (out): a #GdkRectangle.
+ *
+ * Gets the visible region in buffer coordinates that is the visible area of the buffer. This
+ * is similar to gtk_text_view_get_visible_area() except that it takes into account the
+ * #IdeSourceView:scroll-offset property to ensure there is space above and below the
+ * visible_rect.
+ *
+ * Since: 3.32
+ */
+void
+ide_source_view_get_visible_rect (IdeSourceView *self,
+ GdkRectangle *visible_rect)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)self;
+ GdkRectangle area;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+ g_return_if_fail (visible_rect);
+
+ gtk_text_view_get_visible_rect (text_view, &area);
+
+ /*
+ * If we don't have valid line height, not much we can do now. We can just adjust things
+ * later once it becomes available.
+ */
+ if (priv->cached_char_height)
+ {
+ gint max_scroll_offset;
+ gint scroll_offset;
+ gint visible_lines;
+ gint scroll_offset_height;
+
+ visible_lines = area.height / priv->cached_char_height;
+ max_scroll_offset = (visible_lines - 1) / 2;
+ scroll_offset = MIN ((gint)priv->scroll_offset, max_scroll_offset);
+ scroll_offset_height = priv->cached_char_height * scroll_offset;
+
+ area.y += scroll_offset_height;
+ area.height -= (2 * scroll_offset_height);
+
+ /*
+ * If we have an even number of visible lines and scrolloffset is less than our
+ * desired scrolloffset, we need to remove an extra line so we don't have two
+ * visible lines.
+ */
+ if ((scroll_offset < (gint)priv->scroll_offset) && (visible_lines & 1) == 0)
+ area.height -= priv->cached_char_height;
+
+ /*
+ * Use a multiple of the line height so we don't jump around when
+ * focusing the last line (due to Y2 not fitting in the visible area).
+ */
+ area.height = (area.height / priv->cached_char_height) * priv->cached_char_height;
+ }
+
+ *visible_rect = area;
+}
+
+void
+ide_source_view_scroll_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark,
+ IdeSourceScrollAlign use_align,
+ gdouble alignx,
+ gdouble aligny)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GdkRectangle visible_rect;
+ GdkRectangle mark_rect;
+ GtkTextIter iter;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ ide_source_view_get_visible_rect (self, &visible_rect);
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ gtk_text_view_get_iter_location (text_view, &iter, &mark_rect);
+
+ if (!_GDK_RECTANGLE_CONTAINS (&visible_rect, &mark_rect))
+ ide_source_view_scroll_to_mark (self, mark, 0.0, use_align, alignx, aligny, TRUE);
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_source_view_move_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark)
+{
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextIter end;
+ GdkRectangle visible_rect;
+ GdkRectangle iter_rect;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+ g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE);
+
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ ide_source_view_get_visible_rect (self, &visible_rect);
+ gtk_text_view_get_iter_location (text_view, &iter, &iter_rect);
+
+ if (_GDK_RECTANGLE_CONTAINS (&visible_rect, &iter_rect))
+ IDE_RETURN (FALSE);
+
+ if (_GDK_RECTANGLE_Y2 (&iter_rect) > _GDK_RECTANGLE_Y2 (&visible_rect))
+ gtk_text_view_get_iter_at_location (text_view, &iter,
+ _GDK_RECTANGLE_X2 (&visible_rect),
+ _GDK_RECTANGLE_Y2 (&visible_rect));
+ else if (iter_rect.y < visible_rect.y)
+ gtk_text_view_get_iter_at_location (text_view, &iter, visible_rect.x, visible_rect.y);
+ else
+ return gtk_text_view_move_mark_onscreen (text_view, mark);
+
+ gtk_text_buffer_move_mark (buffer, mark, &iter);
+
+ IDE_RETURN (TRUE);
+}
+
+static gboolean
+ide_source_view_mark_is_onscreen (IdeSourceView *self,
+ GtkTextMark *mark)
+{
+ GtkTextBuffer *buffer;
+ GdkRectangle visible_rect;
+ GdkRectangle mark_rect;
+ GtkTextIter iter;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_TEXT_MARK (mark));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+
+ ide_source_view_get_visible_rect (self, &visible_rect);
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &mark_rect);
+
+ return (_GDK_RECTANGLE_CONTAINS (&visible_rect, &mark_rect));
+}
+
+static void
+ide_source_view__vadj_animation_completed (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ /*
+ * If the mark we were scrolling to is not yet on screen, then just wait for another size
+ * allocate so that we can continue making progress.
+ */
+ if (!ide_source_view_mark_is_onscreen (self, priv->scroll_mark))
+ IDE_EXIT;
+
+ priv->scrolling_to_scroll_mark = FALSE;
+
+ IDE_EXIT;
+}
+
+/*
+ * Many parts of this function were taken from gtk_text_view_scroll_to_iter ()
+ * https://developer.gnome.org/gtk3/stable/GtkTextView.html#gtk-text-view-scroll-to-iter
+ */
+void
+ide_source_view_scroll_to_iter (IdeSourceView *self,
+ const GtkTextIter *iter,
+ gdouble within_margin,
+ IdeSourceScrollAlign use_align,
+ gdouble xalign,
+ gdouble yalign,
+ gboolean animate_scroll)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextView *text_view = (GtkTextView *)self;
+ GtkTextBuffer *buffer;
+ GdkRectangle rect;
+ GdkRectangle screen;
+ gint xvalue = 0;
+ gint yvalue = 0;
+ gint scroll_dest;
+ gint screen_bottom;
+ gint screen_right;
+ gint screen_xoffset;
+ gint screen_yoffset;
+ gint current_x_scroll;
+ gint current_y_scroll;
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (within_margin >= 0.0 && within_margin <= 0.5);
+ g_return_if_fail (xalign >= 0.0 && xalign <= 1.0);
+ g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
+
+ if (!ide_source_view_can_animate (self))
+ animate_scroll = FALSE;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_move_mark (buffer, priv->scroll_mark, iter);
+
+ hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self));
+ vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self));
+
+ gtk_text_view_get_iter_location (text_view,
+ iter,
+ &rect);
+
+ gtk_text_view_get_visible_rect (text_view, &screen);
+
+ current_x_scroll = screen.x;
+ current_y_scroll = screen.y;
+
+ screen_xoffset = screen.width * within_margin;
+ screen_yoffset = screen.height * within_margin;
+
+ screen.x += screen_xoffset;
+ screen.y += screen_yoffset;
+ screen.width -= screen_xoffset * 2;
+ screen.height -= screen_yoffset * 2;
+
+
+ /* paranoia check */
+ if (screen.width < 1)
+ screen.width = 1;
+ if (screen.height < 1)
+ screen.height = 1;
+
+ /* The -1 here ensures that we leave enough space to draw the cursor
+ * when this function is used for horizontal scrolling.
+ */
+ screen_right = screen.x + screen.width - 1;
+ screen_bottom = screen.y + screen.height;
+
+
+ /* The alignment affects the point in the target character that we
+ * choose to align. If we're doing right/bottom alignment, we align
+ * the right/bottom edge of the character the mark is at; if we're
+ * doing left/top we align the left/top edge of the character; if
+ * we're doing center alignment we align the center of the
+ * character.
+ */
+
+ /* Vertical alignment */
+ scroll_dest = current_y_scroll;
+ if (SCROLL_Y (use_align))
+ {
+ scroll_dest = rect.y + (rect.height * yalign) - (screen.height * yalign);
+
+ /* if scroll_dest < screen.y, we move a negative increment (up),
+ * else a positive increment (down)
+ */
+ yvalue = scroll_dest - screen.y + screen_yoffset;
+ }
+ else
+ {
+ /* move minimum to get onscreen */
+ if (rect.y < screen.y)
+ {
+ scroll_dest = rect.y;
+ yvalue = scroll_dest - screen.y - screen_yoffset;
+ }
+ else if ((rect.y + rect.height) > screen_bottom)
+ {
+ scroll_dest = rect.y + rect.height;
+ yvalue = scroll_dest - screen_bottom + screen_yoffset;
+ }
+ }
+ yvalue += current_y_scroll;
+
+ /* Scroll offset adjustment */
+ if (priv->cached_char_height)
+ {
+ gint max_scroll_offset;
+ gint visible_lines;
+ gint scroll_offset;
+ gint scroll_offset_height;
+
+ visible_lines = screen.height / priv->cached_char_height;
+ max_scroll_offset = (visible_lines - 1) / 2;
+ scroll_offset = MIN ((gint)priv->scroll_offset, max_scroll_offset);
+ scroll_offset_height = priv->cached_char_height * scroll_offset;
+
+ if (scroll_offset_height > 0)
+ {
+ if (rect.y - scroll_offset_height < yvalue)
+ yvalue -= (scroll_offset_height - (rect.y - yvalue));
+ else if (_GDK_RECTANGLE_Y2 (&rect) + scroll_offset_height > yvalue + screen.height)
+ yvalue += (_GDK_RECTANGLE_Y2 (&rect) + scroll_offset_height) - (yvalue + screen.height);
+ }
+ }
+
+ /* Horizontal alignment */
+ scroll_dest = current_x_scroll;
+ if (SCROLL_X (use_align))
+ {
+ scroll_dest = rect.x + (rect.width * xalign) - (screen.width * xalign);
+
+ /* if scroll_dest < screen.y, we move a negative increment (left),
+ * else a positive increment (right)
+ */
+ xvalue = scroll_dest - screen.x + screen_xoffset;
+ }
+ else
+ {
+ /* move minimum to get onscreen */
+ if (rect.x < screen.x)
+ {
+ scroll_dest = rect.x;
+ xvalue = scroll_dest - screen.x - screen_xoffset;
+ }
+ else if ((rect.x + rect.width) > screen_right)
+ {
+ scroll_dest = rect.x + rect.width;
+ xvalue = scroll_dest - screen_right + screen_xoffset;
+ }
+ }
+ xvalue += current_x_scroll;
+
+ if (animate_scroll)
+ {
+ GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
+ guint duration_msec = LARGE_SCROLL_DURATION_MSEC;
+ gdouble difference;
+ gdouble page_size;
+ gdouble current;
+
+ current = gtk_adjustment_get_value (vadj);
+ page_size = gtk_adjustment_get_page_size (vadj);
+ difference = ABS (current - yvalue);
+
+ /*
+ * Ignore animations if we are scrolling less than two full lines. This
+ * helps when pressing up/down for key repeat. Also, if it's a partial
+ * page scroll (less than page size), use less time to animate, so it
+ * isn't so annoying.
+ */
+ if (difference < (priv->cached_char_height * 2))
+ goto ignore_animation;
+ else if (difference <= page_size)
+ duration_msec = SMALL_SCROLL_DURATION_MSEC;
+
+ priv->scrolling_to_scroll_mark = TRUE;
+
+ if (priv->hadj_animation != NULL)
+ {
+ dzl_animation_stop (priv->hadj_animation);
+ g_clear_weak_pointer (&priv->hadj_animation);
+ }
+
+ priv->hadj_animation =
+ dzl_object_animate (hadj,
+ DZL_ANIMATION_EASE_OUT_CUBIC,
+ duration_msec,
+ frame_clock,
+ "value", (double)xvalue,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (priv->hadj_animation),
+ (gpointer *)&priv->hadj_animation);
+
+ if (priv->vadj_animation != NULL)
+ {
+ dzl_animation_stop (priv->vadj_animation);
+ g_clear_weak_pointer (&priv->vadj_animation);
+ }
+
+ priv->vadj_animation =
+ dzl_object_animate_full (vadj,
+ DZL_ANIMATION_EASE_OUT_CUBIC,
+ duration_msec,
+ frame_clock,
+ (GDestroyNotify)ide_source_view__vadj_animation_completed,
+ self,
+ "value", (double)yvalue,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (priv->vadj_animation),
+ (gpointer *)&priv->vadj_animation);
+ }
+ else
+ {
+ignore_animation:
+ gtk_adjustment_set_value (hadj, xvalue);
+ gtk_adjustment_set_value (vadj, yvalue);
+ }
+
+ IDE_EXIT;
+}
+
+void
+ide_source_view_scroll_to_mark (IdeSourceView *self,
+ GtkTextMark *mark,
+ gdouble within_margin,
+ IdeSourceScrollAlign use_align,
+ gdouble xalign,
+ gdouble yalign,
+ gboolean animate_scroll)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+ g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+ g_return_if_fail (xalign >= 0.0);
+ g_return_if_fail (xalign <= 1.0);
+ g_return_if_fail (yalign >= 0.0);
+ g_return_if_fail (yalign <= 1.0);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ ide_source_view_scroll_to_iter (self, &iter, within_margin, use_align, xalign, yalign,
+ animate_scroll);
+
+#ifdef IDE_ENABLE_TRACE
+ {
+ const gchar *name = gtk_text_mark_get_name (mark);
+ IDE_TRACE_MSG ("Scrolling to mark \"%s\" at %d:%d",
+ name ? name : "unnamed",
+ gtk_text_iter_get_line (&iter),
+ gtk_text_iter_get_line_offset (&iter));
+ }
+#endif
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_source_view_place_cursor_onscreen (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+
+ ret = ide_source_view_move_mark_onscreen (self, insert);
+
+ IDE_RETURN (ret);
+}
+
+/**
+ * ide_source_view_get_file_settings:
+ * @self: an #IdeSourceView.
+ *
+ * Gets the #IdeSourceView:file-settings property. This contains various
+ * settings for how the file should be rendered in the view, and preferences
+ * such as spaces vs tabs.
+ *
+ * Returns: (transfer none) (nullable): An #IdeFileSettings or %NULL.
+ *
+ * Since: 3.32
+ */
+IdeFileSettings *
+ide_source_view_get_file_settings (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
+
+ return (IdeFileSettings *)ide_binding_group_get_source (priv->file_setting_bindings);
+}
+
+gboolean
+ide_source_view_get_highlight_current_line (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->highlight_current_line;
+}
+
+void
+ide_source_view_set_highlight_current_line (IdeSourceView *self,
+ gboolean highlight_current_line)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ /*
+ * This overrides the default GtkSourceView::highlight-current-line so that
+ * we can turn off the line highlight when the IdeSourceView is not in focus.
+ * See ide_source_view_real_focus_in_event() and
+ * ide_source_view_real_focus_out_event() for the machinery.
+ */
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ highlight_current_line = !!highlight_current_line;
+
+ if (highlight_current_line != priv->highlight_current_line)
+ {
+ priv->highlight_current_line = highlight_current_line;
+ g_object_notify (G_OBJECT (self), "highlight-current-line");
+ }
+}
+
+guint
+ide_source_view_get_visual_column (IdeSourceView *self,
+ const GtkTextIter *location)
+{
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), 0);
+
+ return gtk_source_view_get_visual_column(GTK_SOURCE_VIEW (self), location);
+}
+
+void
+ide_source_view_get_visual_position (IdeSourceView *self,
+ guint *line,
+ guint *line_column)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+
+ if (!gtk_widget_has_focus (GTK_WIDGET (self)))
+ {
+ gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, priv->saved_line, 0);
+ ide_source_view_get_iter_at_visual_column (self, priv->saved_line_column, &iter);
+ }
+ else
+ {
+ GtkTextMark *mark;
+
+ mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ }
+
+ if (line)
+ *line = gtk_text_iter_get_line (&iter);
+
+ if (line_column)
+ *line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self), &iter);
+}
+
+gboolean
+ide_source_view_get_show_line_numbers (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->show_line_numbers;
+}
+
+void
+ide_source_view_set_show_line_numbers (IdeSourceView *self,
+ gboolean show_line_numbers)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->show_line_numbers = !!show_line_numbers;
+
+ if (priv->gutter)
+ {
+ ide_gutter_set_show_line_numbers (priv->gutter, show_line_numbers);
+ g_object_notify (G_OBJECT (self), "show-line-numbers");
+ }
+}
+
+gboolean
+ide_source_view_get_show_relative_line_numbers (IdeSourceView *self)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return priv->show_relative_line_numbers;
+}
+
+void
+ide_source_view_set_show_relative_line_numbers (IdeSourceView *self,
+ gboolean show_relative_line_numbers)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ priv->show_relative_line_numbers = !!show_relative_line_numbers;
+
+ if (priv->gutter)
+ {
+ ide_gutter_set_show_relative_line_numbers (priv->gutter, show_relative_line_numbers);
+ g_object_notify (G_OBJECT (self), "show-relative-line-numbers");
+ }
+}
+
+/**
+ * ide_source_view_set_gutter:
+ * @self: a #IdeSourceView
+ * @gutter: an #IdeGutter
+ *
+ * Allows setting the gutter for the sourceview.
+ *
+ * Generally, this will always be #IdeOmniGutterRenderer. However, to avoid
+ * circular dependencies, an interface is used to allow plugins to set
+ * this object.
+ *
+ * Since: 3.32
+ */
+void
+ide_source_view_set_gutter (IdeSourceView *self,
+ IdeGutter *gutter)
+{
+ IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
+ GtkSourceGutter *left_gutter;
+
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+ g_return_if_fail (!gutter || IDE_IS_GUTTER (gutter));
+ g_return_if_fail (!gutter || GTK_SOURCE_IS_GUTTER_RENDERER (gutter));
+
+ if (gutter == priv->gutter)
+ return;
+
+ left_gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self),
+ GTK_TEXT_WINDOW_LEFT);
+
+ if (priv->gutter)
+ {
+ gtk_source_gutter_remove (left_gutter, GTK_SOURCE_GUTTER_RENDERER (priv->gutter));
+ g_clear_object (&priv->gutter);
+ }
+
+ if (gutter)
+ {
+ priv->gutter = g_object_ref_sink (gutter);
+ gtk_source_gutter_insert (left_gutter,
+ GTK_SOURCE_GUTTER_RENDERER (gutter),
+ 0);
+ ide_gutter_set_show_line_numbers (priv->gutter, priv->show_line_numbers);
+ ide_gutter_set_show_relative_line_numbers (priv->gutter, priv->show_relative_line_numbers);
+ ide_gutter_set_show_line_changes (priv->gutter, priv->show_line_changes);
+ ide_gutter_set_show_line_diagnostics (priv->gutter, priv->show_line_diagnostics);
+ ide_gutter_style_changed (gutter);
+ }
+
+ g_object_notify (G_OBJECT (self), "show-line-changes");
+ g_object_notify (G_OBJECT (self), "show-line-diagnostics");
+ g_object_notify (G_OBJECT (self), "show-line-numbers");
+ g_object_notify (G_OBJECT (self), "show-relative-line-numbers");
+}
diff --git a/src/libide/sourceview/archive/ide-source-view.h b/src/libide/sourceview/archive/ide-source-view.h
new file mode 100644
index 000000000..74b1147b3
--- /dev/null
+++ b/src/libide/sourceview/archive/ide-source-view.h
@@ -0,0 +1,180 @@
+/* ide-source-view.h
+ *
+ * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_SOURCEVIEW_INSIDE) && !defined (IDE_SOURCEVIEW_COMPILATION)
+# error "Only <libide-sourceview.h> can be included directly."
+#endif
+
+#include <gtksourceview/gtksource.h>
+#include <libide-code.h>
+
+#include "ide-gutter.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SOURCE_VIEW (ide_source_view_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdeSourceView, ide_source_view, IDE, SOURCE_VIEW, GtkSourceView)
+
+typedef enum
+{
+ IDE_CURSOR_COLUMN,
+ IDE_CURSOR_SELECT,
+ IDE_CURSOR_MATCH
+} IdeCursorType;
+
+typedef enum
+{
+ IDE_SOURCE_SCROLL_NONE = 0,
+ IDE_SOURCE_SCROLL_BOTH = 1,
+ IDE_SOURCE_SCROLL_X = 1 << 1,
+ IDE_SOURCE_SCROLL_Y = 1 << 2,
+} IdeSourceScrollAlign;
+
+struct _IdeSourceViewClass
+{
+ GtkSourceViewClass parent_class;
+
+ void (*focus_location) (IdeSourceView *self,
+ IdeLocation *location);
+ void (*goto_definition) (IdeSourceView *self);
+ void (*move_error) (IdeSourceView *self,
+ GtkDirectionType dir);
+ void (*rebuild_highlight) (IdeSourceView *self);
+ void (*request_documentation) (IdeSourceView *self);
+ void (*sort) (IdeSourceView *self,
+ gboolean ignore_case,
+ gboolean reverse);
+ void (*begin_rename) (IdeSourceView *self);
+ void (*add_cursor) (IdeSourceView *self,
+ guint type);
+ void (*remove_cursors) (IdeSourceView *self);
+};
+
+IDE_AVAILABLE_IN_ALL
+guint ide_source_view_get_visual_column (IdeSourceView *self,
+ const GtkTextIter
*location);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_get_visual_position (IdeSourceView *self,
+ guint *line,
+ guint
*line_column);
+IDE_AVAILABLE_IN_ALL
+IdeFileSettings *ide_source_view_get_file_settings (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+const PangoFontDescription *ide_source_view_get_font_desc (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+PangoFontDescription *ide_source_view_get_scaled_font_desc (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_highlight_current_line (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_insert_matching_brace (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_get_iter_at_visual_column (IdeSourceView *self,
+ guint
column,
+ GtkTextIter
*location);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_overwrite_braces (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_overwrite (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+guint ide_source_view_get_scroll_offset (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_show_grid_lines (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_show_line_changes (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_show_line_diagnostics (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_show_line_numbers (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_show_relative_line_numbers (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_get_visible_rect (IdeSourceView *self,
+ GdkRectangle
*visible_rect);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_font_desc (IdeSourceView *self,
+ const PangoFontDescription
*font_desc);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_font_name (IdeSourceView *self,
+ const gchar
*font_name);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_highlight_current_line (IdeSourceView *self,
+ gboolean
highlight_current_line);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_insert_matching_brace (IdeSourceView *self,
+ gboolean
insert_matching_brace);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_overwrite_braces (IdeSourceView *self,
+ gboolean
overwrite_braces);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_scroll_offset (IdeSourceView *self,
+ guint
scroll_offset);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_show_grid_lines (IdeSourceView *self,
+ gboolean
show_grid_lines);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_show_line_changes (IdeSourceView *self,
+ gboolean
show_line_changes);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_show_line_diagnostics (IdeSourceView *self,
+ gboolean
show_line_diagnostics);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_show_line_numbers (IdeSourceView *self,
+ gboolean
show_line_numbers);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_show_relative_line_numbers (IdeSourceView *self,
+ gboolean
show_relative_line_numbers);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_move_mark_onscreen (IdeSourceView *self,
+ GtkTextMark
*mark);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_place_cursor_onscreen (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_scroll_mark_onscreen (IdeSourceView *self,
+ GtkTextMark *mark,
+ IdeSourceScrollAlign
use_align,
+ gdouble
alignx,
+ gdouble
aligny);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_scroll_to_mark (IdeSourceView *self,
+ GtkTextMark *mark,
+ gdouble
within_margin,
+ IdeSourceScrollAlign
use_align,
+ gdouble
xalign,
+ gdouble
yalign,
+ gboolean
animate_scroll);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_scroll_to_iter (IdeSourceView *self,
+ const GtkTextIter *iter,
+ gdouble
within_margin,
+ IdeSourceScrollAlign
use_align,
+ gdouble
xalign,
+ gdouble
yalign,
+ gboolean
animate_scroll);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_scroll_to_insert (IdeSourceView
*self);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_gutter (IdeSourceView *self,
+ IdeGutter
*gutter);
+
+G_END_DECLS
diff --git a/src/libide/sourceview/gtk/menus.ui b/src/libide/sourceview/archive/menus.ui
similarity index 100%
rename from src/libide/sourceview/gtk/menus.ui
rename to src/libide/sourceview/archive/menus.ui
diff --git a/src/libide/sourceview/ide-source-view.c b/src/libide/sourceview/ide-source-view.c
index 4edbd923e..70270601b 100644
--- a/src/libide/sourceview/ide-source-view.c
+++ b/src/libide/sourceview/ide-source-view.c
@@ -1,6 +1,6 @@
/* ide-source-view.c
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7728 +22,76 @@
#include "config.h"
-#include <cairo-gobject.h>
-#include <glib/gi18n.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libide-code.h>
-#include <libide-plugins.h>
-#include <libide-threading.h>
-
-#include "ide-buffer-private.h"
-
-#include "ide-cursor.h"
#include "ide-source-view.h"
-#include "ide-source-view-enums.h"
-#include "ide-text-util.h"
-
-#define INCLUDE_STATEMENTS "^#include[\\s]+[\\\"\\<][^\\s\\\"\\\'\\<\\>[:cntrl:]]+[\\\"\\>]"
-
-#define DEFAULT_FONT_DESC "Monospace 11"
-#define ANIMATION_X_GROW 50
-#define ANIMATION_Y_GROW 30
-#define SMALL_SCROLL_DURATION_MSEC 100
-#define LARGE_SCROLL_DURATION_MSEC 250
-#define FIXIT_LABEL_LEN_MAX 30
-#define SCROLL_REPLAY_DELAY 1000
-#define DEFAULT_OVERSCROLL_NUM_LINES 1
-#define TAG_DEFINITION "action::hover-definition"
-#define DEFINITION_HIGHLIGHT_MODIFIER GDK_CONTROL_MASK
-
-#define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)
-
-#define _GDK_RECTANGLE_X2(r) ((r)->x + (r)->width)
-#define _GDK_RECTANGLE_Y2(r) ((r)->y + (r)->height)
-#define _GDK_RECTANGLE_CONTAINS(rect,other) rectangle_contains_rectangle(rect,other)
-#define TRACE_RECTANGLE(name, rect) \
- IDE_TRACE_MSG ("%s = Rectangle(x=%d, y=%d, width=%d, height=%d)", \
- name, (rect)->x, (rect)->y, (rect)->width, (rect)->height)
-
-#define SCROLL_X(align) \
- (((align) == IDE_SOURCE_SCROLL_BOTH) || ((align) == IDE_SOURCE_SCROLL_X))
-#define SCROLL_Y(align) \
- (((align) == IDE_SOURCE_SCROLL_BOTH) || ((align) == IDE_SOURCE_SCROLL_Y))
-
-typedef struct
-{
- IdeBuffer *buffer;
- GtkCssProvider *css_provider;
- PangoFontDescription *font_desc;
- IdeExtensionAdapter *indenter_adapter;
- IdeSourceViewCapture *capture;
- gchar *display_name;
- IdeSourceViewMode *mode;
- GtkTextMark *scroll_mark;
- GQueue *selections;
- GQueue *snippets;
- DzlAnimation *hadj_animation;
- DzlAnimation *vadj_animation;
- IdeGutter *gutter;
-
- IdeCompletion *completion;
- IdeHover *hover;
-
- IdeBindingGroup *file_setting_bindings;
- IdeSignalGroup *buffer_signals;
-
- guint change_sequence;
-
- guint target_line_column;
- GString *command_str;
- gunichar command;
- gunichar modifier;
- gunichar search_char;
- gint count;
- gunichar inner_left;
- gunichar inner_right;
-
- guint scroll_offset;
- gint cached_char_height;
- gint cached_char_width;
-
- guint saved_line;
- guint saved_line_column;
- guint saved_selection_line;
- guint saved_selection_line_column;
- GdkRGBA snippet_area_background_rgba;
-
- guint font_scale;
-
- gint overscroll_num_lines;
-
- guint delay_size_allocate_chainup;
- GtkAllocation delay_size_allocation;
-
- IdeLocation *definition_src_location;
- GtkTextMark *definition_highlight_start_mark;
- GtkTextMark *definition_highlight_end_mark;
-
- GRegex *include_regex;
-
- IdeCursor *cursor;
-
- guint in_key_press;
-
- guint auto_indent : 1;
- guint completion_blocked : 1;
- guint did_ctrl_opacity : 1;
- guint highlight_current_line : 1;
- guint in_replay_macro : 1;
- guint insert_mark_cleared : 1;
- guint insert_matching_brace : 1;
- guint interactive_completion : 1;
- guint overwrite_braces : 1;
- guint recording_macro : 1;
- guint scrolling_to_scroll_mark : 1;
- guint show_grid_lines : 1;
- guint snippet_completion : 1;
- guint waiting_for_capture : 1;
- guint waiting_for_symbol : 1;
- guint show_line_changes : 1;
- guint show_line_diagnostics : 1;
- guint show_line_numbers : 1;
- guint show_relative_line_numbers : 1;
-} IdeSourceViewPrivate;
-
-typedef struct
-{
- IdeSourceView *self;
- GtkTextMark *word_start_mark;
- GtkTextMark *word_end_mark;
-} DefinitionHighlightData;
-
-typedef struct
+struct _IdeSourceView
{
- GPtrArray *resolvers;
- IdeLocation *location;
-} FindReferencesTaskData;
-
-G_DEFINE_TYPE_WITH_PRIVATE (IdeSourceView, ide_source_view, GTK_SOURCE_TYPE_VIEW)
-
-enum {
- PROP_0,
- PROP_COMPLETION_N_ROWS,
- PROP_COUNT,
- PROP_FILE_SETTINGS,
- PROP_FONT_NAME,
- PROP_FONT_DESC,
- PROP_INDENTER,
- PROP_INDENT_STYLE,
- PROP_INSERT_MATCHING_BRACE,
- PROP_INTERACTIVE_COMPLETION,
- PROP_MODE_DISPLAY_NAME,
- PROP_OVERWRITE_BRACES,
- PROP_SCROLL_OFFSET,
- PROP_SHOW_GRID_LINES,
- PROP_SHOW_LINE_CHANGES,
- PROP_SHOW_LINE_DIAGNOSTICS,
- PROP_SHOW_RELATIVE_LINE_NUMBERS,
- PROP_OVERSCROLL,
- LAST_PROP,
-
- /* These are overridden */
- PROP_AUTO_INDENT,
- PROP_HIGHLIGHT_CURRENT_LINE,
- PROP_OVERWRITE,
- PROP_SHOW_LINE_NUMBERS,
+ GtkSourceView source_view;
};
enum {
- ACTION,
- ADD_CURSOR,
- APPEND_TO_COUNT,
- AUTO_INDENT,
- BEGIN_MACRO,
- BEGIN_RENAME,
- BEGIN_USER_ACTION,
- CAPTURE_MODIFIER,
- CLEAR_COUNT,
- CLEAR_MODIFIER,
- CLEAR_SEARCH,
- CLEAR_SELECTION,
- CLEAR_SNIPPETS,
- COPY_CLIPBOARD_EXTENDED,
- CYCLE_COMPLETION,
- DECREASE_FONT_SIZE,
- DELETE_SELECTION,
- DRAW_BUBBLES,
- DUPLICATE_ENTIRE_LINE,
- END_MACRO,
- END_USER_ACTION,
- FOCUS_LOCATION,
- FORMAT_SELECTION,
- QUERY_CODE_ACTION,
- FIND_REFERENCES,
- GOTO_DEFINITION,
- HIDE_COMPLETION,
- INCREASE_FONT_SIZE,
- INDENT_SELECTION,
- INSERT_AT_CURSOR_AND_INDENT,
- INSERT_MODIFIER,
- JUMP,
- MOVEMENT,
- MOVE_ERROR,
- MOVE_SEARCH,
- PASTE_CLIPBOARD_EXTENDED,
- POP_SELECTION,
- POP_SNIPPET,
- PUSH_SELECTION,
- PUSH_SNIPPET,
- REBUILD_HIGHLIGHT,
- REINDENT,
- REMOVE_CURSORS,
- REPLAY_MACRO,
- REQUEST_DOCUMENTATION,
- RESET,
- RESET_FONT_SIZE,
- RESTORE_INSERT_MARK,
- SAVE_COMMAND,
- SAVE_INSERT_MARK,
- SAVE_SEARCH_CHAR,
- SELECT_INNER,
- SELECT_TAG,
- SELECTION_THEATRIC,
- SET_MODE,
- SET_OVERWRITE,
- SET_SEARCH_TEXT,
- SORT,
- SWAP_SELECTION_BOUNDS,
- LAST_SIGNAL
-};
-
-enum {
- FONT_SCALE_XX_SMALL,
- FONT_SCALE_X_SMALL,
- FONT_SCALE_SMALL,
- FONT_SCALE_NORMAL,
- FONT_SCALE_LARGE,
- FONT_SCALE_X_LARGE,
- FONT_SCALE_XX_LARGE,
- FONT_SCALE_XXX_LARGE,
- LAST_FONT_SCALE
-};
-
-static GParamSpec *properties [LAST_PROP];
-static guint signals [LAST_SIGNAL];
-static gdouble fontScale [LAST_FONT_SCALE] = {
- 0.57870, 0.69444, 0.83333,
- 1.0,
- 1.2, 1.44, 1.728, 2.48832,
+ PROP_0,
+ N_PROPS
};
-static gboolean ide_source_view_do_size_allocate_hack_cb (gpointer data);
-static void ide_source_view_real_save_insert_mark (IdeSourceView *self);
-static void ide_source_view_real_restore_insert_mark (IdeSourceView *self);
-static void ide_source_view_real_set_mode (IdeSourceView *self,
- const gchar *name,
- IdeSourceViewModeType type);
-static void ide_source_view_save_column (IdeSourceView *self);
-static void ide_source_view_maybe_overwrite (IdeSourceView *self,
- GtkTextIter *iter,
- const gchar *text,
- gint len);
-
-static inline cairo_bool_t
-rectangle_contains_rectangle (const cairo_rectangle_int_t *a,
- const cairo_rectangle_int_t *b)
-{
- return (a->x <= b->x &&
- a->x + (int) a->width >= b->x + (int) b->width &&
- a->y <= b->y &&
- a->y + (int) a->height >= b->y + (int) b->height);
-}
-
-static gpointer
-get_selection_owner (IdeSourceView *self)
-{
- return g_object_get_data (G_OBJECT (gtk_widget_get_toplevel (GTK_WIDGET (self))),
- "IDE_SOURCE_VIEW_SELECTION_OWNER");
-}
-
-static void
-set_selection_owner (IdeSourceView *self,
- gpointer tag)
-{
- g_object_set_data (G_OBJECT (gtk_widget_get_toplevel (GTK_WIDGET (self))),
- "IDE_SOURCE_VIEW_SELECTION_OWNER", tag);
-}
-
-static void
-find_references_task_data_free (FindReferencesTaskData *data)
-{
- g_clear_pointer (&data->resolvers, g_ptr_array_unref);
- g_clear_object (&data->location);
- g_slice_free (FindReferencesTaskData, data);
-}
-
-static void
-block_interactive (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->completion)
- ide_completion_block_interactive (priv->completion);
-}
-
-static void
-unblock_interactive (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->completion != NULL)
- ide_completion_unblock_interactive (priv->completion);
-}
-
-static void
-ide_source_view_set_interactive_completion (IdeSourceView *self,
- gboolean interactive_completion)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- interactive_completion = !!interactive_completion;
-
- if (interactive_completion != priv->interactive_completion)
- {
- priv->interactive_completion = interactive_completion;
-
- if (interactive_completion)
- unblock_interactive (self);
- else
- block_interactive (self);
- }
-}
-
-static void
-definition_highlight_data_free (DefinitionHighlightData *data)
-{
- if (data != NULL)
- {
- GtkTextBuffer *buffer;
-
- buffer = gtk_text_mark_get_buffer (data->word_start_mark);
-
- gtk_text_buffer_delete_mark (buffer, data->word_start_mark);
- gtk_text_buffer_delete_mark (buffer, data->word_end_mark);
-
- g_clear_object (&data->self);
- g_clear_object (&data->word_start_mark);
- g_clear_object (&data->word_end_mark);
-
- g_slice_free (DefinitionHighlightData, data);
- }
-}
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC (DefinitionHighlightData, definition_highlight_data_free)
-
-static gboolean
-ide_source_view_can_animate (IdeSourceView *self)
-{
- GtkSettings *settings;
- GdkScreen *screen;
- gboolean can_animate = FALSE;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- screen = gtk_widget_get_screen (GTK_WIDGET (self));
- settings = gtk_settings_get_for_screen (screen);
-
- g_object_get (settings, "gtk-enable-animations", &can_animate, NULL);
-
- return can_animate;
-}
-
-void
-_ide_source_view_set_count (IdeSourceView *self,
- gint count)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->count = count;
-}
-
-void
-_ide_source_view_set_modifier (IdeSourceView *self,
- gunichar modifier)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->modifier = modifier;
-
- if (priv->recording_macro && !priv->in_replay_macro)
- ide_source_view_capture_record_modifier (priv->capture, modifier);
-}
-
-static IdeIndenter *
-ide_source_view_get_indenter (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->indenter_adapter != NULL)
- return ide_extension_adapter_get_extension (priv->indenter_adapter);
-
- return NULL;
-}
-
-static void
-ide_source_view_block_handlers (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_signal_group_block (priv->buffer_signals);
-}
-
-static void
-ide_source_view_unblock_handlers (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_signal_group_unblock (priv->buffer_signals);
-}
-
-static void
-get_rect_for_iters (GtkTextView *text_view,
- const GtkTextIter *iter1,
- const GtkTextIter *iter2,
- GdkRectangle *rect,
- GtkTextWindowType window_type)
-{
- GdkRectangle area;
- GdkRectangle tmp;
- GtkTextIter begin;
- GtkTextIter end;
- GtkTextIter iter;
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (iter1 != NULL);
- g_assert (iter2 != NULL);
- g_assert (rect != NULL);
- g_assert (gtk_text_iter_get_buffer (iter1) == gtk_text_iter_get_buffer (iter2));
- g_assert (gtk_text_view_get_buffer (text_view) == gtk_text_iter_get_buffer (iter1));
-
- begin = *iter1;
- end = *iter2;
-
- if (gtk_text_iter_equal (&begin, &end))
- {
- gtk_text_view_get_iter_location (text_view, &begin, &area);
- goto finish;
- }
-
- gtk_text_iter_order (&begin, &end);
-
- if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&end))
- {
- gtk_text_view_get_iter_location (text_view, &begin, &area);
- gtk_text_view_get_iter_location (text_view, &end, &tmp);
- gdk_rectangle_union (&area, &tmp, &area);
- goto finish;
- }
-
- gtk_text_view_get_iter_location (text_view, &begin, &area);
-
- iter = begin;
-
- do
- {
- /* skip trailing newline */
- if ((gtk_text_iter_starts_line (&iter) && gtk_text_iter_equal (&iter, &end)))
- break;
-
- gtk_text_view_get_iter_location (text_view, &iter, &tmp);
- gdk_rectangle_union (&area, &tmp, &area);
-
- gtk_text_iter_forward_to_line_end (&iter);
- gtk_text_view_get_iter_location (text_view, &iter, &tmp);
- gdk_rectangle_union (&area, &tmp, &area);
-
- if (!gtk_text_iter_forward_char (&iter))
- break;
- }
- while (gtk_text_iter_compare (&iter, &end) <= 0);
-
-finish:
- gtk_text_view_buffer_to_window_coords (text_view, window_type, area.x, area.y, &area.x, &area.y);
-
- *rect = area;
-}
-
-static void
-animate_expand (IdeSourceView *self,
- const GtkTextIter *begin,
- const GtkTextIter *end)
-{
- DzlBoxTheatric *theatric;
- GtkAllocation alloc;
- GdkRectangle rect = { 0 };
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (begin != NULL);
- g_assert (end != NULL);
-
- get_rect_for_iters (GTK_TEXT_VIEW (self), begin, end, &rect, GTK_TEXT_WINDOW_WIDGET);
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
- rect.height = MIN (rect.height, alloc.height - rect.y);
-
- theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
- "alpha", 0.3,
- "background", "#729fcf",
- "height", rect.height,
- "target", self,
- "width", rect.width,
- "x", rect.x,
- "y", rect.y,
- NULL);
-
- dzl_object_animate_full (theatric,
- DZL_ANIMATION_EASE_IN_CUBIC,
- 250,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- g_object_unref,
- theatric,
- "x", rect.x - ANIMATION_X_GROW,
- "width", rect.width + (ANIMATION_X_GROW * 2),
- "y", rect.y - ANIMATION_Y_GROW,
- "height", rect.height + (ANIMATION_Y_GROW * 2),
- "alpha", 0.0,
- NULL);
-}
-
-static void
-animate_shrink (IdeSourceView *self,
- const GtkTextIter *begin,
- const GtkTextIter *end)
-{
- DzlBoxTheatric *theatric;
- GtkAllocation alloc;
- GdkRectangle rect = { 0 };
- GdkRectangle char_rect = { 0 };
- GtkTextIter copy_begin;
- GtkTextIter copy_end;
- gboolean is_whole_line;
- gboolean is_single_line;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (begin);
- g_assert (end);
-
- get_rect_for_iters (GTK_TEXT_VIEW (self), begin, begin, &char_rect, GTK_TEXT_WINDOW_WIDGET);
- get_rect_for_iters (GTK_TEXT_VIEW (self), begin, end, &rect, GTK_TEXT_WINDOW_WIDGET);
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
- rect.height = MIN (rect.height, alloc.height - rect.y);
-
- copy_begin = *begin;
- copy_end = *end;
-
- gtk_text_iter_order (©_begin, ©_end);
-
- is_single_line = (gtk_text_iter_get_line (©_begin) == gtk_text_iter_get_line (©_end));
- is_whole_line = ((gtk_text_iter_get_line (©_begin) + 1 ==
- gtk_text_iter_get_line (©_end)) &&
- (gtk_text_iter_starts_line (©_begin) &&
- gtk_text_iter_starts_line (©_end)));
+G_DEFINE_TYPE (IdeSourceView, ide_source_view, GTK_SOURCE_TYPE_VIEW)
- theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
- "alpha", 0.3,
- "background", "#729fcf",
- "height", rect.height,
- "target", self,
- "width", rect.width,
- "x", rect.x,
- "y", rect.y,
- NULL);
-
- if (is_whole_line)
- dzl_object_animate_full (theatric,
- DZL_ANIMATION_EASE_OUT_QUAD,
- 150,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- g_object_unref,
- theatric,
- "x", rect.x,
- "width", rect.width,
- "y", rect.y,
- "height", 0,
- "alpha", 0.3,
- NULL);
- else if (is_single_line)
- dzl_object_animate_full (theatric,
- DZL_ANIMATION_EASE_OUT_QUAD,
- 150,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- g_object_unref,
- theatric,
- "x", rect.x,
- "width", 0,
- "y", rect.y,
- "height", rect.height,
- "alpha", 0.3,
- NULL);
- else
- dzl_object_animate_full (theatric,
- DZL_ANIMATION_EASE_OUT_QUAD,
- 150,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- g_object_unref,
- theatric,
- "x", rect.x,
- "width", 0,
- "y", rect.y,
- "height", char_rect.height,
- "alpha", 0.3,
- NULL);
-}
-
-void
-ide_source_view_scroll_to_insert (IdeSourceView *self)
-{
- GtkTextBuffer *buffer;
- GtkTextMark *mark;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- _ide_buffer_cancel_cursor_restore (IDE_BUFFER (buffer));
- mark = gtk_text_buffer_get_insert (buffer);
- ide_source_view_scroll_mark_onscreen (self, mark, TRUE, 0.5, 1.0);
-
- IDE_EXIT;
-}
+static GParamSpec *properties [N_PROPS];
static void
-ide_source_view_invalidate_window (IdeSourceView *self)
-{
- GdkWindow *window;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if ((window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_WIDGET)))
- {
- gdk_window_invalidate_rect (window, NULL, TRUE);
- gtk_widget_queue_draw (GTK_WIDGET (self));
- }
-}
-
-static gchar *
-text_iter_get_line_prefix (const GtkTextIter *iter)
+ide_source_view_dispose (GObject *object)
{
- GtkTextIter begin;
- GString *str;
-
- g_assert (iter);
-
- gtk_text_iter_assign (&begin, iter);
- gtk_text_iter_set_line_offset (&begin, 0);
-
- str = g_string_new (NULL);
-
- if (gtk_text_iter_compare (&begin, iter) != 0)
- {
- do
- {
- gunichar c;
-
- c = gtk_text_iter_get_char (&begin);
-
- switch (c)
- {
- case '\t':
- case ' ':
- g_string_append_unichar (str, c);
- break;
- default:
- g_string_append_c (str, ' ');
- break;
- }
- }
- while (gtk_text_iter_forward_char (&begin) &&
- (gtk_text_iter_compare (&begin, iter) < 0));
- }
-
- return g_string_free (str, FALSE);
+ G_OBJECT_CLASS (ide_source_view_parent_class)->dispose (object);
}
static void
-ide_source_view_update_auto_indent_override (IdeSourceView *self)
+ide_source_view_finalize (GObject *object)
{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkSourceLanguage *language;
- const gchar *lang_id = NULL;
- IdeIndenter *indenter;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /* Update the indenter if necessary */
- if (priv->auto_indent &&
- priv->indenter_adapter != NULL &&
- priv->buffer != NULL &&
- NULL != (language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (priv->buffer))))
- lang_id = gtk_source_language_get_id (language);
-
- if (priv->indenter_adapter != NULL)
- ide_extension_adapter_set_value (priv->indenter_adapter, lang_id);
-
- /* Fetch our indenter */
- indenter = ide_source_view_get_indenter (self);
-
- /*
- * Updates our override of auto-indent from the GtkSourceView underneath us.
- * Since we do our own mimicing of GtkSourceView, we always disable it. Also
- * updates our mode which needs to know if we have an indenter to provide
- * different CSS selectors.
- */
- gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (self), FALSE);
- if (priv->mode != NULL)
- ide_source_view_mode_set_has_indenter (priv->mode, !!indenter);
+ G_OBJECT_CLASS (ide_source_view_parent_class)->finalize (object);
}
static void
-ide_source_view_set_file_settings (IdeSourceView *self,
- IdeFileSettings *file_settings)
+ide_source_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (!file_settings || IDE_IS_FILE_SETTINGS (file_settings));
+ IdeSourceView *self = IDE_SOURCE_VIEW (object);
- if (file_settings != ide_source_view_get_file_settings (self))
+ switch (prop_id)
{
- ide_binding_group_set_source (priv->file_setting_bindings, file_settings);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILE_SETTINGS]);
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
-ide_source_view__buffer_notify_file_settings_cb (IdeSourceView *self,
- GParamSpec *pspec,
- IdeBuffer *buffer)
-{
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- ide_source_view_set_file_settings (self, ide_buffer_get_file_settings (buffer));
-}
-
-static void
-ide_source_view__buffer_notify_language_cb (IdeSourceView *self,
- GParamSpec *pspec,
- IdeBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- const gchar *lang_id;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- lang_id = ide_buffer_get_language_id (buffer);
-
- /* Update the indenter, which is provided by a plugin. */
- if (priv->indenter_adapter != NULL)
- ide_extension_adapter_set_value (priv->indenter_adapter, lang_id);
- ide_source_view_update_auto_indent_override (self);
-
- /* Reload hover providers by language */
- _ide_hover_set_language (priv->hover, lang_id);
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INDENTER]);
-}
-
-static void
-ide_source_view__buffer_notify_style_scheme_cb (IdeSourceView *self,
- GParamSpec *pspec,
- IdeBuffer *buffer)
+ide_source_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkSourceStyleScheme *scheme = NULL;
- GtkSourceStyle *snippet_area_style = NULL;
- g_autofree gchar *snippet_background = NULL;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (buffer));
- if (scheme != NULL)
- snippet_area_style = gtk_source_style_scheme_get_style (scheme, "snippet::area");
-
- if (snippet_area_style != NULL)
- g_object_get (snippet_area_style, "background", &snippet_background, NULL);
+ IdeSourceView *self = IDE_SOURCE_VIEW (object);
- if (snippet_background == NULL ||
- !gdk_rgba_parse (&priv->snippet_area_background_rgba, snippet_background))
+ switch (prop_id)
{
- gdk_rgba_parse (&priv->snippet_area_background_rgba, "#204a87");
- priv->snippet_area_background_rgba.alpha = 0.1;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
-ide_source_view__buffer_request_scroll_to_insert_cb (IdeSourceView *self,
- IdeBuffer *buffer)
-{
- GtkTextMark *mark;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
- gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (self), mark);
-}
-
-static void
-ide_source_view__buffer_changed_cb (IdeSourceView *self,
- IdeBuffer *buffer)
+ide_source_view_class_init (IdeSourceViewClass *klass)
{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- priv->change_sequence++;
+ object_class->dispose = ide_source_view_dispose;
+ object_class->finalize = ide_source_view_finalize;
+ object_class->get_property = ide_source_view_get_property;
+ object_class->set_property = ide_source_view_set_property;
}
static void
-ide_source_view_rebuild_css (IdeSourceView *self)
+ide_source_view_init (IdeSourceView *self)
{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- if (!priv->css_provider)
- {
- GtkStyleContext *style_context;
-
- priv->css_provider = gtk_css_provider_new ();
- style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
- gtk_style_context_add_provider (style_context,
- GTK_STYLE_PROVIDER (priv->css_provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
- }
-
- if (priv->font_desc)
- {
- g_autofree gchar *str = NULL;
- g_autofree gchar *css = NULL;
- const PangoFontDescription *font_desc = priv->font_desc;
- PangoFontDescription *copy = NULL;
-
- if (priv->font_scale != FONT_SCALE_NORMAL)
- font_desc = copy = ide_source_view_get_scaled_font_desc (self);
-
- str = dzl_pango_font_description_to_css (font_desc);
- css = g_strdup_printf ("textview { %s }", str ?: "");
- gtk_css_provider_load_from_data (priv->css_provider, css, -1, NULL);
-
- if (priv->gutter != NULL)
- ide_gutter_style_changed (priv->gutter);
-
- if (priv->completion != NULL)
- _ide_completion_set_font_description (priv->completion, font_desc);
-
- g_clear_pointer (©, pango_font_description_free);
- }
-}
-
-static void
-ide_source_view_invalidate_range_mark (IdeSourceView *self,
- GtkTextMark *mark_begin,
- GtkTextMark *mark_end)
-{
- GtkTextBuffer *buffer;
- GdkRectangle rect;
- GtkTextIter begin;
- GtkTextIter end;
- GdkWindow *window;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_MARK (mark_begin));
- g_assert (GTK_IS_TEXT_MARK (mark_end));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_get_iter_at_mark (buffer, &begin, mark_begin);
- gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);
-
- get_rect_for_iters (GTK_TEXT_VIEW (self), &begin, &end, &rect, GTK_TEXT_WINDOW_TEXT);
- window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_TEXT);
- gdk_window_invalidate_rect (window, &rect, FALSE);
-}
-
-static void
-ide_source_view__buffer_insert_text_cb (IdeSourceView *self,
- GtkTextIter *iter,
- gchar *text,
- gint len,
- GtkTextBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSnippet *snippet;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (iter != NULL);
- g_assert (text != NULL);
- g_assert (IDE_IS_BUFFER (buffer));
-
- if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
- return;
-
- gtk_text_buffer_begin_user_action (buffer);
-
- if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
- {
- ide_source_view_block_handlers (self);
- ide_snippet_before_insert_text (snippet, buffer, iter, text, len);
- ide_source_view_unblock_handlers (self);
- }
-}
-
-static void
-ide_source_view__buffer_insert_text_after_cb (IdeSourceView *self,
- GtkTextIter *iter,
- gchar *text,
- gint len,
- GtkTextBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSnippet *snippet;
- GtkTextIter insert;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (iter != NULL);
- g_assert (text != NULL);
- g_assert (IDE_IS_BUFFER (buffer));
-
- if (ide_buffer_get_loading (IDE_BUFFER (buffer)))
- return;
-
- if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
- {
- GtkTextMark *begin;
- GtkTextMark *end;
-
- ide_source_view_block_handlers (self);
- ide_snippet_after_insert_text (snippet, buffer, iter, text, len);
- ide_source_view_unblock_handlers (self);
-
- begin = ide_snippet_get_mark_begin (snippet);
- end = ide_snippet_get_mark_end (snippet);
- ide_source_view_invalidate_range_mark (self, begin, end);
- }
-
- if (priv->in_key_press)
- {
- /*
- * If we are handling the key-press-event, we might have just inserted
- * a character that indicates we should overwrite the next character.
- * However, due to GtkIMContext constraints, we need to allow it to be
- * inserted and then handle it here.
- */
- ide_source_view_maybe_overwrite (self, iter, text, len);
- }
-
- /* Ignore multiple cursors unless we have focus */
- if (gtk_widget_has_focus (GTK_WIDGET (self)))
- {
- gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert(buffer));
-
- if (gtk_text_iter_equal (iter, &insert))
- {
- ide_source_view_block_handlers (self);
- ide_cursor_insert_text (priv->cursor, text, len);
- ide_source_view_unblock_handlers (self);
- gtk_text_buffer_get_iter_at_mark (buffer, iter, gtk_text_buffer_get_insert (buffer));
- }
- }
-
- gtk_text_buffer_end_user_action (buffer);
-}
-
-static void
-ide_source_view__buffer_delete_range_cb (IdeSourceView *self,
- GtkTextIter *begin,
- GtkTextIter *end,
- GtkTextBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSnippet *snippet;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_BUFFER (buffer));
-
- if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
- {
- GtkTextMark *begin_mark;
- GtkTextMark *end_mark;
-
- ide_source_view_block_handlers (self);
- ide_snippet_before_delete_range (snippet, buffer, begin, end);
- ide_source_view_unblock_handlers (self);
-
- begin_mark = ide_snippet_get_mark_begin (snippet);
- end_mark = ide_snippet_get_mark_end (snippet);
- ide_source_view_invalidate_range_mark (self, begin_mark, end_mark);
- }
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view__buffer_delete_range_after_cb (IdeSourceView *self,
- GtkTextIter *begin,
- GtkTextIter *end,
- GtkTextBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSnippet *snippet;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_BUFFER (buffer));
-
- ide_source_view_block_handlers (self);
-
- if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
- ide_snippet_after_delete_range (snippet, buffer, begin, end);
-
- ide_source_view_unblock_handlers (self);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view__buffer_mark_set_cb (IdeSourceView *self,
- GtkTextIter *iter,
- GtkTextMark *mark,
- GtkTextBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSnippet *snippet;
- GtkTextMark *insert;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (iter != NULL);
- g_assert (GTK_IS_TEXT_MARK (mark));
- g_assert (GTK_IS_TEXT_BUFFER (buffer));
-
- insert = gtk_text_buffer_get_insert (buffer);
-
- if (mark == insert)
- {
- ide_source_view_block_handlers (self);
- while (NULL != (snippet = g_queue_peek_head (priv->snippets)) &&
- !ide_snippet_insert_set (snippet, mark))
- ide_source_view_pop_snippet (self);
- ide_source_view_unblock_handlers (self);
- }
-
-#ifdef IDE_ENABLE_TRACE
- if (mark == insert || mark == gtk_text_buffer_get_selection_bound (buffer))
- {
- GtkTextIter begin;
- GtkTextIter end;
-
- if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
- {
- gtk_text_iter_order (&begin, &end);
- IDE_TRACE_MSG ("Selection is now %d:%d to %d:%d",
- gtk_text_iter_get_line (&begin),
- gtk_text_iter_get_line_offset (&begin),
- gtk_text_iter_get_line (&end),
- gtk_text_iter_get_line_offset (&end));
- }
- }
-#endif
-}
-
-static void
-ide_source_view__buffer_notify_has_selection_cb (IdeSourceView *self,
- GParamSpec *pspec,
- IdeBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gboolean has_selection;
-
- has_selection = gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer));
- ide_source_view_mode_set_has_selection (priv->mode, has_selection);
-
- if (has_selection)
- set_selection_owner (self, G_OBJECT (self));
- else if (get_selection_owner (self) == G_OBJECT (self))
- set_selection_owner (self, NULL);
-}
-
-static void
-ide_source_view__buffer_line_flags_changed_cb (IdeSourceView *self,
- IdeBuffer *buffer)
-{
- GtkSourceGutter *gutter;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self), GTK_TEXT_WINDOW_LEFT);
- gtk_source_gutter_queue_draw (gutter);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view__buffer_loaded_cb (IdeSourceView *self,
- IdeBuffer *buffer)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextMark *insert;
- GtkTextIter iter;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- if (priv->completion_blocked)
- {
- unblock_interactive (self);
- priv->completion_blocked = FALSE;
- }
-
- /* Store the line column (visual offset) so movements are correct. */
- insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, insert);
- priv->target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self), &iter);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_set_cursor_from_name (IdeSourceView *self,
- const gchar *cursor_name)
-{
- GdkDisplay *display;
- GdkCursor *cursor;
- GdkWindow *window = gtk_text_view_get_window (GTK_TEXT_VIEW (self),
- GTK_TEXT_WINDOW_TEXT);
-
- if (!window)
- return;
-
- display = gdk_window_get_display (window);
- cursor = gdk_cursor_new_from_name (display, cursor_name);
-
- gdk_window_set_cursor (window, cursor);
-}
-
-static void
-ide_source_view_reset_definition_highlight (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->definition_src_location)
- g_clear_object (&priv->definition_src_location);
-
- if (priv->buffer != NULL)
- {
- GtkTextIter begin;
- GtkTextIter end;
-
- gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (priv->buffer), &begin, &end);
- gtk_text_buffer_remove_tag_by_name (GTK_TEXT_BUFFER (priv->buffer), TAG_DEFINITION, &begin, &end);
- }
-
- ide_source_view_set_cursor_from_name (self, "text");
-}
-
-static void
-ide_source_view__buffer__notify_can_redo (IdeSourceView *self,
- GParamSpec *pspec,
- IdeBuffer *buffer)
-{
- GActionGroup *group;
- gboolean can_redo;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- g_object_get (buffer,
- "can-redo", &can_redo,
- NULL);
-
- group = gtk_widget_get_action_group (GTK_WIDGET (self), "sourceview");
- dzl_widget_action_group_set_action_enabled (DZL_WIDGET_ACTION_GROUP (group), "redo", can_redo);
-}
-
-static void
-ide_source_view__buffer__notify_can_undo (IdeSourceView *self,
- GParamSpec *pspec,
- IdeBuffer *buffer)
-{
- GActionGroup *group;
- gboolean can_undo;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
-
- g_object_get (buffer,
- "can-undo", &can_undo,
- NULL);
-
- group = gtk_widget_get_action_group (GTK_WIDGET (self), "sourceview");
- dzl_widget_action_group_set_action_enabled (DZL_WIDGET_ACTION_GROUP (group), "undo", can_undo);
-}
-
-static void
-ide_source_view_bind_buffer (IdeSourceView *self,
- IdeBuffer *buffer,
- IdeSignalGroup *group)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autoptr(IdeContext) context = NULL;
- GtkTextMark *insert;
- IdeObjectBox *box;
- GtkTextIter iter;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_BUFFER (buffer));
- g_assert (DZL_IS_SIGNAL_GROUP (group));
-
- priv->buffer = buffer;
-
- ide_source_view_reset_definition_highlight (self);
-
- ide_buffer_hold (buffer);
-
- if (ide_buffer_get_loading (buffer))
- {
- block_interactive (self);
- priv->completion_blocked = TRUE;
- }
-
- context = ide_buffer_ref_context (buffer);
-
- _ide_hover_set_context (priv->hover, context);
-
- box = ide_object_box_from_object (G_OBJECT (buffer));
-
- priv->indenter_adapter = ide_extension_adapter_new (IDE_OBJECT (box),
- peas_engine_get_default (),
- IDE_TYPE_INDENTER,
- "Indenter-Languages",
- NULL);
-
- priv->cursor = g_object_new (IDE_TYPE_CURSOR,
- "ide-source-view", self,
- NULL);
-
- /* Create scroll mark used by movements and our scrolling helper */
- gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
- priv->scroll_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
-
- /* Marks used for definition highlights */
- priv->definition_highlight_start_mark =
- gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
- priv->definition_highlight_end_mark =
- gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer), NULL, &iter, TRUE);
-
- g_object_ref (priv->definition_highlight_start_mark);
- g_object_ref (priv->definition_highlight_end_mark);
-
- ide_source_view__buffer_notify_language_cb (self, NULL, buffer);
- ide_source_view__buffer_notify_file_settings_cb (self, NULL, buffer);
- ide_source_view__buffer_notify_style_scheme_cb (self, NULL, buffer);
- ide_source_view__buffer__notify_can_redo (self, NULL, buffer);
- ide_source_view__buffer__notify_can_undo (self, NULL, buffer);
- ide_source_view_real_set_mode (self, NULL, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
-
- insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
- ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_unbind_buffer (IdeSourceView *self,
- IdeSignalGroup *group)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (DZL_IS_SIGNAL_GROUP (group));
-
- if (priv->buffer == NULL)
- IDE_EXIT;
-
- priv->scroll_mark = NULL;
-
- if (priv->completion_blocked)
- {
- unblock_interactive (self);
- priv->completion_blocked = FALSE;
- }
-
- if (priv->cursor != NULL)
- {
- g_object_run_dispose (G_OBJECT (priv->cursor));
- g_clear_object (&priv->cursor);
- }
-
- ide_clear_and_destroy_object (&priv->indenter_adapter);
- g_clear_object (&priv->definition_highlight_start_mark);
- g_clear_object (&priv->definition_highlight_end_mark);
-
- ide_buffer_release (priv->buffer);
-
- IDE_EXIT;
-}
-
-static gboolean
-is_opening_char (gunichar ch)
-{
- switch (ch)
- {
- case '{':
- case '(':
- case '"':
- case '\'':
- case '[':
- return TRUE;
-
- default:
- return FALSE;
- }
-}
-
-static guint
-count_chars_on_line (IdeSourceView *view,
- gunichar expected_char,
- const GtkTextIter *iter)
-{
- GtkTextIter cur;
- guint count = 0;
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (view), 0);
- g_return_val_if_fail (iter, 0);
-
- cur = *iter;
-
- gtk_text_iter_set_line_offset (&cur, 0);
-
- while (!gtk_text_iter_ends_line (&cur))
- {
- gunichar ch;
-
- ch = gtk_text_iter_get_char (&cur);
-
- if (ch == '\\')
- {
- gtk_text_iter_forward_chars (&cur, 2);
- continue;
- }
-
- count += (ch == expected_char);
- gtk_text_iter_forward_char (&cur);
- }
-
- return count;
-}
-
-static gboolean
-is_xmlish (const gchar *lang_id)
-{
- return (g_strcmp0 (lang_id, "xml") == 0) ||
- (g_strcmp0 (lang_id, "html") == 0);
-
-}
-
-static void
-ide_source_view_maybe_overwrite (IdeSourceView *self,
- GtkTextIter *iter,
- const gchar *text,
- gint len)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextIter insert;
- GtkTextIter next;
- gunichar ch;
- gunichar next_ch;
- gunichar match;
- guint count_open;
- guint count_close;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (iter != NULL);
- g_assert (text != NULL);
- g_assert (len > 0);
-
- /*
- * Some auto-indenters will perform triggers on certain key-press that we
- * would hijack by otherwise "doing nothing" during this key-press. So to
- * avoid that, we actually delete the previous value and then allow this
- * key-press event to continue.
- */
-
- if (!priv->overwrite_braces)
- return;
-
- /*
- * WORKAROUND:
- *
- * If we are inside of a snippet, then let's not do anything. It really
- * messes with the position tracking. Once we can better integrate these
- * things, go ahead and remove this.
- */
- if (priv->snippets->length)
- return;
-
- /*
- * Ignore this if it wasn't a single character insertion.
- */
- if (len != 1)
- return;
-
- /*
- * Short circuit if there is already a selection.
- */
- buffer = gtk_text_iter_get_buffer (iter);
- if (gtk_text_buffer_get_has_selection (buffer))
- return;
-
- /*
- * @iter is pointing at the location we just inserted text. Since we
- * know we only inserted one character, lets move past it and compare
- * to see if we want to overwrite.
- */
- gtk_text_buffer_get_iter_at_mark (buffer, &insert, gtk_text_buffer_get_insert (buffer));
- ch = g_utf8_get_char (text);
- next_ch = gtk_text_iter_get_char (&insert);
-
- switch (ch)
- {
- case ')': case ']': case '}': case '"': case '\'': case ';':
- if (ch == next_ch)
- {
- if (ch == '"' || ch == '\'')
- break;
-
- switch (ch)
- {
- case ']': match = '['; break;
- case '}': match = '{'; break;
- case ')': match = '('; break;
- case '>': match = '<'; break;
- default: match = 0; break;
- }
-
- count_open = count_chars_on_line (self, match, iter);
- count_close = count_chars_on_line (self, ch, iter);
- if (count_close != count_open)
- break;
- }
- /* fall through */
- default:
- return;
- }
-
- next = insert;
-
- gtk_text_iter_forward_char (&next);
- gtk_text_buffer_delete (buffer, &insert, &next);
- *iter = insert;
-}
-
-static gboolean
-ide_source_view_maybe_insert_match (IdeSourceView *self,
- GdkEventKey *event)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkSourceBuffer *sbuf;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- const gchar *lang_id;
- GtkTextIter iter;
- GtkTextIter prev_iter;
- gunichar next_ch = 0;
- gchar ch[2] = { 0 };
-
- /*
- * TODO: I think we should put this into a base class for auto
- * indenters. It would make some things a lot more convenient, like
- * changing which characters we won't add matching characters for.
- */
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (event);
-
- if (priv->cursor != NULL && ide_cursor_is_enabled (priv->cursor))
- return FALSE;
-
- /*
- * If we are disabled, then do nothing.
- */
- if (!priv->insert_matching_brace)
- return FALSE;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- sbuf = GTK_SOURCE_BUFFER (buffer);
-
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- next_ch = gtk_text_iter_get_char (&iter);
-
- prev_iter = iter;
- gtk_text_iter_backward_chars (&prev_iter, 2);
-
- /*
- * If the source language has marked this region as a string or comment,
- * then do nothing.
- */
- if (gtk_source_buffer_iter_has_context_class (sbuf, &prev_iter, "string") ||
- gtk_source_buffer_iter_has_context_class (sbuf, &prev_iter, "comment"))
- return FALSE;
-
- switch (event->keyval)
- {
- case GDK_KEY_braceleft:
- ch[0] = '}';
- break;
-
- case GDK_KEY_parenleft:
- ch[0] = ')';
- break;
-
- case GDK_KEY_bracketleft:
- ch[0] = ']';
- break;
-
- case GDK_KEY_quotedbl:
- ch[0] = '"';
- break;
-
- case GDK_KEY_apostrophe:
- ch[0] = '\'';
- break;
-
- case GDK_KEY_less:
- if (!(lang_id = ide_buffer_get_language_id (IDE_BUFFER (buffer))) || !is_xmlish (lang_id))
- return FALSE;
- ch[0] = '>';
- break;
-
-#if 0
- /*
- * TODO: We should avoid this when we are in comments, etc. That will
- * require some communication with the syntax engine.
- */
- case GDK_KEY_quoteleft:
- case GDK_KEY_quoteright:
- ch = '\'';
- break;
-#endif
-
- default:
- return FALSE;
- }
-
- /*
- * Insert the match if one of the following is true:
- *
- * - We are at EOF
- * - The next character is whitespace
- * - The next character is punctuation
- * - The next character is not a opening brace.
- * - If the char is ", then there must be an even number already on
- * the current line.
- */
-
- if (!next_ch || g_unichar_isspace (next_ch) || (g_unichar_ispunct (next_ch) && !is_opening_char (next_ch)))
- {
- /*
- * Special case for working with double quotes.
- *
- * Ignore double quote if we just added enough to make there be an
- * even number on this line. However, if it was the first quote on
- * the line, we still need to include a second.
- */
- if (ch[0] == '"' || ch[0] == '\'')
- {
- guint count;
-
- count = count_chars_on_line (self, ch[0], &iter);
- if ((count > 1) && ((count % 2) == 0))
- return FALSE;
- }
-
- gtk_text_buffer_insert_at_cursor (buffer, ch, 1);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- gtk_text_iter_backward_char (&iter);
- gtk_text_buffer_select_range (buffer, &iter, &iter);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-ide_source_view_maybe_delete_match (IdeSourceView *self,
- GdkEventKey *event)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextIter iter;
- GtkTextIter prev;
- gunichar ch;
- gunichar match;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (event);
- g_assert (event->keyval == GDK_KEY_BackSpace);
-
- if (!priv->insert_matching_brace)
- return FALSE;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- prev = iter;
- if (!gtk_text_iter_backward_char (&prev))
- return FALSE;
-
- ch = gtk_text_iter_get_char (&prev);
-
- switch (ch)
- {
- case '[': match = ']'; break;
- case '{': match = '}'; break;
- case '(': match = ')'; break;
- case '"': match = '"'; break;
- case '\'': match = '\''; break;
- case '<': match = '>'; break;
- default: match = 0; break;
- }
-
- if (match && (gtk_text_iter_get_char (&iter) == match))
- {
- gtk_text_iter_forward_char (&iter);
- gtk_text_buffer_delete (buffer, &prev, &iter);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-ide_source_view_do_indent (IdeSourceView *self,
- GdkEventKey *event,
- IdeIndenter *indenter)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkWidget *widget = (GtkWidget *)self;
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- g_autofree gchar *indent = NULL;
- GtkTextIter begin;
- GtkTextIter end;
- gint cursor_offset = 0;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (priv->auto_indent == TRUE);
- g_assert (event);
- g_assert (!indenter || IDE_IS_INDENTER (indenter));
-
- buffer = gtk_text_view_get_buffer (text_view);
-
- /*
- * Insert into the buffer so the auto-indenter can see it. If
- * GtkSourceView:auto-indent is set, then we will end up with very
- * unpredictable results.
- */
- GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_press_event (widget, event);
-
- /*
- * Set begin and end to the position of the new insertion point.
- */
- insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->buffer));
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &begin, insert);
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &end, insert);
-
- /*
- * Let the formatter potentially set the replacement text. If we don't have a
- * formatter, use our simple formatter which tries to mimic GtkSourceView.
- */
- indent = ide_indenter_format (indenter, text_view, &begin, &end, &cursor_offset, event);
-
- if (indent != NULL)
- {
- /*
- * Insert the indention text.
- */
- gtk_text_buffer_begin_user_action (buffer);
- if (!gtk_text_iter_equal (&begin, &end))
- gtk_text_buffer_delete (buffer, &begin, &end);
- gtk_text_buffer_insert (buffer, &begin, indent, -1);
- gtk_text_buffer_end_user_action (buffer);
-
- /*
- * Make sure we stay in the visible rect.
- */
- ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
-
- /*
- * Place the cursor, as it could be somewhere within our indent text.
- */
- gtk_text_buffer_get_iter_at_mark (buffer, &begin, insert);
- if (cursor_offset > 0)
- gtk_text_iter_forward_chars (&begin, cursor_offset);
- else if (cursor_offset < 0)
- gtk_text_iter_backward_chars (&begin, ABS (cursor_offset));
- gtk_text_buffer_select_range (buffer, &begin, &begin);
- }
-
- IDE_EXIT;
-}
-
-static inline gboolean
-compare_keys (GdkKeymap *keymap,
- GdkEventKey *event,
- GtkBindingEntry *binding_entry,
- guint *new_keyval,
- GdkModifierType *state_consumed)
-{
- gdk_keymap_translate_keyboard_state (keymap,
- event->hardware_keycode, event->state, event->group,
- new_keyval, NULL, NULL, state_consumed);
-
- if (g_ascii_isupper (*new_keyval))
- {
- *new_keyval = gdk_keyval_to_lower (*new_keyval);
- *state_consumed &= ~GDK_SHIFT_MASK;
- }
-
- return (*new_keyval == binding_entry->keyval &&
- (event->state & ~(*state_consumed) & ALL_ACCELS_MASK) == (binding_entry->modifiers &
ALL_ACCELS_MASK));
-}
-
-static gboolean
-is_key_vim_binded (GtkWidget *widget,
- GdkEventKey *event,
- guint *new_keyval,
- GdkModifierType *state_consumed)
-{
- GdkKeymap *keymap;
- GtkBindingSet *binding_set;
- GtkBindingEntry *binding_entry;
- GtkStyleContext *context;
- GtkStateFlags state;
- GPtrArray *binding_set_array;
-
-
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (IDE_SOURCE_VIEW (widget));
-
- context = gtk_widget_get_style_context (GTK_WIDGET (priv->mode));
- keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
- state = gtk_widget_get_state_flags (GTK_WIDGET (priv->mode));
-
- gtk_style_context_get (context, state, "gtk-key-bindings", &binding_set_array, NULL);
- if (binding_set_array)
- {
- for (guint i = 0; i < binding_set_array->len; i++)
- {
- binding_set = g_ptr_array_index (binding_set_array, i);
- if (g_str_has_prefix (binding_set->set_name, "builder-vim"))
- {
- binding_entry = binding_set->entries;
- while (binding_entry)
- {
- if (compare_keys (keymap, event, binding_entry, new_keyval, state_consumed))
- {
- g_ptr_array_unref (binding_set_array);
- return TRUE;
- }
-
- binding_entry = binding_entry->set_next;
- }
- }
- }
-
- g_ptr_array_unref (binding_set_array);
- }
-
- return FALSE;
-}
-
-static void
-command_string_append_to (GString *command_str,
- guint keyval,
- GdkModifierType state)
-{
- if (state & GDK_CONTROL_MASK)
- g_string_append (command_str, "<ctrl>");
-
- if (state & GDK_SHIFT_MASK)
- g_string_append (command_str, "<shift>");
-
- if (state & GDK_MOD1_MASK)
- g_string_append (command_str, "<alt>");
-
- if ((keyval >= '!' && keyval <= '~' ) && keyval != GDK_KEY_bracketleft && keyval != GDK_KEY_bracketright)
- g_string_append_c (command_str, keyval);
- else if (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9)
- g_string_append_c (command_str, keyval - GDK_KEY_KP_0 + '0');
- else
- {
- g_string_append_c (command_str, '[');
- g_string_append (command_str, gdk_keyval_name (keyval));
- g_string_append_c (command_str, ']');
- }
-}
-
-static gboolean
-ide_source_view_do_mode (IdeSourceView *self,
- GdkEventKey *event)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autofree gchar *suggested_default = NULL;
- guint new_keyval;
- GdkModifierType state;
- GdkModifierType state_consumed;
- gboolean ret = FALSE;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->mode)
- {
- IdeSourceViewMode *mode;
- gboolean handled;
- gboolean remove = FALSE;
-
-#ifdef IDE_ENABLE_TRACE
- {
- gunichar ch = 0;
- gchar *name = NULL;
-
- g_object_get (priv->mode, "name", &name, NULL);
- if (event->string)
- ch = g_utf8_get_char (event->string);
- IDE_TRACE_MSG ("dispatching to mode \"%s\": (%s)",
- name, g_unichar_isprint (ch) ? event->string : "");
- g_free (name);
- }
-#endif
-
- /* hold a reference incase binding changes mode */
- mode = g_object_ref (priv->mode);
-
- if (is_key_vim_binded (GTK_WIDGET (self), event, &new_keyval, &state_consumed))
- {
- state = event->state & ~(state_consumed);
- command_string_append_to (priv->command_str, new_keyval, state);
- }
-
- /* lookup what this mode thinks our next default should be */
- suggested_default = g_strdup (ide_source_view_mode_get_default_mode (priv->mode));
-
- handled = _ide_source_view_mode_do_event (priv->mode, event, &remove);
-
- if (remove)
- {
- /* only remove mode if it is still active */
- if (priv->mode == mode)
- g_clear_object (&priv->mode);
- }
-
- g_object_unref (mode);
-
- if (handled)
- ret = TRUE;
- }
-
- if (priv->mode == NULL)
- ide_source_view_real_set_mode (self, suggested_default, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
-
- g_assert (priv->mode != NULL);
-
- if (ide_source_view_mode_get_mode_type (priv->mode) == IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT)
- g_string_erase (priv->command_str, 0, -1);
-
- if (ide_source_view_mode_get_keep_mark_on_char (priv->mode))
- {
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextMark *selection;
- GtkTextIter insert_iter;
- GtkTextIter selection_iter;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- insert = gtk_text_buffer_get_insert (buffer);
- selection = gtk_text_buffer_get_selection_bound (buffer);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert);
- gtk_text_buffer_get_iter_at_mark (buffer, &selection_iter, selection);
-
- if (gtk_text_iter_ends_line (&insert_iter) && !gtk_text_iter_starts_line (&insert_iter))
- {
- gtk_text_iter_backward_char (&insert_iter);
- if (gtk_text_buffer_get_has_selection (buffer))
- gtk_text_buffer_select_range (buffer, &insert_iter, &selection_iter);
- else
- gtk_text_buffer_select_range (buffer, &insert_iter, &insert_iter);
- }
- }
-
- gtk_text_view_reset_cursor_blink (GTK_TEXT_VIEW (self));
-
- return ret;
-}
-
-static gboolean
-is_modifier_key (GdkEventKey *event)
-{
- static const guint modifier_keyvals[] = {
- GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock,
- GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock, GDK_KEY_Control_L,
- GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R,
- GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R,
- GDK_KEY_Hyper_L, GDK_KEY_Hyper_R, GDK_KEY_ISO_Level3_Shift,
- GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group,
- GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group,
- GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key,
- GDK_KEY_Scroll_Lock,
- 0
- };
- const guint *ac_val;
-
- ac_val = modifier_keyvals;
- while (*ac_val)
- {
- if (event->keyval == *ac_val++)
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-ide_source_view_key_press_event (GtkWidget *widget,
- GdkEventKey *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- IdeSnippet *snippet;
- gboolean ret = FALSE;
- guint change_sequence;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- insert = gtk_text_buffer_get_insert (buffer);
-
- /*
- * If we are waiting for input for a modifier key, dispatch it now.
- */
- if (priv->waiting_for_capture)
- {
- if (!is_modifier_key (event))
- {
- guint new_keyval;
- GdkModifierType state_consumed;
- GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
-
- _ide_source_view_set_modifier (self, gdk_keyval_to_unicode (event->keyval));
- gdk_keymap_translate_keyboard_state (keymap,
- event->hardware_keycode, event->state, event->group,
- &new_keyval, NULL, NULL, &state_consumed);
-
- command_string_append_to (priv->command_str, new_keyval, event->state & ~(state_consumed));
- }
-
- return TRUE;
- }
-
- /*
- * If we got Control alone, with no key, and the completion window is
- * visible, then request that it make itself less visible.
- */
- if (event->keyval == GDK_KEY_Control_L &&
- event->state == 0 &&
- ide_completion_is_visible (priv->completion))
- {
- priv->did_ctrl_opacity = TRUE;
- return GDK_EVENT_STOP;
- }
-
- priv->did_ctrl_opacity = FALSE;
-
- /*
- * Are we currently recording a macro? If so lets stash the event for later.
- */
- if (priv->recording_macro)
- ide_source_view_capture_record_event (priv->capture, (GdkEvent *)event,
- priv->count, priv->modifier);
-
- /*
- * Check our current change sequence. If the buffer has changed during the
- * key-press handler, we'll refocus our selves at the insert caret.
- */
- change_sequence = priv->change_sequence;
-
- priv->in_key_press++;
-
- /*
- * If we are in a non-default mode, dispatch the event to the mode. This allows custom
- * keybindings like Emacs and Vim to be implemented using gtk-bindings CSS.
- */
- if (ide_source_view_do_mode (self, event))
- {
- ret = TRUE;
- goto cleanup;
- }
-
- /*
- * Handle movement through the tab stops of the current snippet if needed.
- */
- if (NULL != (snippet = g_queue_peek_head (priv->snippets)))
- {
- switch ((gint) event->keyval)
- {
- case GDK_KEY_Escape:
- ide_source_view_block_handlers (self);
- ide_source_view_pop_snippet (self);
- ide_source_view_scroll_to_insert (self);
- ide_source_view_unblock_handlers (self);
- ret = TRUE;
- goto cleanup;
-
- case GDK_KEY_KP_Tab:
- case GDK_KEY_Tab:
- if ((event->state & GDK_SHIFT_MASK) == 0)
- {
- ide_source_view_block_handlers (self);
- if (!ide_snippet_move_next (snippet))
- ide_source_view_pop_snippet (self);
- ide_completion_cancel (priv->completion);
- /* TODO: ask snippet if we should auto-display completion options? */
- ide_source_view_scroll_to_insert (self);
- ide_source_view_unblock_handlers (self);
- ret = TRUE;
- goto cleanup;
- }
- /* Fallthrough */
- case GDK_KEY_ISO_Left_Tab:
- ide_source_view_block_handlers (self);
- ide_snippet_move_previous (snippet);
- ide_source_view_scroll_to_insert (self);
- ide_source_view_unblock_handlers (self);
- ret = TRUE;
- goto cleanup;
-
- default:
- break;
- }
- }
-
- /*
- * If we are backspacing, and the next character is the matching brace,
- * then we might want to delete it too.
- */
- if ((event->keyval == GDK_KEY_BackSpace) && !gtk_text_buffer_get_has_selection (buffer))
- {
- if (ide_source_view_maybe_delete_match (self, event))
- {
- ret = TRUE;
- goto cleanup;
- }
- }
-
- /*
- * If we have an auto-indenter and the event is for a trigger key, then we
- * chain up to the parent class to insert the character, and then let the
- * auto-indenter fix things up.
- */
- if (priv->buffer != NULL &&
- priv->auto_indent &&
- (priv->cursor == NULL || !ide_cursor_is_enabled (priv->cursor)))
- {
- IdeIndenter *indenter = ide_source_view_get_indenter (self);
-
- /*
- * Indenter may be NULL and that is okay, the IdeIdenter API
- * knows how to deal with that situation by emulating GtkSourceView
- * indentation style.
- */
-
- if (ide_indenter_is_trigger (indenter, event))
- {
- ide_source_view_do_indent (self, event, indenter);
- ret = TRUE;
- goto cleanup;
- }
- }
-
- /*
- * If repeat-with-count is set, we need to repeat the insertion multiple times.
- */
- if (priv->count &&
- priv->mode &&
- ide_source_view_mode_get_repeat_insert_with_count (priv->mode))
- {
- for (gint i = MAX (1, priv->count); i > 0; i--)
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_press_event (widget, event);
- priv->count = 0;
- }
- else
- {
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_press_event (widget, event);
- }
-
- /*
- * If we just inserted ({["', we might want to insert a matching close.
- */
- if (ret)
- ide_source_view_maybe_insert_match (self, event);
-
- /*
- * Only scroll to the insert mark if we made a change.
- */
- if (priv->change_sequence != change_sequence)
- ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
-
-cleanup:
- priv->in_key_press--;
-
- return ret;
-}
-
-static gboolean
-ide_source_view_key_release_event (GtkWidget *widget,
- GdkEventKey *event)
-{
- IdeSourceView *self = (IdeSourceView *) widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gboolean ret;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->key_release_event (widget, event);
-
- if (priv->did_ctrl_opacity)
- {
- IdeCompletionDisplay *display = ide_completion_get_display (priv->completion);
-
- if (event->keyval == GDK_KEY_Control_L &&
- event->state == GDK_CONTROL_MASK &&
- ide_completion_is_visible (priv->completion))
- {
- if (gtk_widget_get_opacity (GTK_WIDGET (display)) == 1.0)
- dzl_object_animate (display, DZL_ANIMATION_LINEAR, 250, NULL, "opacity", 0.1, NULL);
- else
- dzl_object_animate (display, DZL_ANIMATION_LINEAR, 250, NULL, "opacity", 1.0, NULL);
- }
-
- priv->did_ctrl_opacity = FALSE;
- }
-
- if (priv->definition_src_location)
- ide_source_view_reset_definition_highlight (self);
-
- return ret;
-}
-
-static gboolean
-ide_source_view_process_press_on_definition (IdeSourceView *self,
- GdkEventButton *event)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextIter iter;
- GtkTextWindowType window_type;
- gint buffer_x;
- gint buffer_y;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (event != NULL);
-
- window_type = gtk_text_view_get_window_type (text_view, event->window);
- gtk_text_view_window_to_buffer_coords (text_view,
- window_type,
- event->x,
- event->y,
- &buffer_x,
- &buffer_y);
- gtk_text_view_get_iter_at_location (text_view,
- &iter,
- buffer_x,
- buffer_y);
-
- if (priv->definition_src_location != NULL)
- {
- GtkTextIter definition_highlight_start;
- GtkTextIter definition_highlight_end;
-
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
- &definition_highlight_start,
- priv->definition_highlight_start_mark);
-
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
- &definition_highlight_end,
- priv->definition_highlight_end_mark);
-
- if (gtk_text_iter_in_range (&iter, &definition_highlight_start, &definition_highlight_end))
- {
- g_autoptr(IdeLocation) src_location = NULL;
-
- src_location = g_object_ref (priv->definition_src_location);
- ide_source_view_reset_definition_highlight (self);
- g_signal_emit (self, signals [FOCUS_LOCATION], 0, src_location);
- }
-
- ide_source_view_reset_definition_highlight (self);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-ide_source_view_real_button_press_event (GtkWidget *widget,
- GdkEventButton *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = (GtkTextView *)widget;
- gboolean ret;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_VIEW (text_view));
-
- if (ide_source_view_process_press_on_definition (self, event))
- return TRUE;
-
- if (event->button == GDK_BUTTON_PRIMARY)
- {
- if (event->state & GDK_CONTROL_MASK)
- {
- if (!ide_cursor_is_enabled (priv->cursor))
- ide_cursor_add_cursor (priv->cursor, IDE_CURSOR_SELECT);
- }
- else if (ide_cursor_is_enabled (priv->cursor))
- {
- ide_cursor_remove_cursors (priv->cursor);
- }
- }
-
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->button_press_event (widget, event);
-
- /*
- * Keep mark on the last character if the sourceviewmode dictates such.
- */
- if (gtk_widget_has_focus (widget) &&
- priv->mode &&
- ide_source_view_mode_get_keep_mark_on_char (priv->mode))
- {
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextMark *selection;
- GtkTextIter iter;
- GtkTextIter iter2;
-
- buffer = gtk_text_view_get_buffer (text_view);
- insert = gtk_text_buffer_get_insert (buffer);
- selection = gtk_text_buffer_get_selection_bound (buffer);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter2, selection);
-
- if (gtk_text_iter_ends_line (&iter) && !gtk_text_iter_starts_line (&iter))
- {
- GtkTextIter prev = iter;
-
- gtk_text_iter_backward_char (&prev);
- if (gtk_text_iter_equal (&iter, &iter2))
- gtk_text_buffer_select_range (buffer, &prev, &prev);
- }
- }
-
- /*
- * Update our target column so movements don't cause us to revert
- * to the previous column.
- */
- ide_source_view_save_column (self);
-
- return ret;
-}
-
-static gboolean
-ide_source_view_real_button_release_event (GtkWidget *widget,
- GdkEventButton *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gboolean ret;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->button_release_event (widget, event);
-
- if ((event->button == GDK_BUTTON_PRIMARY) && (event->state & GDK_CONTROL_MASK))
- ide_cursor_add_cursor (priv->cursor, IDE_CURSOR_SELECT);
-
- return ret;
-}
-
-static gboolean
-ide_source_get_word_from_iter (const GtkTextIter *iter,
- GtkTextIter *word_start,
- GtkTextIter *word_end)
-{
- /* Just using forward/backward to word start/end is not enough
- * because _ break words when using those functions while they
- * are commonly used in the same word in code */
- *word_start = *iter;
- *word_end = *iter;
-
- do
- {
- const gunichar c = gtk_text_iter_get_char (word_end);
- if (!(g_unichar_isalnum (c) || c == '_'))
- break;
- }
- while (gtk_text_iter_forward_char (word_end));
-
- if (gtk_text_iter_equal (word_start, word_end))
- {
- /* Iter is not inside a word */
- return FALSE;
- }
-
- while (gtk_text_iter_backward_char (word_start))
- {
- const gunichar c = gtk_text_iter_get_char (word_start);
- if (!(g_unichar_isalnum (c) || c == '_'))
- {
- gtk_text_iter_forward_char (word_start);
- break;
- }
- }
-
- return (!gtk_text_iter_equal (word_start, word_end));
-}
-
-static void
-ide_source_view_get_definition_on_mouse_over_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- g_autoptr(DefinitionHighlightData) data = user_data;
- IdeSourceViewPrivate *priv;
- IdeBuffer *buffer = (IdeBuffer *)object;
- g_autoptr(IdeSymbol) symbol = NULL;
- g_autoptr(GError) error = NULL;
- IdeLocation *srcloc;
- IdeSymbolKind kind;
-
- IDE_ENTRY;
-
- g_assert (data != NULL);
- g_assert (IDE_IS_BUFFER (buffer));
- g_assert (IDE_IS_SOURCE_VIEW (data->self));
-
- priv = ide_source_view_get_instance_private (data->self);
-
- priv->waiting_for_symbol = FALSE;
-
- symbol = ide_buffer_get_symbol_at_location_finish (buffer, result, &error);
-
- if (symbol == NULL)
- {
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
- g_warning ("%s", error->message);
- IDE_EXIT;
- }
-
- /* Short circuit if the async operation completed after we closed */
- if (priv->buffer == NULL)
- IDE_EXIT;
-
- kind = ide_symbol_get_kind (symbol);
-
- srcloc = ide_symbol_get_location (symbol);
-
- if (srcloc == NULL)
- srcloc = ide_symbol_get_header_location (symbol);
-
- if (srcloc != NULL)
- {
- GtkTextIter word_start;
- GtkTextIter word_end;
-
- if (priv->definition_src_location != NULL && priv->definition_src_location != srcloc)
- g_clear_object (&priv->definition_src_location);
-
- if (priv->definition_src_location == NULL)
- priv->definition_src_location = g_object_ref (srcloc);
-
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
- &word_start, data->word_start_mark);
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
- &word_end, data->word_end_mark);
-
- if (kind == IDE_SYMBOL_KIND_HEADER)
- {
- GtkTextIter line_start = word_start;
- GtkTextIter line_end = word_end;
- g_autofree gchar *line_text = NULL;
- g_autoptr (GMatchInfo) matchInfo = NULL;
-
- gtk_text_iter_set_line_offset (&line_start, 0);
- gtk_text_iter_forward_to_line_end (&line_end);
-
- line_text = gtk_text_iter_get_visible_text (&line_start,&line_end);
-
- g_regex_match (priv->include_regex, line_text, 0, &matchInfo);
-
- if (g_match_info_matches (matchInfo))
- {
- gint start_pos;
- gint end_pos;
- g_match_info_fetch_pos (matchInfo,
- 0,
- &start_pos,
- &end_pos);
- word_start = line_start;
- word_end = line_start;
-
- gtk_text_iter_set_line_index (&word_start, start_pos);
- gtk_text_iter_set_line_index (&word_end, end_pos);
- }
- }
-
- gtk_text_buffer_apply_tag_by_name (GTK_TEXT_BUFFER (priv->buffer),
- TAG_DEFINITION, &word_start, &word_end);
-
- if (priv->definition_highlight_start_mark != NULL)
- gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (priv->buffer),
- priv->definition_highlight_start_mark,
- &word_start);
-
- if (priv->definition_highlight_end_mark != NULL)
- gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (priv->buffer),
- priv->definition_highlight_end_mark,
- &word_end);
-
- ide_source_view_set_cursor_from_name (data->self, "pointer");
- }
- else
- ide_source_view_reset_definition_highlight (data->self);
-
- IDE_EXIT;
-}
-
-static gboolean
-ide_source_view_real_motion_notify_event (GtkWidget *widget,
- GdkEventMotion *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextIter iter;
- GtkTextIter start_iter;
- GtkTextIter line_start_iter;
- GtkTextIter end_iter;
- gunichar ch;
- gint buffer_x;
- gint buffer_y;
- GtkTextWindowType window_type;
- DefinitionHighlightData *data;
- gboolean word_found = FALSE;
- gboolean ret;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->motion_notify_event (widget, event);
-
- if ((event->state & ALL_ACCELS_MASK) != DEFINITION_HIGHLIGHT_MODIFIER)
- {
- if (priv->definition_src_location)
- ide_source_view_reset_definition_highlight (self);
-
- return ret;
- }
-
- window_type = gtk_text_view_get_window_type (GTK_TEXT_VIEW (self), event->window);
- gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (self),
- window_type,
- event->x,
- event->y,
- &buffer_x,
- &buffer_y);
- gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (self),
- &iter,
- buffer_x,
- buffer_y);
-
- /* Workaround about a Clang bug where <> includes are not correctly reported */
- line_start_iter = iter;
- gtk_text_iter_set_line_offset (&line_start_iter, 0);
-
- if (gtk_text_iter_ends_line (&line_start_iter))
- goto cleanup;
-
- while ((ch = gtk_text_iter_get_char (&line_start_iter)) &&
- g_unichar_isspace (ch) &&
- gtk_text_iter_forward_char (&line_start_iter))
- ;
-
- if (ch == '#')
- {
- g_autofree gchar *str = NULL;
- GtkTextIter sharp_iter = line_start_iter;
- GtkTextIter line_end_iter = iter;
-
- gtk_text_iter_forward_char (&line_start_iter);
- gtk_text_iter_forward_to_line_end (&line_end_iter);
- str = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (priv->buffer), &line_start_iter, &line_end_iter,
FALSE);
- g_strchug (str);
- if (g_str_has_prefix (str, "include"))
- {
- iter = start_iter = sharp_iter;
- end_iter = line_end_iter;
- word_found = TRUE;
- }
- }
-
- if (!word_found && !ide_source_get_word_from_iter (&iter, &start_iter, &end_iter))
- goto cleanup;
-
- if (priv->definition_src_location)
- {
- GtkTextIter definition_highlight_start;
- GtkTextIter definition_highlight_end;
-
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
- &definition_highlight_start,
- priv->definition_highlight_start_mark);
-
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer),
- &definition_highlight_end,
- priv->definition_highlight_end_mark);
-
- if (gtk_text_iter_equal (&definition_highlight_start, &start_iter) &&
- gtk_text_iter_equal (&definition_highlight_end, &end_iter))
- return ret;
-
- ide_source_view_reset_definition_highlight (self);
- }
-
- /* Skip work if we're already active */
- if (priv->waiting_for_symbol)
- return ret;
-
- priv->waiting_for_symbol = TRUE;
-
- data = g_slice_new0 (DefinitionHighlightData);
- data->self = self;
- data->word_start_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (priv->buffer),
- NULL, &start_iter, TRUE);
- data->word_end_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (priv->buffer),
- NULL, &end_iter, TRUE);
-
- g_object_ref (data->self);
- g_object_ref (data->word_start_mark);
- g_object_ref (data->word_end_mark);
-
- ide_buffer_get_symbol_at_location_async (priv->buffer,
- &iter,
- NULL,
- ide_source_view_get_definition_on_mouse_over_cb,
- data);
-
- return ret;
-
-cleanup:
- ide_source_view_reset_definition_highlight (self);
- return ret;
-}
-
-static void
-ide_source_view_real_add_cursor (IdeSourceView *self,
- IdeCursorType type)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_cursor_add_cursor (priv->cursor, type);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_remove_cursors (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_cursor_remove_cursors (priv->cursor);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_style_updated (GtkWidget *widget)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- PangoContext *context;
- PangoLayout *layout;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- GTK_WIDGET_CLASS (ide_source_view_parent_class)->style_updated (widget);
-
- context = gtk_widget_get_pango_context (widget);
- layout = pango_layout_new (context);
- pango_layout_set_text (layout, "X", 1);
- pango_layout_get_pixel_size (layout, &priv->cached_char_width, &priv->cached_char_height);
- g_object_unref (layout);
-}
-
-static void
-ide_source_view_real_append_to_count (IdeSourceView *self,
- gint digit)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- g_return_if_fail (digit >= 0);
- g_return_if_fail (digit <= 9);
-
- priv->count = (priv->count * 10) + digit;
-}
-
-static void
-ide_source_view_real_capture_modifier (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- priv->waiting_for_capture = TRUE;
- while ((priv->modifier == 0) && gtk_widget_has_focus (GTK_WIDGET (self)))
- gtk_main_iteration ();
- priv->waiting_for_capture = FALSE;
-}
-
-static void
-ide_source_view_real_change_case (IdeSourceView *self,
- GtkSourceChangeCaseType type)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextIter begin;
- GtkTextIter end;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_cursor_remove_cursors (priv->cursor);
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
-
- if (GTK_SOURCE_IS_BUFFER (buffer))
- gtk_source_buffer_change_case (GTK_SOURCE_BUFFER (buffer), type, &begin, &end);
-}
-
-static void
-ide_source_view_real_clear_count (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- priv->count = 0;
-}
-
-static void
-ide_source_view_real_clear_modifier (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- priv->modifier = 0;
-}
-
-static void
-ide_source_view_real_clear_selection (IdeSourceView *self)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextIter iter;
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
-
- buffer = gtk_text_view_get_buffer (text_view);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
-}
-
-static void
-ide_source_view_real_cycle_completion (IdeSourceView *self,
- GtkDirectionType direction)
-{
- IdeCompletion *completion;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- completion = ide_source_view_get_completion (self);
-
- if (!ide_completion_is_visible (completion))
- {
- ide_completion_show (completion);
- return;
- }
-
- switch (direction)
- {
- case GTK_DIR_TAB_FORWARD:
- case GTK_DIR_DOWN:
- ide_completion_move_cursor (completion, GTK_MOVEMENT_DISPLAY_LINES, 1);
- break;
-
- case GTK_DIR_TAB_BACKWARD:
- case GTK_DIR_UP:
- ide_completion_move_cursor (completion, GTK_MOVEMENT_DISPLAY_LINES, -1);
- break;
-
- case GTK_DIR_LEFT:
- case GTK_DIR_RIGHT:
- default:
- break;
- }
-}
-
-static void
-ide_source_view_real_delete_selection (IdeSourceView *self)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GtkTextIter begin;
- GtkTextIter end;
- gboolean editable;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_VIEW (text_view));
-
- buffer = gtk_text_view_get_buffer (text_view);
- editable = gtk_text_view_get_editable (text_view);
-
- if (!editable)
- return;
-
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
-
- gtk_text_iter_order (&begin, &end);
-
- if (gtk_text_iter_is_end (&end) && gtk_text_iter_starts_line (&begin))
- {
- gtk_text_buffer_begin_user_action (buffer);
- gtk_text_iter_backward_char (&begin);
- gtk_text_buffer_delete (buffer, &begin, &end);
- gtk_text_buffer_end_user_action (buffer);
- }
- else
- {
- gtk_text_buffer_delete_selection (buffer, TRUE, editable);
- }
-
- ide_source_view_save_column (self);
-}
-
-static void
-ide_source_view_real_indent_selection (IdeSourceView *self,
- gint level)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkSourceView *source_view = (GtkSourceView *)self;
- GtkTextBuffer *buffer;
- GtkTextIter iter;
- GtkTextIter selection;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- /*
- * Use count to increase direction.
- */
- if (priv->count && level)
- level *= (gint)priv->count;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- if (level < 0)
- {
- for (; level < 0; level++)
- {
- if (gtk_text_buffer_get_selection_bounds (buffer, &iter, &selection))
- gtk_source_view_unindent_lines (source_view, &iter, &selection);
- }
- }
- else
- {
- for (; level > 0; level--)
- {
- if (gtk_text_buffer_get_selection_bounds (buffer, &iter, &selection))
- gtk_source_view_indent_lines (source_view, &iter, &selection);
- }
- }
-}
-
-static void
-ide_source_view_real_insert_modifier (IdeSourceView *self,
- gboolean use_count)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- gchar str[8] = { 0 };
- gint count = 1;
- gint len;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (!priv->modifier)
- return;
-
- if (use_count)
- count = MAX (1, priv->count);
-
- len = g_unichar_to_utf8 (priv->modifier, str);
- str [len] = '\0';
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- gtk_text_buffer_begin_user_action (buffer);
- for (gint i = 0; i < count; i++)
- gtk_text_buffer_insert_at_cursor (buffer, str, len);
- gtk_text_buffer_end_user_action (buffer);
-}
-
-static void
-ide_source_view_real_duplicate_entire_line (IdeSourceView *self)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextIter begin, end;
- gboolean selected;
- g_autofree gchar *text = NULL;
- g_autofree gchar *duplicate_line = NULL;
- GtkTextMark *cursor;
- GtkTextBuffer *buffer;
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (text_view);
- cursor = gtk_text_buffer_get_insert (buffer);
-
- gtk_text_buffer_begin_user_action (buffer);
-
- selected = gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
-
- if (selected)
- {
- duplicate_line = gtk_text_iter_get_text (&begin, &end);
- gtk_text_buffer_insert (buffer, &begin, duplicate_line, -1);
- }
- else
- {
- gtk_text_buffer_get_iter_at_mark (buffer, &begin, cursor);
- end = begin;
-
- gtk_text_iter_set_line_offset (&begin, 0);
-
- if (!gtk_text_iter_ends_line (&end))
- gtk_text_iter_forward_to_line_end (&end);
-
- if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&end))
- {
- text = gtk_text_iter_get_text (&begin, &end);
- duplicate_line = g_strconcat (text, "\n", NULL);
- gtk_text_buffer_insert (buffer, &begin, duplicate_line, -1);
- }
- }
-
- gtk_text_buffer_end_user_action (buffer);
-}
-
-static void
-ide_source_view_real_join_lines (IdeSourceView *self)
-{
- GtkTextBuffer *buffer;
- GtkTextMark *mark;
- GtkTextIter begin;
- GtkTextIter end;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- if (!GTK_SOURCE_IS_BUFFER (buffer))
- return;
-
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
- gtk_text_iter_order (&begin, &end);
-
- /*
- * We want to leave the cursor inbetween the joined lines, so lets create an
- * insert mark and delete it later after we reposition the cursor.
- */
- mark = gtk_text_buffer_create_mark (buffer, NULL, &end, TRUE);
-
- /* join lines and restore the insert mark inbetween joined lines. */
- gtk_text_buffer_begin_user_action (buffer);
- gtk_source_buffer_join_lines (GTK_SOURCE_BUFFER (buffer), &begin, &end);
- gtk_text_buffer_get_iter_at_mark (buffer, &end, mark);
- gtk_text_buffer_select_range (buffer, &end, &end);
- gtk_text_buffer_end_user_action (buffer);
-
- /* Remove our temporary mark. */
- gtk_text_buffer_delete_mark (buffer, mark);
-}
-
-static void
-ide_source_view_real_copy_clipboard_extended (IdeSourceView *self)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkClipboard *clipboard;
- GtkTextBuffer *buffer;
- GtkTextIter begin, end;
- g_autofree gchar *text = NULL;
- g_autofree gchar *new_text = NULL;
- gsize len;
-
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self), GDK_SELECTION_CLIPBOARD);
- buffer = gtk_text_view_get_buffer (text_view);
-
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
- if (gtk_text_iter_is_end (&end))
- {
- text = gtk_text_buffer_get_text (buffer, &begin, &end, FALSE);
- len = strlen (text);
- new_text = g_malloc (len + 1);
- memcpy (new_text, text, len);
- new_text[len] = '\n';
-
- gtk_clipboard_set_text (clipboard, new_text, len + 1);
- }
- else
- gtk_text_buffer_copy_clipboard (buffer, clipboard);
-}
-
-static void
-ide_source_view_real_paste_clipboard_extended (IdeSourceView *self,
- gboolean smart_lines,
- gboolean after_cursor,
- gboolean place_cursor_at_original)
-
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = (GtkTextView *)self;
- g_autofree gchar *text = NULL;
- GtkClipboard *clipboard;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextIter iter;
- guint target_line;
- guint target_line_column;
-
- /*
- * NOTE:
- *
- * In this function, we try to improve how pasting works in GtkTextView. There are some
- * semantics that make things easier by tracking the paste of an entire line versus small
- * snippets of text.
- *
- * Basically, we are implementing something close to Vim. However that is not a strict
- * requirement, just what we are starting with. In fact, the rest of the handling to be like vim
- * is handled within vim.css (for example, what character to leave the insert mark on).
- */
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (text_view);
- insert = gtk_text_buffer_get_insert (buffer);
-
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self), GDK_SELECTION_CLIPBOARD);
- text = gtk_clipboard_wait_for_text (clipboard);
- /* Possible mismatch between the clipboard content and the utf-8 converted text
- * so we set back the utf-8 text in the clipboard to be sure.
- */
- gtk_clipboard_set_text (clipboard, text, -1);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- target_line = gtk_text_iter_get_line (&iter);
- target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self), &iter);
-
- if (priv->count == 0)
- priv->count = 1;
-
- gtk_text_buffer_begin_user_action (buffer);
-
- for (; priv->count > 0; priv->count--)
- {
- /*
- * If we are pasting an entire line, we don't want to paste it at the current location. We want
- * to insert a new line after the current line, and then paste it there (so move the insert mark
- * first).
- */
- if (smart_lines && text && g_str_has_suffix (text, "\n"))
- {
- g_autofree gchar *trimmed = NULL;
-
- /*
- * WORKAROUND:
- *
- * This is a hack so that we can continue to use the paste code from within GtkTextBuffer.
- *
- * We needed to keep the trailing \n in the text so that we know when we are selecting whole
- * lines. We also need to insert a new line manually based on the context. Furthermore, we
- * need to remove the trailing line since we already added one.
- *
- * Terribly annoying, but the result is something that feels very nice, similar to Vim.
- */
- trimmed = g_strndup (text, strlen (text) - 1);
-
- if (after_cursor)
- {
- if (!gtk_text_iter_ends_line (&iter))
- gtk_text_iter_forward_to_line_end (&iter);
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- g_signal_emit_by_name (self, "insert-at-cursor", "\n");
- }
- else
- {
- gtk_text_iter_set_line_offset (&iter, 0);
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- g_signal_emit_by_name (self, "insert-at-cursor", "\n");
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- gtk_text_iter_backward_line (&iter);
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- }
-
- if (!place_cursor_at_original)
- {
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- target_line = gtk_text_iter_get_line (&iter);
- target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self),
- &iter);
- }
-
- gtk_clipboard_set_text (clipboard, trimmed, -1);
- GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->paste_clipboard (text_view);
- gtk_clipboard_set_text (clipboard, text, -1);
- }
- else
- {
- if (after_cursor)
- {
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- if (!gtk_text_iter_ends_line (&iter))
- gtk_text_iter_forward_char (&iter);
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- }
-
- GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->paste_clipboard (text_view);
-
- if (!place_cursor_at_original)
- {
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- target_line = gtk_text_iter_get_line (&iter);
- target_line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self),
- &iter);
- }
- }
-
- /* Revalidate the position on our next attempt through the paste */
- gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, target_line, 0);
- ide_source_view_get_iter_at_visual_column (self, target_line_column, &iter);
- }
-
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- gtk_text_buffer_end_user_action (buffer);
-
- if (priv->cursor != NULL)
- ide_cursor_clear_highlight (priv->cursor);
-}
-
-static void
-ide_source_view_real_selection_theatric (IdeSourceView *self,
- IdeSourceViewTheatric theatric)
-{
- GtkTextBuffer *buffer;
- GtkTextIter begin;
- GtkTextIter end;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert ((theatric == IDE_SOURCE_VIEW_THEATRIC_EXPAND) ||
- (theatric == IDE_SOURCE_VIEW_THEATRIC_SHRINK));
-
- if (!ide_source_view_can_animate (self))
- return;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
- gtk_text_iter_order (&begin, &end);
-
- if (gtk_text_iter_equal (&begin, &end))
- return;
-
- if (gtk_text_iter_starts_line (&end))
- gtk_text_iter_backward_char (&end);
-
- switch (theatric)
- {
- case IDE_SOURCE_VIEW_THEATRIC_EXPAND:
- animate_expand (self, &begin, &end);
- break;
-
- case IDE_SOURCE_VIEW_THEATRIC_SHRINK:
- animate_shrink (self, &begin, &end);
- break;
-
- default:
- break;
- }
-}
-
-static void
-ide_source_view_save_column (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextIter iter;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (text_view);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- priv->target_line_column = ide_source_view_get_visual_column (self, &iter);
-}
-
-static void
-ide_source_view_update_display_name (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- const gchar *display_name = NULL;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->mode != NULL)
- display_name = ide_source_view_mode_get_display_name (priv->mode);
-
- if (g_strcmp0 (display_name, priv->display_name) != 0)
- {
- g_free (priv->display_name);
- priv->display_name = g_strdup (display_name);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODE_DISPLAY_NAME]);
- }
-}
-
-static void
-ide_source_view_real_set_overwrite (IdeSourceView *self,
- gboolean overwrite)
-{
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- IDE_TRACE_MSG ("Setting overwrite to %s", overwrite ? "TRUE" : "FALSE");
-
- gtk_text_view_set_overwrite (GTK_TEXT_VIEW (self), overwrite);
-}
-
-static void
-ide_source_view_real_swap_selection_bounds (IdeSourceView *self)
-{
- GtkTextBuffer *buffer;
- GtkTextIter insert;
- GtkTextIter selection_bound;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_get_selection_bounds (buffer, &insert, &selection_bound);
- gtk_text_buffer_select_range (buffer, &selection_bound, &insert);
-}
-
-static void
-ide_source_view_real_move_error (IdeSourceView *self,
- GtkDirectionType dir)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeDiagnostics *diagnostics;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GFile *file;
- GtkTextIter iter;
- gboolean wrap_around = TRUE;
- gboolean (*movement) (GtkTextIter *) = NULL;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (!priv->buffer)
- return;
-
- if (!(diagnostics = ide_buffer_get_diagnostics (priv->buffer)))
- return;
-
- file = ide_buffer_get_file (priv->buffer);
-
- if (dir == GTK_DIR_RIGHT)
- dir = GTK_DIR_DOWN;
- else if (dir == GTK_DIR_LEFT)
- dir = GTK_DIR_UP;
-
- /*
- * TODO: This is not particularly very efficient. But I didn't feel like
- * plumbing access to the diagnostics set and duplicating most of
- * the code for getting a diagnostic at a line. Once the diagnostics
- * get support for fast lookups (bloom filter or something) then
- * we should change to that.
- */
-
- if (dir == GTK_DIR_DOWN)
- movement = gtk_text_iter_forward_line;
- else
- movement = gtk_text_iter_backward_line;
-
- buffer = GTK_TEXT_BUFFER (priv->buffer);
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
-
-wrapped:
- while (movement (&iter))
- {
- IdeDiagnostic *diag;
- guint line = gtk_text_iter_get_line (&iter);
-
- if ((diag = ide_diagnostics_get_diagnostic_at_line (diagnostics, file, line)))
- {
- IdeLocation *location;
-
- location = ide_diagnostic_get_location (diag);
-
- if (location)
- {
- guint line_offset;
-
- line_offset = ide_location_get_line_offset (location);
- gtk_text_iter_set_line_offset (&iter, 0);
- for (; line_offset; line_offset--)
- if (gtk_text_iter_ends_line (&iter) || !gtk_text_iter_forward_char (&iter))
- break;
-
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
- return;
- }
-
- break;
- }
- }
-
- if (wrap_around)
- {
- if (dir == GTK_DIR_DOWN)
- gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (priv->buffer), &iter);
- else
- gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (priv->buffer), &iter);
- wrap_around = FALSE;
- goto wrapped;
- }
-}
-
-static gboolean
-is_same_range (GtkTextIter *new_start,
- GtkTextIter *old_start,
- GtkTextIter *new_sel,
- GtkTextIter *old_sel)
-{
- if (gtk_text_iter_equal (new_start, old_start))
- return gtk_text_iter_equal (old_sel, new_sel);
-
- if (gtk_text_iter_equal (new_start, old_sel))
- return gtk_text_iter_equal (old_start, new_sel);
-
- return FALSE;
-}
-
-static void
-ide_source_view_restore_insert_mark (IdeSourceView *self,
- gboolean move_mark)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextIter iter;
- GtkTextIter selection;
- GtkTextIter old_iter;
- GtkTextIter old_selection;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->insert_mark_cleared)
- {
- priv->insert_mark_cleared = FALSE;
- return;
- }
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, priv->saved_line, 0);
- ide_source_view_get_iter_at_visual_column (self, priv->saved_line_column, &iter);
- gtk_text_buffer_get_iter_at_line_offset (buffer,
- &selection,
- priv->saved_selection_line,
- 0);
- ide_source_view_get_iter_at_visual_column (self,
- priv->saved_selection_line_column,
- &selection);
-
- gtk_text_buffer_get_selection_bounds (buffer, &old_iter, &old_selection);
-
- if (!is_same_range (&iter, &old_iter, &selection, &old_selection))
- gtk_text_buffer_select_range (buffer, &iter, &selection);
-
- if (move_mark)
- {
- GtkTextMark *insert;
-
- insert = gtk_text_buffer_get_insert (buffer);
- ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
- }
-}
-
-void
-_ide_source_view_clear_saved_mark (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->insert_mark_cleared = TRUE;
-}
-
-static void
-ide_source_view_real_save_insert_mark (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextMark *selection_bound;
- GtkTextIter iter;
- GtkTextIter selection;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- priv->insert_mark_cleared = FALSE;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- insert = gtk_text_buffer_get_insert (buffer);
- selection_bound = gtk_text_buffer_get_selection_bound (buffer);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
- gtk_text_buffer_get_iter_at_mark (buffer, &selection, selection_bound);
-
- priv->saved_line = gtk_text_iter_get_line (&iter);
- priv->saved_line_column = ide_source_view_get_visual_column (self, &iter);
- priv->saved_selection_line = gtk_text_iter_get_line (&selection);
- priv->saved_selection_line_column = ide_source_view_get_visual_column (self, &selection);
-
- priv->target_line_column = priv->saved_line_column;
-}
-
-static void
-ide_source_view_real_save_command (IdeSourceView *self)
-{
- GdkEvent *event;
- guint keyval;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- event = gtk_get_current_event ();
- if (event && gdk_event_get_keyval (event, &keyval))
- priv->command = (gunichar)keyval;
-}
-
-static void
-ide_source_view_real_save_search_char (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->modifier)
- priv->search_char = priv->modifier;
-}
-
-/* In string mode, the search act only on the current line,
- * search a string to the right if we are not already in one,
- * and only inner_left is used ( inner_right is set to it )
- */
-static void
-ide_source_view_real_select_inner (IdeSourceView *self,
- const gchar *inner_left,
- const gchar *inner_right,
- gboolean exclusive,
- gboolean string_mode)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gunichar unichar_inner_left;
- gunichar unichar_inner_right;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- unichar_inner_left = g_utf8_get_char (inner_left);
- unichar_inner_right = g_utf8_get_char (inner_right);
-
- _ide_source_view_select_inner (self,
- unichar_inner_left,
- unichar_inner_right,
- priv->count,
- exclusive,
- string_mode);
-}
-
-static void
-ide_source_view_real_select_tag (IdeSourceView *self,
- gboolean exclusive)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- _ide_source_view_select_tag (self, priv->count, exclusive);
-}
-
-static void
-ide_source_view_real_pop_selection (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextMark *selection_bound;
- GtkTextIter insert_iter;
- GtkTextIter selection_bound_iter;
- gpointer *data;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- data = g_queue_pop_head (priv->selections);
-
- if (!data)
- {
- g_warning ("request to pop selection that does not exist!");
- return;
- }
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- insert = gtk_text_buffer_get_insert (buffer);
- selection_bound = gtk_text_buffer_get_selection_bound (buffer);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, data [0]);
- gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound_iter, data [1]);
-
- gtk_text_buffer_move_mark (buffer, insert, &insert_iter);
- gtk_text_buffer_move_mark (buffer, selection_bound, &selection_bound_iter);
-
- gtk_text_buffer_delete_mark (buffer, data [0]);
- gtk_text_buffer_delete_mark (buffer, data [1]);
-
- g_object_unref (data [0]);
- g_object_unref (data [1]);
- g_free (data);
-}
-
-static void
-ide_source_view_real_push_selection (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextMark *selection_bound;
- GtkTextIter insert_iter;
- GtkTextIter selection_bound_iter;
- gpointer *data;
- gboolean left_gravity;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert);
-
- selection_bound = gtk_text_buffer_get_selection_bound (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound_iter, selection_bound);
-
- left_gravity = (gtk_text_iter_compare (&insert_iter, &selection_bound_iter) <= 0);
- insert = gtk_text_buffer_create_mark (buffer, NULL, &insert_iter, left_gravity);
-
- left_gravity = (gtk_text_iter_compare (&selection_bound_iter, &insert_iter) < 0);
- selection_bound = gtk_text_buffer_create_mark (buffer, NULL, &selection_bound_iter, left_gravity);
-
- data = g_new0 (gpointer, 2);
- data [0] = g_object_ref (insert);
- data [1] = g_object_ref (selection_bound);
-
- g_queue_push_head (priv->selections, data);
-}
-
-static void
-ide_source_view_real_push_snippet (IdeSourceView *self,
- IdeSnippet *snippet,
- const GtkTextIter *location)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autoptr(GFile) gparentfile = NULL;
- g_autoptr(IdeContext) ide_context = NULL;
- IdeSnippetContext *context;
- GFile *file = NULL;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (IDE_IS_SNIPPET (snippet));
- g_assert (location != NULL);
-
- context = ide_snippet_get_context (snippet);
-
- if (priv->buffer != NULL)
- {
- if ((file = ide_buffer_get_file (priv->buffer)))
- {
- g_autofree gchar *name = NULL;
- g_autofree gchar *path = NULL;
- g_autofree gchar *dirname = NULL;
-
- name = g_file_get_basename (file);
- gparentfile = g_file_get_parent (file);
- dirname = g_file_get_path (gparentfile);
- path = g_file_get_path (file);
- ide_snippet_context_add_variable (context, "filename", name);
- ide_snippet_context_add_variable (context, "dirname", dirname);
- ide_snippet_context_add_variable (context, "path", path);
- }
-
- if ((ide_context = ide_buffer_ref_context (priv->buffer)))
- {
- g_autoptr(GFile) workdir = NULL;
-
- workdir = ide_context_ref_workdir (ide_context);
- if (workdir && file)
- {
- g_autofree gchar *relative_path = NULL;
- relative_path = g_file_get_relative_path (workdir, file);
- ide_snippet_context_add_variable (context, "relative_path", relative_path);
- }
- if (workdir && gparentfile)
- {
- g_autofree gchar *relative_dirname = NULL;
- relative_dirname = g_file_get_relative_path (workdir, gparentfile);
- ide_snippet_context_add_variable (context, "relative_dirname", relative_dirname);
- }
- }
- }
-}
-
-static void
-ide_source_view_real_reindent (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- IdeIndenter *indenter;
- GtkTextIter begin;
- GtkTextIter end;
- GdkWindow *window;
- GtkTextIter iter;
- guint i;
- guint first_line;
- g_autoptr(GPtrArray) lines = NULL;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->buffer == NULL)
- return;
-
- /* indenter may be NULL and that is okay */
- indenter = ide_source_view_get_indenter (self);
-
- buffer = GTK_TEXT_BUFFER (priv->buffer);
- window = gtk_text_view_get_window (GTK_TEXT_VIEW (self), GTK_TEXT_WINDOW_TEXT);
-
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
- gtk_text_iter_order (&begin, &end);
-
- gtk_text_iter_set_line_offset (&begin, 0);
- first_line = gtk_text_iter_get_line (&begin);
-
- /* if the end position is at index 0 of the next line (common with
- * line mode in vim), then move it back to the end of the previous
- * line, since we don't really care about that next line.
- */
- if (gtk_text_iter_starts_line (&end) &&
- gtk_text_iter_get_line (&begin) != gtk_text_iter_get_line (&end))
- gtk_text_iter_backward_char (&end);
-
- if (!gtk_text_iter_ends_line (&end))
- gtk_text_iter_forward_to_line_end (&end);
-
- lines = g_ptr_array_new_with_free_func (g_free);
-
- if (gtk_text_iter_compare (&begin, &end) == 0)
- g_ptr_array_add (lines, g_strdup (""));
- else
- for (iter = begin;
- gtk_text_iter_compare (&iter, &end) < 0;
- gtk_text_iter_forward_line (&iter))
- {
- GtkTextIter line_end = iter;
- gchar *line;
-
- if (!gtk_text_iter_ends_line (&line_end))
- gtk_text_iter_forward_to_line_end (&line_end);
-
- line = gtk_text_iter_get_slice (&iter, &line_end);
- g_ptr_array_add (lines, g_strstrip (line));
- }
-
- gtk_text_buffer_begin_user_action (buffer);
-
- gtk_text_buffer_delete (buffer, &begin, &end);
-
- for (i = 0; i < lines->len; i++)
- {
- g_autofree gchar *indent = NULL;
- const gchar *line;
- GdkEventKey *event;
- gint cursor_offset;
-
- line = g_ptr_array_index (lines, i);
- event = dzl_gdk_synthesize_event_key (window, '\n');
- indent = ide_indenter_format (indenter, GTK_TEXT_VIEW (self), &begin, &end, &cursor_offset, event);
- gdk_event_free ((GdkEvent *)event);
-
- if (indent != NULL)
- {
- if (!gtk_text_iter_equal (&begin, &end))
- gtk_text_buffer_delete (buffer, &begin, &end);
-
- gtk_text_buffer_insert (buffer, &begin, indent, -1);
- gtk_text_buffer_insert (buffer, &begin, line, -1);
-
- if (i != lines->len - 1)
- gtk_text_buffer_insert (buffer, &begin, "\n", -1);
- }
-
- end = begin;
- }
-
- gtk_text_buffer_end_user_action (buffer);
-
- /* Advance to first non-whitespace */
- gtk_text_iter_set_line (&begin, first_line);
- while (!gtk_text_iter_ends_line (&begin) &&
- g_unichar_isspace (gtk_text_iter_get_char (&begin)))
- gtk_text_iter_forward_char (&begin);
-
- gtk_text_buffer_select_range (buffer, &begin, &begin);
-}
-
-static void
-ide_source_view_set_overscroll_num_lines (IdeSourceView *self,
- gint num_lines)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gint height;
- gint new_margin;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- priv->overscroll_num_lines = num_lines;
-
- /* Do nothing if there is no height yet */
- if (!(height = gtk_widget_get_allocated_height (GTK_WIDGET (self))))
- return;
-
- new_margin = priv->overscroll_num_lines * priv->cached_char_height;
-
- if (new_margin < 0)
- {
- new_margin = height + new_margin;
- if (new_margin < 0)
- new_margin = height - priv->cached_char_height;
- }
-
- new_margin = CLAMP (new_margin, 0, MAX (height - priv->cached_char_height, 0));
-
- /* ensure enough space for the overlay scrollbars and their interactive
- * trough when scrolled to the end.
- */
- if (new_margin < 16)
- new_margin = 16;
-
- g_object_set (self,
- "bottom-margin", new_margin,
- NULL);
-}
-
-static void
-ide_source_view_constructed (GObject *object)
-{
- IdeSourceView *self = (IdeSourceView *)object;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- G_OBJECT_CLASS (ide_source_view_parent_class)->constructed (object);
-
- _ide_source_view_init_shortcuts (self);
-
- ide_source_view_real_set_mode (self, NULL, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
-
- priv->definition_src_location = NULL;
- ide_source_view_reset_definition_highlight (self);
-
- priv->completion = _ide_completion_new (GTK_SOURCE_VIEW (self));
-
- /* Disable sourceview completion always */
- gtk_source_completion_block_interactive (gtk_source_view_get_completion (GTK_SOURCE_VIEW (self)));
-
- /* Disable completion until focus-in-event */
- block_interactive (self);
-}
-
-static void
-ide_source_view_real_insert_at_cursor (GtkTextView *text_view,
- const gchar *str)
-{
- IdeSourceView *self = (IdeSourceView *)text_view;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (str);
-
- GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->insert_at_cursor (text_view, str);
-
- buffer = gtk_text_view_get_buffer (text_view);
- insert = gtk_text_buffer_get_insert (buffer);
- ide_source_view_scroll_mark_onscreen (self, insert, FALSE, 0, 0);
-}
-
-static void
-ide_source_view_real_sort (IdeSourceView *self,
- gboolean ignore_case,
- gboolean reverse)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GtkTextIter begin;
- GtkTextIter end;
- GtkSourceSortFlags sort_flags = GTK_SOURCE_SORT_FLAGS_NONE;
-
- g_assert (GTK_TEXT_VIEW (self));
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (text_view);
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
-
- if (gtk_text_iter_equal (&begin, &end))
- gtk_text_buffer_get_bounds (buffer, &begin, &end);
-
- if (!ignore_case)
- sort_flags |= GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE;
-
- if (reverse)
- sort_flags |= GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER;
-
- gtk_source_buffer_sort_lines (GTK_SOURCE_BUFFER (buffer),
- &begin,
- &end,
- sort_flags,
- 0);
-}
-
-static void
-ide_source_view_draw_snippet_background (IdeSourceView *self,
- cairo_t *cr,
- IdeSnippet *snippet,
- gint width)
-{
- GtkTextBuffer *buffer;
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextIter begin;
- GtkTextIter end;
- GtkTextMark *mark_begin;
- GtkTextMark *mark_end;
- GdkRectangle r;
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (cr);
- g_assert (IDE_IS_SNIPPET (snippet));
-
- buffer = gtk_text_view_get_buffer (text_view);
-
- mark_begin = ide_snippet_get_mark_begin (snippet);
- mark_end = ide_snippet_get_mark_end (snippet);
-
- if (!mark_begin || !mark_end)
- return;
-
- gtk_text_buffer_get_iter_at_mark (buffer, &begin, mark_begin);
- gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);
-
- get_rect_for_iters (text_view, &begin, &end, &r, GTK_TEXT_WINDOW_TEXT);
-
- gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_TEXT, r.x, r.y, &r.x, &r.y);
-
- dzl_cairo_rounded_rectangle (cr, &r, 5, 5);
-
- cairo_fill (cr);
-}
-
-static void
-ide_source_view_draw_snippets_background (IdeSourceView *self,
- cairo_t *cr)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = GTK_TEXT_VIEW (self);
- GdkWindow *window;
- gint width;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (cr);
-
- window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT);
- width = gdk_window_get_width (window);
-
- cairo_save (cr);
-
- gdk_cairo_set_source_rgba (cr, &priv->snippet_area_background_rgba);
-
- for (guint i = 0; i < priv->snippets->length; i++)
- {
- IdeSnippet *snippet = g_queue_peek_nth (priv->snippets, i);
-
- ide_source_view_draw_snippet_background (self,
- cr,
- snippet,
- width - ((priv->snippets->length - i) * 10));
- }
-
- cairo_restore (cr);
-}
-
-static void
-ide_source_view_real_draw_layer (GtkTextView *text_view,
- GtkTextViewLayer layer,
- cairo_t *cr)
-{
- IdeSourceView *self = (IdeSourceView *)text_view;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (cr);
-
- GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->draw_layer (text_view, layer, cr);
-
- if (layer == GTK_TEXT_VIEW_LAYER_BELOW_TEXT)
- {
- if (priv->snippets->length)
- ide_source_view_draw_snippets_background (self, cr);
-
- if (g_signal_has_handler_pending (self, signals [DRAW_BUBBLES], 0, FALSE))
- {
- GdkRectangle rect;
-
- gtk_text_view_get_visible_rect (text_view, &rect);
-
- cairo_save (cr);
- cairo_translate (cr, rect.x, rect.y);
- g_signal_emit (self, signals [DRAW_BUBBLES], 0, cr);
- cairo_restore (cr);
- }
- }
-}
-
-static gboolean
-ide_source_view_focus_in_event (GtkWidget *widget,
- GdkEventFocus *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gboolean ret;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /* Restore the completion window now that we have regained focus. */
- unblock_interactive (self);
-
- /* Force size allocation immediately if we have something queued. */
- if (priv->delay_size_allocate_chainup)
- {
- g_clear_handle_id (&priv->delay_size_allocate_chainup, g_source_remove);
- ide_source_view_do_size_allocate_hack_cb (self);
- }
-
- /*
- * Restore the insert mark, but ignore selections (since we cant ensure they
- * will stay looking selected, as the other frame could be a view into our
- * own buffer).
- */
- if (get_selection_owner (self) != self)
- {
- priv->saved_selection_line = priv->saved_line;
- priv->saved_selection_line_column = priv->saved_line_column;
- }
-
- ide_source_view_restore_insert_mark (self, FALSE);
-
- /* restore line highlight if enabled */
- if (priv->highlight_current_line)
- gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (self), TRUE);
-
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->focus_in_event (widget, event);
-
- return ret;
-}
-
-static gboolean
-ide_source_view_focus_out_event (GtkWidget *widget,
- GdkEventFocus *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gboolean ret;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /* save our insert mark for when we focus back in. it could have moved if
- * another view into the same buffer has caused the insert mark to jump.
- */
- ide_source_view_real_save_insert_mark (self);
-
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->focus_out_event (widget, event);
-
- /*
- * Block the completion window while we are not focused. It confuses text
- * insertion and such.
- */
- block_interactive (self);
-
- /* We don't want highlight-current-line unless the widget is in focus, so
- * disable it until we get re-focused.
- */
- gtk_source_view_set_highlight_current_line (GTK_SOURCE_VIEW (self), FALSE);
-
- if (priv->cursor != NULL)
- ide_cursor_remove_cursors (priv->cursor);
-
- return ret;
-}
-
-static void
-ide_source_view_real_begin_macro (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSourceViewModeType mode_type;
- GdkEvent *event;
- const gchar *mode_name;
- gunichar modifier;
- gint count;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->in_replay_macro)
- IDE_GOTO (in_replay);
-
- priv->recording_macro = TRUE;
-
- mode_type = ide_source_view_mode_get_mode_type (priv->mode);
- mode_name = ide_source_view_mode_get_name (priv->mode);
- modifier = priv->modifier;
- count = priv->count;
- event = gtk_get_current_event ();
-
- g_clear_object (&priv->capture);
-
- priv->capture = ide_source_view_capture_new (self, mode_name, mode_type, count, modifier);
- ide_source_view_capture_record_event (priv->capture, event, count, modifier);
- gdk_event_free (event);
-
-in_replay:
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_end_macro (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->in_replay_macro)
- IDE_GOTO (in_replay);
-
- priv->recording_macro = FALSE;
-
-in_replay:
- IDE_EXIT;
-}
-
-static void
-ide_source_view_goto_definition_symbol_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- g_autoptr(IdeSourceView) self = user_data;
- g_autoptr(IdeSymbol) symbol = NULL;
- IdeBuffer *buffer = (IdeBuffer *)object;
- g_autoptr(GError) error = NULL;
- IdeLocation *srcloc;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_BUFFER (buffer));
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- symbol = ide_buffer_get_symbol_at_location_finish (buffer, result, &error);
-
- if (symbol == NULL)
- {
- g_warning ("%s", error->message);
- IDE_EXIT;
- }
-
- srcloc = ide_symbol_get_location (symbol);
-
- if (srcloc == NULL)
- srcloc = ide_symbol_get_header_location (symbol);
-
- if (srcloc != NULL)
- {
- guint line = ide_location_get_line (srcloc);
- guint line_offset = ide_location_get_line_offset (srcloc);
- GFile *file = ide_location_get_file (srcloc);
- GFile *our_file = ide_buffer_get_file (buffer);
-
-#ifdef IDE_ENABLE_TRACE
- const gchar *filename = g_file_peek_path (file);
-
- IDE_TRACE_MSG ("%s => %s +%u:%u",
- ide_symbol_get_name (symbol),
- filename, line+1, line_offset+1);
-#endif
-
- /* Stash our current position for jump-back */
- ide_source_view_jump (self, NULL, NULL);
-
- /*
- * If we are navigating within this file, just stay captive instead of
- * potentially allowing jumping to the file in another editor.
- */
- if (g_file_equal (file, our_file))
- {
- GtkTextIter iter;
-
- gtk_text_buffer_get_iter_at_line_offset (GTK_TEXT_BUFFER (buffer),
- &iter, line, line_offset);
- gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
- ide_source_view_scroll_to_insert (self);
- IDE_EXIT;
- }
-
- g_signal_emit (self, signals [FOCUS_LOCATION], 0, srcloc);
- }
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_goto_definition (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->buffer != NULL)
- {
- GtkTextMark *insert;
- GtkTextIter iter;
-
- insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->buffer));
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &iter, insert);
-
- ide_buffer_get_symbol_at_location_async (priv->buffer,
- &iter,
- NULL,
- ide_source_view_goto_definition_symbol_cb,
- g_object_ref (self));
- }
-}
-
-static void
-ide_source_view_real_hide_completion (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_completion_hide (priv->completion);
-}
-
-static void
-ide_source_view_real_replay_macro (IdeSourceView *self,
- gboolean use_count)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- IdeSourceViewCapture *capture;
- gint count = 1;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->recording_macro)
- {
- g_warning ("Cannot playback macro while recording.");
- IDE_EXIT;
- }
-
- if (priv->in_replay_macro)
- {
- g_warning ("Cannot playback macro while playing back macro.");
- IDE_EXIT;
- }
-
- if (priv->capture == NULL)
- return;
-
- if (use_count)
- count = MAX (1, priv->count);
-
- IDE_TRACE_MSG ("Replaying capture %d times.", count);
-
- priv->in_replay_macro = TRUE;
- capture = priv->capture, priv->capture = NULL;
- for (gint i = 0; i < count; i++)
- ide_source_view_capture_replay (capture);
- g_clear_object (&priv->capture);
- priv->capture = capture, capture = NULL;
- priv->in_replay_macro = FALSE;
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_begin_user_action (IdeSourceView *self)
-{
- GtkTextBuffer *buffer;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_begin_user_action (buffer);
-}
-
-static void
-ide_source_view_end_user_action (IdeSourceView *self)
-{
- GtkTextBuffer *buffer;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_end_user_action (buffer);
-}
-
-gboolean
-ide_source_view_get_overwrite (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- if (gtk_text_view_get_overwrite (GTK_TEXT_VIEW (self)))
- {
- if (!priv->mode || !ide_source_view_mode_get_block_cursor (priv->mode))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gchar *
-ide_source_view_get_fixit_label (IdeSourceView *self,
- IdeTextEdit *fixit)
-{
- IdeLocation *begin_loc;
- IdeLocation *end_loc;
- IdeRange *range;
- GtkTextBuffer *buffer;
- GtkTextIter begin;
- GtkTextIter end;
- gchar *old_text = NULL;
- gchar *new_text = NULL;
- gchar *tmp;
- gchar *ret = NULL;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (fixit != NULL);
-
- range = ide_text_edit_get_range (fixit);
- if (range == NULL)
- goto cleanup;
-
- new_text = g_strdup (ide_text_edit_get_text (fixit));
- if (new_text == NULL)
- goto cleanup;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- if (!IDE_IS_BUFFER (buffer))
- goto cleanup;
-
- begin_loc = ide_range_get_begin (range);
- end_loc = ide_range_get_end (range);
-
- ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &begin, begin_loc);
- ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &end, end_loc);
-
- old_text = gtk_text_iter_get_slice (&begin, &end);
-
- if (strlen (old_text) > FIXIT_LABEL_LEN_MAX)
- {
- tmp = old_text;
- old_text = g_strndup (tmp, FIXIT_LABEL_LEN_MAX);
- g_free (tmp);
- }
-
- if (strlen (new_text) > FIXIT_LABEL_LEN_MAX)
- {
- tmp = new_text;
- new_text = g_strndup (tmp, FIXIT_LABEL_LEN_MAX);
- g_free (tmp);
- }
-
- tmp = old_text;
- old_text = g_markup_escape_text (old_text, -1);
- g_free (tmp);
-
- tmp = new_text;
- new_text = g_markup_escape_text (new_text, -1);
- g_free (tmp);
-
- if (old_text [0] == '\0')
- ret = g_strdup_printf (_("Insert “%s”"), new_text);
- else
- ret = g_strdup_printf (_("Replace “%s” with “%s”"), old_text, new_text);
-
-cleanup:
- g_free (old_text);
- g_free (new_text);
-
- return ret;
-}
-
-static void
-ide_source_view__fixit_activate (IdeSourceView *self,
- GtkMenuItem *menu_item)
-{
- IdeTextEdit *fixit;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_MENU_ITEM (menu_item));
-
- fixit = g_object_get_data (G_OBJECT (menu_item), "IDE_FIXIT");
-
- if (fixit != NULL)
- {
- IdeLocation *srcloc;
- IdeRange *range;
- GtkTextBuffer *buffer;
- const gchar *text;
- GtkTextIter begin;
- GtkTextIter end;
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- if (!IDE_IS_BUFFER (buffer))
- return;
-
- text = ide_text_edit_get_text (fixit);
- range = ide_text_edit_get_range (fixit);
-
- srcloc = ide_range_get_begin (range);
- ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &begin, srcloc);
-
- srcloc = ide_range_get_end (range);
- ide_buffer_get_iter_at_location (IDE_BUFFER (buffer), &end, srcloc);
-
- gtk_text_buffer_begin_user_action (buffer);
- gtk_text_buffer_delete (buffer, &begin, &end);
- gtk_text_buffer_insert (buffer, &begin, text, -1);
- gtk_text_buffer_end_user_action (buffer);
- }
-}
-
-static void
-ide_source_view_real_populate_popup (GtkTextView *text_view,
- GtkWidget *popup)
-{
- IdeSourceView *self = (IdeSourceView *)text_view;
- g_autoptr(GPtrArray) line_diags = NULL;
- GtkSeparatorMenuItem *sep;
- IdeDiagnostics *diagnostics;
- GtkTextBuffer *buffer;
- GtkMenuItem *menu_item;
- GtkTextMark *insert;
- GtkTextIter iter;
- GtkTextIter begin;
- GtkTextIter end;
- GMenu *model;
-
- g_assert (GTK_IS_TEXT_VIEW (text_view));
- g_assert (GTK_IS_WIDGET (popup));
-
- GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->populate_popup (text_view, popup);
-
- if (!GTK_IS_MENU (popup))
- return;
-
- buffer = gtk_text_view_get_buffer (text_view);
- if (!IDE_IS_BUFFER (buffer))
- return;
-
- model = dzl_application_get_menu_by_id (DZL_APPLICATION_DEFAULT, "ide-source-view-popup-menu");
- gtk_menu_shell_bind_model (GTK_MENU_SHELL (popup), G_MENU_MODEL (model), NULL, TRUE);
-
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
-
- /*
- * TODO: I'm pretty sure we don't want to use the insert mark, but the
- * location of the button-press-event (if there was one).
- */
- insert = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
-
- /*
- * Check if we have a diagnostic at this position and if there are fixits associated with it.
- * If so, display the "Apply TextEdit" menu item with available fixits.
- */
- if ((diagnostics = ide_buffer_get_diagnostics (IDE_BUFFER (buffer))))
- {
- g_assert (IDE_IS_DIAGNOSTICS (diagnostics));
-
- line_diags = ide_diagnostics_get_diagnostics_at_line (diagnostics,
- ide_buffer_get_file (IDE_BUFFER (buffer)),
- gtk_text_iter_get_line (&iter));
- IDE_PTR_ARRAY_SET_FREE_FUNC (line_diags, g_object_unref);
- }
-
- if (line_diags != NULL)
- {
- for (guint j = 0; j < line_diags->len; j++)
- {
- IdeDiagnostic *diag = g_ptr_array_index (line_diags, j);
- guint num_fixits;
-
- num_fixits = ide_diagnostic_get_n_fixits (diag);
-
- if (num_fixits > 0)
- {
- GtkWidget *parent;
- GtkWidget *submenu;
- guint i;
-
- sep = g_object_new (GTK_TYPE_SEPARATOR_MENU_ITEM,
- "visible", TRUE,
- NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (popup), GTK_WIDGET (sep));
-
- submenu = gtk_menu_new ();
-
- parent = g_object_new (GTK_TYPE_MENU_ITEM,
- "label", _("Apply Fix-It"),
- "submenu", submenu,
- "visible", TRUE,
- NULL);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (popup), parent);
-
- for (i = 0; i < num_fixits; i++)
- {
- IdeTextEdit *fixit;
- gchar *label;
-
- fixit = ide_diagnostic_get_fixit (diag, i);
- label = ide_source_view_get_fixit_label (self, fixit);
-
- menu_item = g_object_new (GTK_TYPE_MENU_ITEM,
- "label", label,
- "visible", TRUE,
- NULL);
- gtk_menu_shell_append (GTK_MENU_SHELL (submenu), GTK_WIDGET (menu_item));
-
- g_object_set_data_full (G_OBJECT (menu_item),
- "IDE_FIXIT",
- g_object_ref (fixit),
- (GDestroyNotify)g_object_unref);
-
- g_signal_connect_object (menu_item,
- "activate",
- G_CALLBACK (ide_source_view__fixit_activate),
- self,
- G_CONNECT_SWAPPED);
- }
- }
- }
- }
-}
-
-static void
-ide_source_view_real_rebuild_highlight (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->buffer != NULL)
- ide_buffer_rehighlight (priv->buffer);
-
- IDE_EXIT;
-}
-
-static gboolean
-ignore_invalid_buffers (GBinding *binding,
- const GValue *from_value,
- GValue *to_value,
- gpointer user_data)
-{
- if (G_VALUE_HOLDS (from_value, GTK_TYPE_TEXT_BUFFER))
- {
- GtkTextBuffer *buffer;
-
- buffer = g_value_get_object (from_value);
-
- if (IDE_IS_BUFFER (buffer))
- {
- g_value_set_object (to_value, buffer);
- return TRUE;
- }
- }
-
- g_value_set_object (to_value, NULL);
-
- return TRUE;
-}
-
-static void
-ide_source_view_set_indent_style (IdeSourceView *self,
- IdeIndentStyle indent_style)
-{
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert ((indent_style == IDE_INDENT_STYLE_SPACES) ||
- (indent_style == IDE_INDENT_STYLE_TABS));
-
- if (indent_style == IDE_INDENT_STYLE_SPACES)
- gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (self), TRUE);
- else
- gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (self), FALSE);
-}
-
-static gboolean
-ide_source_view_do_size_allocate_hack_cb (gpointer data)
-{
- IdeSourceView *self = data;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkAllocation alloc = priv->delay_size_allocation;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- priv->delay_size_allocate_chainup = 0;
-
- GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (GTK_WIDGET (self), &alloc);
-
- ide_source_view_set_overscroll_num_lines (self, priv->overscroll_num_lines);
-
- return G_SOURCE_REMOVE;
-}
-
-/*
- * HACK:
- *
- * We really want the panels in Builder to be as smooth as possible when
- * animating in and out of the scene. However, since these are not floating
- * panels, we have the challenge of trying to go through the entire relayout,
- * pixelcache, draw cycle many times per-second. Most systems are simply not
- * up to the task.
- *
- * We can, however, take a shortcut when shrinking the allocation. We can
- * simply defer the allocation request that would normally be chained up
- * to GtkTextView and finish that work after the animation has completed.
- * We use a simple heuristic to determine this, simply "missing" a size
- * allocation from the typical frame clock cycle.
- */
-static gboolean
-ide_source_view_do_size_allocate_hack (IdeSourceView *self,
- GtkAllocation *allocation)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkWidget *widget = (GtkWidget *)self;
- GtkAllocation old;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (allocation != NULL);
-
- /*
- * If we are shrinking the allocation, we can go forward with the hack.
- * If not, we will abort our request and do the normal chainup cycle.
- */
- gtk_widget_get_allocation (widget, &old);
- if ((old.width < allocation->width) || (old.height < allocation->height))
- return FALSE;
-
- /*
- * Save the allocation for later. We'll need it to apply after our timeout
- * which will occur just after the last frame (or sooner if we stall the
- * drawing pipeline).
- */
- priv->delay_size_allocation = *allocation;
-
- /*
- * Register our timeout to occur just after a normal frame interval.
- * If we are animating at 60 FPS, we should get another size-allocate within
- * the frame cycle, typically 17 msec.
- */
- g_clear_handle_id (&priv->delay_size_allocate_chainup, g_source_remove);
- priv->delay_size_allocate_chainup = g_timeout_add (30,
- ide_source_view_do_size_allocate_hack_cb,
- self);
-
- return TRUE;
-}
-
-static void
-ide_source_view_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (GTK_IS_WIDGET (widget));
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (allocation != NULL);
-
- if (!ide_source_view_do_size_allocate_hack (self, allocation))
- {
- GTK_WIDGET_CLASS (ide_source_view_parent_class)->size_allocate (GTK_WIDGET (self), allocation);
- ide_source_view_set_overscroll_num_lines (self, priv->overscroll_num_lines);
- }
-}
-
-static gboolean
-ide_source_view_scroll_event (GtkWidget *widget,
- GdkEventScroll *event)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- gboolean ret = GDK_EVENT_PROPAGATE;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /*
- * If the user started a manual scroll while we were attempting to scroll to
- * the target position, just abort our delayed scroll.
- */
- priv->scrolling_to_scroll_mark = FALSE;
-
- /*
- * Be forward-portable against changes underneath us.
- */
- if (GTK_WIDGET_CLASS (ide_source_view_parent_class)->scroll_event)
- ret = GTK_WIDGET_CLASS (ide_source_view_parent_class)->scroll_event (widget, event);
-
- return ret;
-}
-
-static void
-ide_source_view_real_reset_font_size (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->font_scale != FONT_SCALE_NORMAL)
- {
- priv->font_scale = FONT_SCALE_NORMAL;
- ide_source_view_rebuild_css (self);
- }
-}
-
-static void
-ide_source_view_real_increase_font_size (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->font_scale < LAST_FONT_SCALE - 1)
- {
- priv->font_scale++;
- ide_source_view_rebuild_css (self);
- }
-}
-
-static void
-ide_source_view_real_decrease_font_size (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->font_scale > 0)
- {
- priv->font_scale--;
- ide_source_view_rebuild_css (self);
- }
-}
-
-static void
-ide_source_view_real_delete_from_cursor (GtkTextView *text_view,
- GtkDeleteType delete_type,
- gint count)
-{
- if (delete_type == GTK_DELETE_PARAGRAPHS)
- ide_text_util_delete_line (text_view, count);
- else
- GTK_TEXT_VIEW_CLASS (ide_source_view_parent_class)->delete_from_cursor (text_view,
- delete_type,
- count);
-}
-
-static void
-ide_source_view_real_select_all (IdeSourceView *self,
- gboolean select_)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- g_signal_chain_from_overridden_handler (self, select_);
-
- priv->insert_mark_cleared = TRUE;
-}
-
-static void
-ide_source_view_rename_changed (IdeSourceView *self,
- DzlSimplePopover *popover)
-{
- const gchar *text;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (DZL_IS_SIMPLE_POPOVER (popover));
-
- text = dzl_simple_popover_get_text (popover);
- dzl_simple_popover_set_ready (popover, text != NULL);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_rename_edits_applied (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- g_autoptr(IdeSourceView) self = user_data;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_BUFFER_MANAGER (object));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /*
- * The completion window can sometimes popup when performing the replacements
- * so we manually hide that window here.
- */
- ide_source_view_real_hide_completion (self);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_rename_edits_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdeRenameProvider *provider = (IdeRenameProvider *)object;
- g_autoptr(IdeSourceView) self = user_data;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autoptr(GPtrArray) edits = NULL;
- g_autoptr(GError) error = NULL;
- g_autoptr(IdeContext) context = NULL;
- IdeBufferManager *buffer_manager;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_RENAME_PROVIDER (provider));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (!ide_rename_provider_rename_finish (provider, result, &edits, &error))
- {
- /* TODO: Propagate error to UI */
- g_warning ("%s", error->message);
- IDE_EXIT;
- }
-
- g_assert (edits != NULL);
-
- IDE_PTR_ARRAY_SET_FREE_FUNC (edits, g_object_unref);
-
- context = ide_buffer_ref_context (priv->buffer);
- buffer_manager = ide_buffer_manager_from_context (context);
-
- ide_buffer_manager_apply_edits_async (buffer_manager,
- IDE_PTR_ARRAY_STEAL_FULL (&edits),
- NULL,
- ide_source_view_rename_edits_applied,
- g_steal_pointer (&self));
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_rename_activate (IdeSourceView *self,
- const gchar *text,
- DzlSimplePopover *popover)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autoptr(IdeLocation) location = NULL;
- IdeRenameProvider *provider;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (text != NULL);
- g_assert (DZL_IS_SIMPLE_POPOVER (popover));
-
- if (NULL == (provider = ide_buffer_get_rename_provider (priv->buffer)))
- IDE_EXIT;
-
- location = ide_buffer_get_insert_location (priv->buffer);
-
- ide_rename_provider_rename_async (provider,
- location,
- text,
- NULL,
- ide_source_view_rename_edits_cb,
- g_object_ref (self));
-
- /*
- * TODO: We should probably lock all buffers so that we can ensure
- * that our edit points are correct by time we get called back.
- */
-
- gtk_popover_popdown (GTK_POPOVER (popover));
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_begin_rename (IdeSourceView *self)
-{
- IdeRenameProvider *provider;
- DzlSimplePopover *popover;
- g_autofree gchar *title = NULL;
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- GtkTextIter iter;
- GtkTextIter word_start;
- GtkTextIter word_end;
- g_autofree gchar *symbol = NULL;
- GdkRectangle loc;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- provider = ide_buffer_get_rename_provider (IDE_BUFFER (buffer));
-
- if (provider == NULL)
- {
- g_message ("Cannot rename, operation requires an IdeRenameProvider");
- return;
- }
-
- insert = gtk_text_buffer_get_insert (buffer);
- title = ide_buffer_dup_title (IDE_BUFFER (buffer));
-
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
-
- g_debug ("Renaming symbol from %s +%d:%d",
- title,
- gtk_text_iter_get_line (&iter) + 1,
- gtk_text_iter_get_line_offset (&iter) + 1);
-
- gtk_text_buffer_select_range (buffer, &iter, &iter);
- gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &loc);
- gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (self),
- GTK_TEXT_WINDOW_WIDGET,
- loc.x, loc.y, &loc.x, &loc.y);
-
- // get symbol to rename (for the popup)
- if (gtk_text_iter_inside_word (&iter) && !gtk_text_iter_starts_word (&iter))
- {
- word_start = iter;
- word_end = iter;
- gtk_text_iter_backward_word_start (&word_start);
- gtk_text_iter_forward_word_end (&word_end);
- }
- else if (gtk_text_iter_starts_word (&iter))
- {
- word_start = iter;
- word_end = iter;
- gtk_text_iter_forward_word_end (&word_end);
- }
- else if (gtk_text_iter_ends_word (&iter))
- {
- word_start = iter;
- word_end = iter;
- gtk_text_iter_backward_word_start (&word_start);
- }
-
- symbol = gtk_text_iter_get_text (&word_start, &word_end);
-
- popover = g_object_new (DZL_TYPE_SIMPLE_POPOVER,
- "title", _("Rename symbol"),
- "button-text", _("Rename"),
- "text", symbol,
- "relative-to", self,
- "pointing-to", &loc,
- NULL);
-
- g_signal_connect_object (popover,
- "changed",
- G_CALLBACK (ide_source_view_rename_changed),
- self,
- G_CONNECT_SWAPPED);
-
- g_signal_connect_object (popover,
- "activate",
- G_CALLBACK (ide_source_view_rename_activate),
- self,
- G_CONNECT_SWAPPED);
-
- gtk_popover_popup (GTK_POPOVER (popover));
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_format_selection_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdeBuffer *buffer = (IdeBuffer *)object;
- g_autoptr(IdeSourceView) self = user_data;
- g_autoptr(GError) error = NULL;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (G_IS_ASYNC_RESULT (result));
-
- if (!ide_buffer_format_selection_finish (buffer, result, &error))
- {
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
- g_warning ("%s", error->message);
- }
-
- gtk_text_view_set_editable (GTK_TEXT_VIEW (self), TRUE);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_format_selection (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autoptr(IdeFormatterOptions) options = NULL;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- options = ide_formatter_options_new ();
- ide_formatter_options_set_tab_width (options,
- gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (self)));
- ide_formatter_options_set_insert_spaces (options,
- gtk_source_view_get_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (self)));
-
- gtk_text_view_set_editable (GTK_TEXT_VIEW (self), FALSE);
- ide_buffer_format_selection_async (priv->buffer,
- options,
- NULL,
- ide_source_view_format_selection_cb,
- g_object_ref (self));
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_code_action_execute_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IDE_TRACE_MSG ("ide_source_view_code_action_execute_cb done");
-}
-
-static void
-execute_code_action_cb (IdeCodeAction *code_action)
-{
- ide_code_action_execute_async(code_action,
- NULL,
- ide_source_view_code_action_execute_cb,
- g_object_ref (code_action));
-}
-
-static void
-popup_menu_detach (GtkWidget *attach_widget,
- GtkMenu *menu)
-{
-}
-
-static void
-ide_source_view_code_action_query_cb(GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdeBuffer *buffer = (IdeBuffer *)object;
-
- g_autoptr(IdeSourceView) self = user_data;
- g_autoptr(GError) error = NULL;
- g_autoptr(GPtrArray) code_actions = NULL;
-
- IDE_ENTRY;
-
- g_assert(IDE_IS_SOURCE_VIEW(self));
- g_assert(G_IS_ASYNC_RESULT(result));
-
- code_actions = ide_buffer_code_action_query_finish(buffer, result, &error);
-
- if (!code_actions)
- {
- if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
- g_warning("%s", error->message);
- IDE_EXIT;
- }
-
- if (code_actions->len)
- {
- IdeContext* context;
- GtkTextView* text_view;
- GtkTextIter iter;
- GdkRectangle iter_location;
- GdkRectangle visible_rect;
- gboolean is_visible;
- GtkWidget* popup_menu;
-
- context = ide_buffer_ref_context(buffer);
-
- popup_menu = gtk_menu_new();
-
- gtk_style_context_add_class(gtk_widget_get_style_context(popup_menu),
- GTK_STYLE_CLASS_CONTEXT_MENU);
-
-
- gtk_menu_attach_to_widget(GTK_MENU(popup_menu),
- GTK_WIDGET(self),
- popup_menu_detach);
-
- for (gsize i = 0; i < code_actions->len; i++)
- {
- IdeCodeAction* code_action;
- GtkWidget* menu_item;
-
- code_action = g_ptr_array_index(code_actions, i);
- ide_object_append(IDE_OBJECT(context), IDE_OBJECT(code_action));
- menu_item = gtk_menu_item_new_with_label(ide_code_action_get_title(code_action));
-
- g_signal_connect_swapped(menu_item,
- "activate",
- G_CALLBACK(execute_code_action_cb),
- code_action);
-
-
- gtk_widget_show(menu_item);
- gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), menu_item);
- }
-
- gtk_menu_shell_select_first(GTK_MENU_SHELL(popup_menu), FALSE);
-
- text_view = GTK_TEXT_VIEW(self);
-
- gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(text_view),
- &iter,
- gtk_text_buffer_get_insert(gtk_text_view_get_buffer(text_view)));
-
- gtk_text_view_get_iter_location(text_view, &iter, &iter_location);
- gtk_text_view_get_visible_rect(text_view, &visible_rect);
-
- is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
- iter_location.x < visible_rect.x + visible_rect.width &&
- iter_location.y + iter_location.height > visible_rect.y &&
- iter_location.y < visible_rect.y + visible_rect.height);
-
- if (is_visible)
- {
- gtk_text_view_buffer_to_window_coords(text_view,
- GTK_TEXT_WINDOW_WIDGET,
- iter_location.x,
- iter_location.y,
- &iter_location.x,
- &iter_location.y);
-
- gtk_menu_popup_at_rect(GTK_MENU(popup_menu),
- gtk_widget_get_window(GTK_WIDGET(text_view)),
- &iter_location,
- GDK_GRAVITY_SOUTH_EAST,
- GDK_GRAVITY_NORTH_WEST,
- NULL);
- }
- }
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_code_action_query (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- ide_buffer_code_action_query_async (priv->buffer,
- NULL,
- ide_source_view_code_action_query_cb,
- g_object_ref (self));
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_find_references_jump (IdeSourceView *self,
- GtkListBoxRow *row,
- GtkListBox *list_box)
-{
- IdeLocation *location;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_LIST_BOX_ROW (row));
- g_assert (GTK_IS_LIST_BOX (list_box));
-
- location = g_object_get_data (G_OBJECT (row), "IDE_LOCATION");
-
- if (location != NULL)
- g_signal_emit (self, signals [FOCUS_LOCATION], 0, location);
-
- IDE_EXIT;
-}
-
-static gboolean
-insert_mark_within_range (IdeBuffer *buffer,
- IdeRange *range)
-{
- GtkTextMark *insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
- IdeLocation *begin = ide_range_get_begin (range);
- IdeLocation *end = ide_range_get_end (range);
- GtkTextIter iter;
- GtkTextIter begin_iter;
- GtkTextIter end_iter;
-
- if (!begin || !end)
- return FALSE;
-
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, insert);
- ide_buffer_get_iter_at_location (buffer, &begin_iter, begin);
- ide_buffer_get_iter_at_location (buffer, &end_iter, end);
-
- return gtk_text_iter_compare (&begin_iter, &iter) <= 0 &&
- gtk_text_iter_compare (&end_iter, &iter) >= 0;
-
-}
-
-static void
-ide_source_view_find_references_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdeSymbolResolver *symbol_resolver = (IdeSymbolResolver *)object;
- g_autoptr(GPtrArray) references = NULL;
- g_autoptr(GError) error = NULL;
- g_autoptr(IdeTask) task = user_data;
- FindReferencesTaskData *data;
- IdeSourceView *self;
- IdeSourceViewPrivate *priv;
- GtkScrolledWindow *scroller;
- GtkPopover *popover;
- GtkListBox *list_box;
- GtkTextMark *insert;
- GtkTextIter iter;
- GdkRectangle loc;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SYMBOL_RESOLVER (symbol_resolver));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_TASK (task));
-
- references = ide_symbol_resolver_find_references_finish (symbol_resolver, result, &error);
-
- IDE_PTR_ARRAY_SET_FREE_FUNC (references, g_object_unref);
-
- self = ide_task_get_source_object (task);
- priv = ide_source_view_get_instance_private (self);
- data = ide_task_get_task_data (task);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (data != NULL);
- g_assert (data->resolvers != NULL);
- g_assert (data->resolvers->len > 0);
-
- g_ptr_array_remove_index (data->resolvers, data->resolvers->len - 1);
-
- /* If references are not found and symbol resolvers are left try those */
- if (references == NULL && data->resolvers->len > 0)
- {
- GCancellable *cancellable;
- IdeSymbolResolver *resolver;
-
- cancellable = ide_task_get_cancellable (task);
- resolver = g_ptr_array_index (data->resolvers, data->resolvers->len - 1);
-
- ide_symbol_resolver_find_references_async (resolver,
- data->location,
- ide_buffer_get_language_id (priv->buffer),
- cancellable,
- ide_source_view_find_references_cb,
- g_steal_pointer (&task));
- return;
- }
-
- /* Ignore popover if we are no longer visible or not top-most */
- if (!gtk_widget_get_visible (GTK_WIDGET (self)) ||
- !gtk_widget_get_child_visible (GTK_WIDGET (self)))
- IDE_EXIT;
-
- insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (priv->buffer));
- gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (priv->buffer), &iter, insert);
- gtk_text_buffer_select_range (GTK_TEXT_BUFFER (priv->buffer), &iter, &iter);
- gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &loc);
- gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (self),
- GTK_TEXT_WINDOW_WIDGET,
- loc.x, loc.y, &loc.x, &loc.y);
-
- popover = g_object_new (GTK_TYPE_POPOVER,
- "modal", TRUE,
- "position", GTK_POS_TOP,
- "relative-to", self,
- "pointing-to", &loc,
- NULL);
-
- scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
- "min-content-height", 35,
- "max-content-height", 200,
- "propagate-natural-height", TRUE,
- "propagate-natural-width", TRUE,
- "visible", TRUE,
- NULL);
- gtk_container_add (GTK_CONTAINER (popover), GTK_WIDGET (scroller));
-
- list_box = g_object_new (GTK_TYPE_LIST_BOX,
- "visible", TRUE,
- NULL);
- gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (list_box));
-
- if (references != NULL && references->len > 0)
- {
- g_autoptr(IdeContext) context = ide_buffer_ref_context (priv->buffer);
- g_autoptr(GFile) workdir = ide_context_ref_workdir (context);
-
- for (guint i = 0; i < references->len; i++)
- {
- IdeRange *range = g_ptr_array_index (references, i);
- IdeLocation *begin = ide_range_get_begin (range);
- GFile *file = ide_location_get_file (begin);
- guint line = ide_location_get_line (begin);
- guint line_offset = ide_location_get_line_offset (begin);
- g_autofree gchar *name = NULL;
- g_autofree gchar *text = NULL;
- GtkListBoxRow *row;
- GtkLabel *label;
-
- if (g_file_has_prefix (file, workdir))
- name = g_file_get_relative_path (workdir, file);
- else if (g_file_is_native (file))
- name = g_file_get_path (file);
- else
- name = g_file_get_uri (file);
-
- /* translators: %s is the filename, then line number, column number. <> are pango markup */
- text = g_strdup_printf (_("<b>%s</b> — <small>Line %u, Column %u</small>"),
- name, line + 1, line_offset + 1);
-
- label = g_object_new (GTK_TYPE_LABEL,
- "xalign", 0.0f,
- "label", text,
- "use-markup", TRUE,
- "visible", TRUE,
- NULL);
- row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
- "child", label,
- "visible", TRUE,
- NULL);
- g_object_set_data_full (G_OBJECT (row),
- "IDE_LOCATION",
- g_object_ref (begin),
- g_object_unref);
- gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (row));
-
- if (insert_mark_within_range (priv->buffer, range))
- gtk_list_box_select_row (list_box, row);
- }
- }
- else
- {
- GtkLabel *label = g_object_new (GTK_TYPE_LABEL,
- "label", _("No references were found"),
- "visible", TRUE,
- NULL);
- gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (label));
- }
-
- g_signal_connect_object (list_box,
- "row-activated",
- G_CALLBACK (ide_source_view_real_find_references_jump),
- self,
- G_CONNECT_SWAPPED);
-
- gtk_popover_popup (popover);
-
- g_signal_connect (popover, "hide", G_CALLBACK (gtk_widget_destroy), NULL);
-
- ide_task_return_boolean (task, TRUE);
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_find_references (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- g_autoptr(IdeTask) task = NULL;
- g_autoptr(GPtrArray) resolvers = NULL;
- FindReferencesTaskData *data;
- IdeSymbolResolver *resolver;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- task = ide_task_new (self, NULL, NULL, NULL);
- ide_task_set_source_tag (task, ide_source_view_real_find_references);
-
- resolvers = ide_buffer_get_symbol_resolvers (priv->buffer);
- IDE_PTR_ARRAY_SET_FREE_FUNC (resolvers, g_object_unref);
-
- if (resolvers->len == 0)
- {
- g_debug ("No symbol resolver is available");
- IDE_EXIT;
- }
-
- data = g_slice_new0 (FindReferencesTaskData);
- data->resolvers = g_steal_pointer (&resolvers);
- data->location = ide_buffer_get_insert_location (priv->buffer);
- ide_task_set_task_data (task, data, find_references_task_data_free);
-
- resolver = g_ptr_array_index (data->resolvers, data->resolvers->len - 1);
-
- /* Try each symbol resolver one by one to find references. */
- ide_symbol_resolver_find_references_async (resolver,
- data->location,
- ide_buffer_get_language_id (priv->buffer),
- NULL,
- ide_source_view_find_references_cb,
- g_steal_pointer (&task));
-
- IDE_EXIT;
-}
-
-static void
-ide_source_view_real_request_documentation (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextIter iter;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- if (priv->buffer == NULL)
- return;
-
- ide_buffer_get_selection_bounds (priv->buffer, &iter, NULL);
-
- _ide_hover_display (priv->hover, &iter);
-}
-
-static void
-ide_source_view_real_undo (GtkSourceView *view)
-{
- IdeSourceView *self = (IdeSourceView *)view;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /*
- * Disable things that could confuse undo. For example, we need to bail on
- * any in-flight snippets because they just can't deal with the buffer
- * changes correctly given the GtkTextMark vs run-length design.
- */
- ide_source_view_clear_snippets (self);
-
- GTK_SOURCE_VIEW_CLASS (ide_source_view_parent_class)->undo (view);
-}
-
-static void
-ide_source_view_real_reset (IdeSourceView *self)
-{
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- g_signal_emit (self, signals [CLEAR_SEARCH], 0);
- g_signal_emit (self, signals [CLEAR_MODIFIER], 0);
- g_signal_emit (self, signals [CLEAR_SELECTION], 0);
- g_signal_emit (self, signals [CLEAR_COUNT], 0);
- g_signal_emit (self, signals [CLEAR_SNIPPETS], 0);
- g_signal_emit (self, signals [HIDE_COMPLETION], 0);
- g_signal_emit (self, signals [REMOVE_CURSORS], 0);
- g_signal_emit (self, signals [SET_MODE], 0, NULL, IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT);
-}
-
-static void
-ide_source_view_destroy (GtkWidget *widget)
-{
- IdeSourceView *self = (IdeSourceView *)widget;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /* Ensure we release the buffer immediately */
- if (priv->buffer_signals != NULL)
- ide_signal_group_set_target (priv->buffer_signals, NULL);
-
- GTK_WIDGET_CLASS (ide_source_view_parent_class)->destroy (widget);
-}
-
-static void
-ide_source_view_dispose (GObject *object)
-{
- IdeSourceView *self = (IdeSourceView *)object;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- if (priv->hadj_animation)
- {
- dzl_animation_stop (priv->hadj_animation);
- g_clear_weak_pointer (&priv->hadj_animation);
- }
-
- if (priv->vadj_animation)
- {
- dzl_animation_stop (priv->vadj_animation);
- g_clear_weak_pointer (&priv->vadj_animation);
- }
-
- ide_source_view_clear_snippets (self);
-
- g_clear_handle_id (&priv->delay_size_allocate_chainup, g_source_remove);
-
- g_clear_object (&priv->hover);
- g_clear_object (&priv->completion);
- g_clear_object (&priv->capture);
- ide_clear_and_destroy_object (&priv->indenter_adapter);
- g_clear_object (&priv->css_provider);
- g_clear_object (&priv->mode);
- g_clear_object (&priv->buffer_signals);
- g_clear_object (&priv->file_setting_bindings);
- g_clear_object (&priv->gutter);
-
- if (priv->command_str != NULL)
- {
- g_string_free (priv->command_str, TRUE);
- priv->command_str = NULL;
- }
-
- G_OBJECT_CLASS (ide_source_view_parent_class)->dispose (object);
-}
-
-static void
-ide_source_view_finalize (GObject *object)
-{
- IdeSourceView *self = (IdeSourceView *)object;
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_clear_pointer (&priv->display_name, g_free);
- g_clear_pointer (&priv->font_desc, pango_font_description_free);
- g_clear_pointer (&priv->selections, g_queue_free);
- g_clear_pointer (&priv->snippets, g_queue_free);
- g_clear_pointer (&priv->include_regex, g_regex_unref);
-
- G_OBJECT_CLASS (ide_source_view_parent_class)->finalize (object);
-}
-
-static void
-ide_source_view_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- IdeSourceView *self = IDE_SOURCE_VIEW (object);
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- switch (prop_id)
- {
- case PROP_AUTO_INDENT:
- g_value_set_boolean (value, priv->auto_indent);
- break;
-
- case PROP_COMPLETION_N_ROWS:
- g_value_set_uint (value, ide_completion_get_n_rows (priv->completion));
- break;
-
- case PROP_COUNT:
- g_value_set_int (value, ide_source_view_get_count (self));
- break;
-
- case PROP_FILE_SETTINGS:
- g_value_set_object (value, ide_source_view_get_file_settings (self));
- break;
-
- case PROP_FONT_DESC:
- g_value_set_boxed (value, ide_source_view_get_font_desc (self));
- break;
-
- case PROP_HIGHLIGHT_CURRENT_LINE:
- g_value_set_boolean (value, ide_source_view_get_highlight_current_line (self));
- break;
-
- case PROP_INDENTER:
- g_value_set_object (value, ide_source_view_get_indenter (self));
- break;
-
- case PROP_INSERT_MATCHING_BRACE:
- g_value_set_boolean (value, ide_source_view_get_insert_matching_brace (self));
- break;
-
- case PROP_INTERACTIVE_COMPLETION:
- g_value_set_boolean (value, priv->interactive_completion);
- break;
-
- case PROP_MODE_DISPLAY_NAME:
- g_value_set_string (value, ide_source_view_get_mode_display_name (self));
- break;
-
- case PROP_OVERWRITE:
- g_value_set_boolean (value, ide_source_view_get_overwrite (self));
- break;
-
- case PROP_OVERWRITE_BRACES:
- g_value_set_boolean (value, ide_source_view_get_overwrite_braces (self));
- break;
-
- case PROP_SCROLL_OFFSET:
- g_value_set_uint (value, ide_source_view_get_scroll_offset (self));
- break;
-
- case PROP_SHOW_GRID_LINES:
- g_value_set_boolean (value, ide_source_view_get_show_grid_lines (self));
- break;
-
- case PROP_SHOW_LINE_CHANGES:
- g_value_set_boolean (value, ide_source_view_get_show_line_changes (self));
- break;
-
- case PROP_SHOW_LINE_DIAGNOSTICS:
- g_value_set_boolean (value, ide_source_view_get_show_line_diagnostics (self));
- break;
-
- case PROP_SHOW_LINE_NUMBERS:
- g_value_set_boolean (value, ide_source_view_get_show_line_numbers (self));
- break;
-
- case PROP_SHOW_RELATIVE_LINE_NUMBERS:
- g_value_set_boolean (value, ide_source_view_get_show_relative_line_numbers (self));
- break;
-
- case PROP_OVERSCROLL:
- g_value_set_int (value, priv->overscroll_num_lines);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-ide_source_view_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- IdeSourceView *self = IDE_SOURCE_VIEW (object);
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- switch (prop_id)
- {
- case PROP_AUTO_INDENT:
- priv->auto_indent = !!g_value_get_boolean (value);
- ide_source_view_update_auto_indent_override (self);
- break;
-
- case PROP_COMPLETION_N_ROWS:
- if (priv->completion != NULL)
- ide_completion_set_n_rows (priv->completion, g_value_get_uint (value));
- break;
-
- case PROP_COUNT:
- ide_source_view_set_count (self, g_value_get_int (value));
- break;
-
- case PROP_FONT_NAME:
- ide_source_view_set_font_name (self, g_value_get_string (value));
- break;
-
- case PROP_FONT_DESC:
- ide_source_view_set_font_desc (self, g_value_get_boxed (value));
- break;
-
- case PROP_INDENT_STYLE:
- ide_source_view_set_indent_style (self, g_value_get_enum (value));
- break;
-
- case PROP_INSERT_MATCHING_BRACE:
- ide_source_view_set_insert_matching_brace (self, g_value_get_boolean (value));
- break;
-
- case PROP_INTERACTIVE_COMPLETION:
- ide_source_view_set_interactive_completion (self, g_value_get_boolean (value));
- break;
-
- case PROP_OVERWRITE:
- gtk_text_view_set_overwrite (GTK_TEXT_VIEW (self), g_value_get_boolean (value));
- break;
-
- case PROP_OVERWRITE_BRACES:
- ide_source_view_set_overwrite_braces (self, g_value_get_boolean (value));
- break;
-
- case PROP_SCROLL_OFFSET:
- ide_source_view_set_scroll_offset (self, g_value_get_uint (value));
- break;
-
- case PROP_SHOW_GRID_LINES:
- ide_source_view_set_show_grid_lines (self, g_value_get_boolean (value));
- break;
-
- case PROP_SHOW_LINE_CHANGES:
- ide_source_view_set_show_line_changes (self, g_value_get_boolean (value));
- break;
-
- case PROP_SHOW_LINE_DIAGNOSTICS:
- ide_source_view_set_show_line_diagnostics (self, g_value_get_boolean (value));
- break;
-
- case PROP_SHOW_LINE_NUMBERS:
- ide_source_view_set_show_line_numbers (self, g_value_get_boolean (value));
- break;
-
- case PROP_SHOW_RELATIVE_LINE_NUMBERS:
- ide_source_view_set_show_relative_line_numbers (self, g_value_get_boolean (value));
- break;
-
- case PROP_OVERSCROLL:
- ide_source_view_set_overscroll_num_lines (self, g_value_get_int (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-ide_source_view_class_init (IdeSourceViewClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass);
- GtkSourceViewClass *gsv_class = GTK_SOURCE_VIEW_CLASS (klass);
- GtkBindingSet *binding_set;
-
- object_class->constructed = ide_source_view_constructed;
- object_class->dispose = ide_source_view_dispose;
- object_class->finalize = ide_source_view_finalize;
- object_class->get_property = ide_source_view_get_property;
- object_class->set_property = ide_source_view_set_property;
-
- widget_class->button_press_event = ide_source_view_real_button_press_event;
- widget_class->button_release_event = ide_source_view_real_button_release_event;
- widget_class->motion_notify_event = ide_source_view_real_motion_notify_event;
- widget_class->focus_in_event = ide_source_view_focus_in_event;
- widget_class->focus_out_event = ide_source_view_focus_out_event;
- widget_class->key_press_event = ide_source_view_key_press_event;
- widget_class->key_release_event = ide_source_view_key_release_event;
- widget_class->scroll_event = ide_source_view_scroll_event;
- widget_class->size_allocate = ide_source_view_size_allocate;
- widget_class->style_updated = ide_source_view_real_style_updated;
- widget_class->destroy = ide_source_view_destroy;
-
- text_view_class->delete_from_cursor = ide_source_view_real_delete_from_cursor;
- text_view_class->draw_layer = ide_source_view_real_draw_layer;
- text_view_class->insert_at_cursor = ide_source_view_real_insert_at_cursor;
- text_view_class->populate_popup = ide_source_view_real_populate_popup;
-
- gsv_class->undo = ide_source_view_real_undo;
-
- klass->add_cursor = ide_source_view_real_add_cursor;
- klass->remove_cursors = ide_source_view_real_remove_cursors;
- klass->append_to_count = ide_source_view_real_append_to_count;
- klass->begin_macro = ide_source_view_real_begin_macro;
- klass->begin_rename = ide_source_view_real_begin_rename;
- klass->capture_modifier = ide_source_view_real_capture_modifier;
- klass->clear_count = ide_source_view_real_clear_count;
- klass->clear_modifier = ide_source_view_real_clear_modifier;
- klass->clear_selection = ide_source_view_real_clear_selection;
- klass->clear_snippets = ide_source_view_clear_snippets;
- klass->copy_clipboard_extended = ide_source_view_real_copy_clipboard_extended;
- klass->cycle_completion = ide_source_view_real_cycle_completion;
- klass->decrease_font_size = ide_source_view_real_decrease_font_size;
- klass->delete_selection = ide_source_view_real_delete_selection;
- klass->end_macro = ide_source_view_real_end_macro;
- klass->goto_definition = ide_source_view_real_goto_definition;
- klass->hide_completion = ide_source_view_real_hide_completion;
- klass->increase_font_size = ide_source_view_real_increase_font_size;
- klass->indent_selection = ide_source_view_real_indent_selection;
- klass->insert_modifier = ide_source_view_real_insert_modifier;
- klass->move_error = ide_source_view_real_move_error;
- klass->movement = ide_source_view_real_movement;
- klass->paste_clipboard_extended = ide_source_view_real_paste_clipboard_extended;
- klass->pop_selection = ide_source_view_real_pop_selection;
- klass->push_selection = ide_source_view_real_push_selection;
- klass->rebuild_highlight = ide_source_view_real_rebuild_highlight;
- klass->replay_macro = ide_source_view_real_replay_macro;
- klass->request_documentation = ide_source_view_real_request_documentation;
- klass->reset_font_size = ide_source_view_real_reset_font_size;
- klass->restore_insert_mark = ide_source_view_real_restore_insert_mark;
- klass->save_command = ide_source_view_real_save_command;
- klass->save_insert_mark = ide_source_view_real_save_insert_mark;
- klass->save_search_char = ide_source_view_real_save_search_char;
- klass->select_inner = ide_source_view_real_select_inner;
- klass->select_tag = ide_source_view_real_select_tag;
- klass->selection_theatric = ide_source_view_real_selection_theatric;
- klass->set_mode = ide_source_view_real_set_mode;
- klass->set_overwrite = ide_source_view_real_set_overwrite;
- klass->sort = ide_source_view_real_sort;
- klass->swap_selection_bounds = ide_source_view_real_swap_selection_bounds;
-
- g_object_class_override_property (object_class, PROP_AUTO_INDENT, "auto-indent");
-
- properties [PROP_COMPLETION_N_ROWS] =
- g_param_spec_uint ("completion-n-rows",
- "Completion N Rows",
- "The number of completion rows to display to the user",
- 1, 32, 5,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_COUNT] =
- g_param_spec_int ("count",
- "Count",
- "The count for movements.",
- -1,
- G_MAXINT,
- 0,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_FILE_SETTINGS] =
- g_param_spec_object ("file-settings",
- "File Settings",
- "The file settings that have been loaded for the file.",
- IDE_TYPE_FILE_SETTINGS,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_FONT_DESC] =
- g_param_spec_boxed ("font-desc",
- "Font Description",
- "The Pango font description to use for rendering source.",
- PANGO_TYPE_FONT_DESCRIPTION,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_FONT_NAME] =
- g_param_spec_string ("font-name",
- "Font Name",
- "The Pango font name to use for rendering source.",
- "Monospace",
- (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
-
- g_object_class_override_property (object_class,
- PROP_HIGHLIGHT_CURRENT_LINE,
- "highlight-current-line");
-
- properties [PROP_INDENTER] =
- g_param_spec_object ("indenter",
- "Indenter",
- "Indenter",
- IDE_TYPE_INDENTER,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_INDENT_STYLE] =
- g_param_spec_enum ("indent-style",
- "Indent Style",
- "Indent Style",
- IDE_TYPE_INDENT_STYLE,
- IDE_INDENT_STYLE_TABS,
- (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_INTERACTIVE_COMPLETION] =
- g_param_spec_boolean ("interactive-completion",
- "Interactive Completion",
- "If completion should be completed interactively",
- TRUE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_INSERT_MATCHING_BRACE] =
- g_param_spec_boolean ("insert-matching-brace",
- "Insert Matching Brace",
- "Insert a matching brace/bracket/quotation/parenthesis.",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_override_property (object_class, PROP_OVERWRITE, "overwrite");
-
- properties [PROP_MODE_DISPLAY_NAME] =
- g_param_spec_string ("mode-display-name",
- "Mode Display Name",
- "The display name of the keybinding mode.",
- NULL,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_OVERWRITE_BRACES] =
- g_param_spec_boolean ("overwrite-braces",
- "Overwrite Braces",
- "Overwrite a matching brace/bracket/quotation/parenthesis.",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_SCROLL_OFFSET] =
- g_param_spec_uint ("scroll-offset",
- "Scroll Offset",
- "The number of lines between the insertion cursor and screen boundary.",
- 0,
- G_MAXUINT,
- 0,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_SHOW_GRID_LINES] =
- g_param_spec_boolean ("show-grid-lines",
- "Show Grid Lines",
- "If the background grid should be shown.",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_SHOW_LINE_CHANGES] =
- g_param_spec_boolean ("show-line-changes",
- "Show Line Changes",
- "If line changes should be shown in the left gutter.",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * IdeSourceView:show-line-diagnostics:
- *
- * If the diagnostics gutter should be visible.
- *
- * This also requires that IdeBuffer:highlight-diagnostics is set to %TRUE
- * to generate diagnostics.
- *
- * Since: 3.32
- */
- properties [PROP_SHOW_LINE_DIAGNOSTICS] =
- g_param_spec_boolean ("show-line-diagnostics",
- "Show Line Diagnostics",
- "If line changes diagnostics should be shown in the left gutter.",
- TRUE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_override_property (object_class, PROP_SHOW_LINE_NUMBERS, "show-line-numbers");
-
- properties [PROP_SHOW_RELATIVE_LINE_NUMBERS] =
- g_param_spec_boolean ("show-relative-line-numbers",
- "Show Relative Line Numbers",
- "Show line numbers relative to the cursor line",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_OVERSCROLL] =
- g_param_spec_int ("overscroll",
- "Overscroll",
- "The number of lines to scroll beyond the end of the "
- "buffer. A negative number of lines will scroll until "
- "only that number of lines is visible",
- G_MININT,
- G_MAXINT,
- DEFAULT_OVERSCROLL_NUM_LINES,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_properties (object_class, LAST_PROP, properties);
-
- signals [ACTION] =
- g_signal_new_class_handler ("action",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (dzl_gtk_widget_action_with_string),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 3,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_STRING);
-
- signals [APPEND_TO_COUNT] =
- g_signal_new ("append-to-count",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, append_to_count),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- G_TYPE_INT);
-
- /**
- * IdeSourceView::begin-macro:
- *
- * This signal will begin recording input to the #IdeSourceView. This includes the current
- * #IdeSourceViewMode, #IdeSourceView:count and #IdeSourceView:modifier which will be used
- * to replay the sequence starting from the correct state.
- *
- * Pair this with an emission of #IdeSourceView::end-macro to complete the sequence.
- *
- * Since: 3.32
- */
- signals [BEGIN_MACRO] =
- g_signal_new ("begin-macro",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, begin_macro),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView::begin-rename:
- *
- * This signal is emitted when the source view should begin a rename
- * operation using the #IdeRenameProvider from the underlying buffer. The
- * cursor position will be used as the location when sending the request to
- * the provider.
- *
- * Since: 3.32
- */
- signals [BEGIN_RENAME] =
- g_signal_new ("begin-rename",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, begin_rename),
- NULL, NULL, NULL, G_TYPE_NONE, 0);
-
- signals [BEGIN_USER_ACTION] =
- g_signal_new_class_handler ("begin-user-action",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_begin_user_action),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [SAVE_COMMAND] =
- g_signal_new ("save-command",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, save_command),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-
- signals [SAVE_SEARCH_CHAR] =
- g_signal_new ("save-search-char",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, save_search_char),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView::capture-modifier:
- *
- * This signal will block the main loop in a similar fashion to how
- * gtk_dialog_run() performs until a key-press has occurred that can be
- * captured for use in movements.
- *
- * Pressing Escape or unfocusing the widget will break from this loop.
- *
- * Use of this signal is not recommended except in very specific cases.
- *
- * Since: 3.32
- */
- signals [CAPTURE_MODIFIER] =
- g_signal_new ("capture-modifier",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, capture_modifier),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- g_signal_override_class_handler ("change-case",
- G_TYPE_FROM_CLASS (klass),
- G_CALLBACK (ide_source_view_real_change_case));
-
- signals [CLEAR_COUNT] =
- g_signal_new ("clear-count",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, clear_count),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [CLEAR_MODIFIER] =
- g_signal_new ("clear-modifier",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, clear_modifier),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [CLEAR_SEARCH] =
- g_signal_new ("clear-search",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, clear_search),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [CLEAR_SELECTION] =
- g_signal_new ("clear-selection",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, clear_selection),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [CLEAR_SNIPPETS] =
- g_signal_new ("clear-snippets",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, clear_snippets),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [COPY_CLIPBOARD_EXTENDED] =
- g_signal_new ("copy-clipboard-extended",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, copy_clipboard_extended),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [CYCLE_COMPLETION] =
- g_signal_new ("cycle-completion",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, cycle_completion),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- GTK_TYPE_DIRECTION_TYPE);
-
- signals [DECREASE_FONT_SIZE] =
- g_signal_new ("decrease-font-size",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, decrease_font_size),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [DELETE_SELECTION] =
- g_signal_new ("delete-selection",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, delete_selection),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [DRAW_BUBBLES] =
- g_signal_new ("draw-bubbles",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_VOID__BOXED,
- G_TYPE_NONE,
- 1,
- CAIRO_GOBJECT_TYPE_CONTEXT);
- g_signal_set_va_marshaller (signals [DRAW_BUBBLES],
- G_TYPE_FROM_CLASS (klass),
- g_cclosure_marshal_VOID__BOXEDv);
-
- /**
- * IdeSourceView::end-macro:
- *
- * You should call #IdeSourceView::begin-macro before emitting this signal.
- *
- * Complete a macro recording sequence. This may be called more times than is necessary,
- * since #IdeSourceView will only keep the most recent macro recording. This can be
- * helpful when implementing recording sequences such as in Vim.
- *
- * Since: 3.32
- */
- signals [END_MACRO] =
- g_signal_new ("end-macro",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, end_macro),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [END_USER_ACTION] =
- g_signal_new_class_handler ("end-user-action",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_end_user_action),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [FIND_REFERENCES] =
- g_signal_new_class_handler ("find-references",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_real_find_references),
- NULL, NULL, NULL, G_TYPE_NONE, 0);
-
- signals [FOCUS_LOCATION] =
- g_signal_new ("focus-location",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeSourceViewClass, focus_location),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- IDE_TYPE_LOCATION);
-
- signals [FORMAT_SELECTION] =
- g_signal_new_class_handler ("format-selection",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_real_format_selection),
- NULL, NULL, NULL, G_TYPE_NONE, 0);
-
- signals [QUERY_CODE_ACTION] =
- g_signal_new_class_handler ("query-code-action",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_real_code_action_query),
- NULL, NULL, NULL, G_TYPE_NONE, 0);
-
- signals [GOTO_DEFINITION] =
- g_signal_new ("goto-definition",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, goto_definition),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [HIDE_COMPLETION] =
- g_signal_new ("hide-completion",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, hide_completion),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [INCREASE_FONT_SIZE] =
- g_signal_new ("increase-font-size",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, increase_font_size),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [INDENT_SELECTION] =
- g_signal_new ("indent-selection",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, indent_selection),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- G_TYPE_INT);
-
- /**
- * IdeSourceView::insert-modifier:
- * @self: An #IdeSourceView
- * @use_count: If the count property should be used to repeat.
- *
- * Inserts the current modifier character at the insert mark in the buffer.
- * If @use_count is %TRUE, then the character will be inserted
- * #IdeSourceView:count times.
- *
- * Since: 3.32
- */
- signals [INSERT_MODIFIER] =
- g_signal_new ("insert-modifier",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, insert_modifier),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- g_signal_override_class_handler ("join-lines",
- G_TYPE_FROM_CLASS (klass),
- G_CALLBACK (ide_source_view_real_join_lines));
-
- signals [JUMP] =
- g_signal_new ("jump",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeSourceViewClass, jump),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 2,
- GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
- GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- signals [MOVEMENT] =
- g_signal_new ("movement",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, movement),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 4,
- IDE_TYPE_SOURCE_VIEW_MOVEMENT,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN);
-
- /**
- * IdeSourceView::move-error:
- * @self: An #IdeSourceView.
- * @dir: The direction to move.
- *
- * Moves to the next search result either forwards or backwards.
- *
- * Since: 3.32
- */
- signals [MOVE_ERROR] =
- g_signal_new ("move-error",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, move_error),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- GTK_TYPE_DIRECTION_TYPE);
-
- signals [MOVE_SEARCH] =
- g_signal_new ("move-search",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, move_search),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 6,
- GTK_TYPE_DIRECTION_TYPE,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN,
- G_TYPE_INT);
-
- signals [PASTE_CLIPBOARD_EXTENDED] =
- g_signal_new ("paste-clipboard-extended",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, paste_clipboard_extended),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 3,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN);
-
- /**
- * IdeSourceView::pop-selection:
- *
- * Reselects a previousl selected range of text that was saved using
- * IdeSourceView::push-selection.
- *
- * Since: 3.32
- */
- signals [POP_SELECTION] =
- g_signal_new ("pop-selection",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, pop_selection),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView::pop-snippet:
- * @self: An #IdeSourceView
- * @snippet: An #IdeSnippet.
- *
- * Pops the current snippet from the sourceview if there is one.
- *
- * Since: 3.32
- */
- signals [POP_SNIPPET] =
- g_signal_new_class_handler ("pop-snippet",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- NULL,
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView::push-selection:
- *
- * Saves the current selection away to be restored by a call to
- * IdeSourceView::pop-selection. You must pop the selection to keep
- * the selection stack in consistent order.
- *
- * Since: 3.32
- */
- signals [PUSH_SELECTION] =
- g_signal_new ("push-selection",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, push_selection),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView::push-snippet:
- * @self: An #IdeSourceView
- * @snippet: An #IdeSnippet.
- * @iter: (allow-none): The location for the snippet, or %NULL.
- *
- * Pushes @snippet onto the snippet stack at either @iter or the insertion
- * mark if @iter is not provided.
- *
- * Since: 3.32
- */
- signals [PUSH_SNIPPET] =
- g_signal_new_class_handler ("push-snippet",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_CALLBACK (ide_source_view_real_push_snippet),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 2,
- IDE_TYPE_SNIPPET,
- GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- signals [REBUILD_HIGHLIGHT] =
- g_signal_new ("rebuild-highlight",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, rebuild_highlight),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [DUPLICATE_ENTIRE_LINE] =
- g_signal_new_class_handler ("duplicate-entire-line",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_real_duplicate_entire_line),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [REINDENT] =
- g_signal_new_class_handler ("reindent",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_real_reindent),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView:replay-macro:
- * @self: an #IdeSourceView.
- *
- * Replays the last series of captured events that were captured between calls
- * to #IdeSourceView::begin-macro and #IdeSourceView::end-macro.
- *
- * Since: 3.32
- */
- signals [REPLAY_MACRO] =
- g_signal_new ("replay-macro",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, replay_macro),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- signals [REQUEST_DOCUMENTATION] =
- g_signal_new ("request-documentation",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, request_documentation),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- /**
- * IdeSourceView::reset:
- *
- * This is a helper signal that will try to reset keyboard input
- * and various stateful settings of the sourceview. This is a good
- * signal to map to the "Escape" key.
- *
- * Since: 3.32
- */
- signals [RESET] =
- g_signal_new_class_handler ("reset",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_source_view_real_reset),
- NULL, NULL, NULL, G_TYPE_NONE, 0);
-
- signals [RESET_FONT_SIZE] =
- g_signal_new ("reset-font-size",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, reset_font_size),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [RESTORE_INSERT_MARK] =
- g_signal_new ("restore-insert-mark",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, restore_insert_mark),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [SAVE_INSERT_MARK] =
- g_signal_new ("save-insert-mark",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, save_insert_mark),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- g_signal_override_class_handler ("select-all",
- G_TYPE_FROM_CLASS (klass),
- G_CALLBACK (ide_source_view_real_select_all));
-
- signals [SELECT_INNER] =
- g_signal_new ("select-inner",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, select_inner),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 4,
- G_TYPE_STRING,
- G_TYPE_STRING,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN);
-
- signals [SELECT_TAG] =
- g_signal_new ("select-tag",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, select_tag),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- signals [SELECTION_THEATRIC] =
- g_signal_new ("selection-theatric",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, selection_theatric),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- IDE_TYPE_SOURCE_VIEW_THEATRIC);
-
- signals [SET_MODE] =
- g_signal_new ("set-mode",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, set_mode),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 2,
- G_TYPE_STRING,
- IDE_TYPE_SOURCE_VIEW_MODE_TYPE);
-
- signals [SET_OVERWRITE] =
- g_signal_new ("set-overwrite",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, set_overwrite),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- G_TYPE_BOOLEAN);
-
- signals [SET_SEARCH_TEXT] =
- g_signal_new ("set-search-text",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, set_search_text),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 2,
- G_TYPE_STRING,
- G_TYPE_BOOLEAN);
-
- /**
- * IdeSourceView::sort:
- * @self: an #IdeSourceView.
- * @ignore_case: If character case should be ignored.
- * @reverse: If the lines should be sorted in reverse order
- *
- * This signal is meant to be activated from keybindings to sort the currently selected lines.
- * The lines are sorted using qsort() and either strcmp() or strcasecmp().
- *
- * Since: 3.32
- */
- signals [SORT] =
- g_signal_new ("sort",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, sort),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 2,
- G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN);
-
- signals [SWAP_SELECTION_BOUNDS] =
- g_signal_new ("swap-selection-bounds",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, swap_selection_bounds),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- signals [ADD_CURSOR] =
- g_signal_new ("add-cursor",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, add_cursor),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 1,
- IDE_TYPE_CURSOR_TYPE);
-
- signals [REMOVE_CURSORS] =
- g_signal_new ("remove-cursors",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (IdeSourceViewClass, remove_cursors),
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 0);
-
- binding_set = gtk_binding_set_by_class (klass);
-
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_r,
- GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- "begin-rename", 0);
-
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_q,
- GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- "query-code-action", 0);
-
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_space,
- GDK_CONTROL_MASK | GDK_SHIFT_MASK,
- "find-references", 0);
-
- /* Override "Home" and "<Shift>Home" to use our movements
- * instead of the smart home feature of GtkSourceView.
- */
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_Home,
- 0,
- "movement", 4,
- IDE_TYPE_SOURCE_VIEW_MOVEMENT, IDE_SOURCE_VIEW_MOVEMENT_SMART_HOME,
- G_TYPE_BOOLEAN, FALSE,
- G_TYPE_BOOLEAN, TRUE,
- G_TYPE_BOOLEAN, FALSE);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_Home,
- GDK_MOD1_MASK | GDK_SHIFT_MASK,
- "movement", 4,
- IDE_TYPE_SOURCE_VIEW_MOVEMENT, IDE_SOURCE_VIEW_MOVEMENT_SMART_HOME,
- G_TYPE_BOOLEAN, TRUE,
- G_TYPE_BOOLEAN, TRUE,
- G_TYPE_BOOLEAN, FALSE);
-
- /* Remove Emoji from GtkTextView, we'll add back in various keybinding modes */
- binding_set = gtk_binding_set_by_class (g_type_class_peek (GTK_TYPE_TEXT_VIEW));
- gtk_binding_entry_remove (binding_set, GDK_KEY_period, GDK_CONTROL_MASK);
- gtk_binding_entry_remove (binding_set, GDK_KEY_semicolon, GDK_CONTROL_MASK);
-}
-
-static void
-ide_source_view_init (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- gtk_widget_add_events (GTK_WIDGET (self), GDK_ENTER_NOTIFY_MASK);
- gtk_widget_set_has_tooltip (GTK_WIDGET (self), FALSE);
-
- priv->include_regex = g_regex_new (INCLUDE_STATEMENTS,
- G_REGEX_OPTIMIZE,
- 0,
- NULL);
-
- priv->show_line_numbers = TRUE;
- priv->show_line_changes = TRUE;
- priv->show_line_diagnostics = TRUE;
- priv->interactive_completion = TRUE;
- priv->target_line_column = 0;
- priv->snippets = g_queue_new ();
- priv->selections = g_queue_new ();
- priv->font_scale = FONT_SCALE_NORMAL;
- priv->command_str = g_string_sized_new (32);
- priv->overscroll_num_lines = DEFAULT_OVERSCROLL_NUM_LINES;
-
- priv->hover = _ide_hover_new (self);
-
- priv->file_setting_bindings = ide_binding_group_new ();
- ide_binding_group_bind (priv->file_setting_bindings, "auto-indent",
- self, "auto-indent", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "indent-width",
- self, "indent-width", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "tab-width",
- self, "tab-width", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "right-margin-position",
- self, "right-margin-position", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "indent-style",
- self, "indent-style", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "show-right-margin",
- self, "show-right-margin", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "insert-matching-brace",
- self, "insert-matching-brace", G_BINDING_SYNC_CREATE);
- ide_binding_group_bind (priv->file_setting_bindings, "overwrite-braces",
- self, "overwrite-braces", G_BINDING_SYNC_CREATE);
-
- priv->buffer_signals = ide_signal_group_new (IDE_TYPE_BUFFER);
-
- ide_signal_group_connect_object (priv->buffer_signals,
- "changed",
- G_CALLBACK (ide_source_view__buffer_changed_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "request-scroll-to-insert",
- G_CALLBACK (ide_source_view__buffer_request_scroll_to_insert_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "line-flags-changed",
- G_CALLBACK (ide_source_view__buffer_line_flags_changed_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "notify::can-redo",
- G_CALLBACK (ide_source_view__buffer__notify_can_redo),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "notify::can-undo",
- G_CALLBACK (ide_source_view__buffer__notify_can_undo),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "notify::file-settings",
- G_CALLBACK (ide_source_view__buffer_notify_file_settings_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "notify::language",
- G_CALLBACK (ide_source_view__buffer_notify_language_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "notify::style-scheme",
- G_CALLBACK (ide_source_view__buffer_notify_style_scheme_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "insert-text",
- G_CALLBACK (ide_source_view__buffer_insert_text_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "insert-text",
- G_CALLBACK (ide_source_view__buffer_insert_text_after_cb),
- self,
- G_CONNECT_SWAPPED | G_CONNECT_AFTER);
- ide_signal_group_connect_object (priv->buffer_signals,
- "delete-range",
- G_CALLBACK (ide_source_view__buffer_delete_range_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "delete-range",
- G_CALLBACK (ide_source_view__buffer_delete_range_after_cb),
- self,
- G_CONNECT_SWAPPED | G_CONNECT_AFTER);
- ide_signal_group_connect_object (priv->buffer_signals,
- "mark-set",
- G_CALLBACK (ide_source_view__buffer_mark_set_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "loaded",
- G_CALLBACK (ide_source_view__buffer_loaded_cb),
- self,
- G_CONNECT_SWAPPED);
- ide_signal_group_connect_object (priv->buffer_signals,
- "notify::has-selection",
- G_CALLBACK (ide_source_view__buffer_notify_has_selection_cb),
- self,
- G_CONNECT_SWAPPED);
- g_signal_connect_object (priv->buffer_signals,
- "bind",
- G_CALLBACK (ide_source_view_bind_buffer),
- self,
- G_CONNECT_SWAPPED);
- g_signal_connect_object (priv->buffer_signals,
- "unbind",
- G_CALLBACK (ide_source_view_unbind_buffer),
- self,
- G_CONNECT_SWAPPED);
-
- g_object_bind_property_full (self, "buffer", priv->buffer_signals, "target", 0,
- ignore_invalid_buffers, NULL, NULL, NULL);
-
- dzl_widget_action_group_attach (self, "sourceview");
-}
-
-const PangoFontDescription *
-ide_source_view_get_font_desc (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
-
- return priv->font_desc;
-}
-
-/**
- * ide_source_view_get_scaled_font_desc:
- * @self: a #IdeSourceView
- *
- * Like ide_source_view_get_font_desc() but takes the editor zoom into
- * account. You must free the result with pango_font_description_free().
- *
- * Returns: (transfer full): a #PangoFontDescription
- *
- * Since: 3.32
- */
-PangoFontDescription *
-ide_source_view_get_scaled_font_desc (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- PangoFontDescription *copy;
- gdouble font_scale;
- guint font_size;
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
-
- copy = pango_font_description_copy (priv->font_desc);
- font_size = pango_font_description_get_size (priv->font_desc);
- font_scale = fontScale [priv->font_scale];
- pango_font_description_set_size (copy, font_size * font_scale);
-
- return g_steal_pointer (©);
-}
-
-void
-ide_source_view_set_font_desc (IdeSourceView *self,
- const PangoFontDescription *font_desc)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- if (font_desc != priv->font_desc)
- {
- g_clear_pointer (&priv->font_desc, pango_font_description_free);
-
- if (font_desc)
- priv->font_desc = pango_font_description_copy (font_desc);
- else
- priv->font_desc = pango_font_description_from_string (DEFAULT_FONT_DESC);
-
- priv->font_scale = FONT_SCALE_NORMAL;
-
- ide_source_view_rebuild_css (self);
- }
-}
-
-void
-ide_source_view_set_font_name (IdeSourceView *self,
- const gchar *font_name)
-{
- PangoFontDescription *font_desc = NULL;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- if (font_name)
- font_desc = pango_font_description_from_string (font_name);
- ide_source_view_set_font_desc (self, font_desc);
- if (font_desc)
- pango_font_description_free (font_desc);
-}
-
-gboolean
-ide_source_view_get_show_line_changes (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->show_line_changes;
-}
-
-void
-ide_source_view_set_show_line_changes (IdeSourceView *self,
- gboolean show_line_changes)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->show_line_changes = !!show_line_changes;
-
- if (priv->gutter)
- {
- ide_gutter_set_show_line_changes (priv->gutter, show_line_changes);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_LINE_CHANGES]);
- }
-}
-
-gboolean
-ide_source_view_get_show_line_diagnostics (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->show_line_diagnostics;
-}
-
-void
-ide_source_view_set_show_line_diagnostics (IdeSourceView *self,
- gboolean show_line_diagnostics)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->show_line_diagnostics = !!show_line_diagnostics;
-
- if (priv->gutter)
- {
- ide_gutter_set_show_line_diagnostics (priv->gutter, show_line_diagnostics);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_LINE_DIAGNOSTICS]);
- }
-}
-
-gboolean
-ide_source_view_get_show_grid_lines (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->show_grid_lines;
-}
-
-void
-ide_source_view_set_show_grid_lines (IdeSourceView *self,
- gboolean show_grid_lines)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- show_grid_lines = !!show_grid_lines;
-
- if (show_grid_lines != priv->show_grid_lines)
- {
- priv->show_grid_lines = show_grid_lines;
- if (show_grid_lines)
- gtk_source_view_set_background_pattern (GTK_SOURCE_VIEW (self),
- GTK_SOURCE_BACKGROUND_PATTERN_TYPE_GRID);
- else
- gtk_source_view_set_background_pattern (GTK_SOURCE_VIEW (self),
- GTK_SOURCE_BACKGROUND_PATTERN_TYPE_NONE);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_GRID_LINES]);
- }
-}
-
-gboolean
-ide_source_view_get_insert_matching_brace (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->insert_matching_brace;
-}
-
-void
-ide_source_view_get_iter_at_visual_column (IdeSourceView *self,
- guint column,
- GtkTextIter *location)
-{
- gunichar tab_char;
- guint visual_col = 0;
- guint tab_width;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- tab_char = g_utf8_get_char ("\t");
- tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (self));
- gtk_text_iter_set_line_offset (location, 0);
-
- while (!gtk_text_iter_ends_line (location))
- {
- if (gtk_text_iter_get_char (location) == tab_char)
- visual_col += (tab_width - (visual_col % tab_width));
- else
- ++visual_col;
-
- if (visual_col > column)
- break;
-
- /* FIXME: this does not handle invisible text correctly, but
- * * gtk_text_iter_forward_visible_cursor_position is too
- * slow */
- if (!gtk_text_iter_forward_char (location))
- break;
- }
-}
-
-gboolean
-ide_source_view_get_overwrite_braces (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->overwrite_braces;
-}
-
-void
-ide_source_view_set_insert_matching_brace (IdeSourceView *self,
- gboolean insert_matching_brace)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- insert_matching_brace = !!insert_matching_brace;
-
- if (insert_matching_brace != priv->insert_matching_brace)
- {
- priv->insert_matching_brace = insert_matching_brace;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INSERT_MATCHING_BRACE]);
- }
-}
-
-void
-ide_source_view_set_overwrite_braces (IdeSourceView *self,
- gboolean overwrite_braces)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- overwrite_braces = !!overwrite_braces;
-
- if (overwrite_braces != priv->overwrite_braces)
- {
- priv->overwrite_braces = overwrite_braces;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_OVERWRITE_BRACES]);
- }
-}
-
-/**
- * ide_source_view_get_scroll_offset:
- *
- * Gets the #IdeSourceView:scroll-offset property. This property contains the number of lines
- * that should be kept above or below the line containing the insertion cursor relative to the
- * top and bottom of the visible text window.
- *
- * Since: 3.32
- */
-guint
-ide_source_view_get_scroll_offset (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), 0);
-
- return priv->scroll_offset;
-}
-
-/**
- * ide_source_view_set_scroll_offset:
- *
- * Sets the #IdeSourceView:scroll-offset property. See ide_source_view_get_scroll_offset() for
- * more information. Set to 0 to unset this property.
- *
- * Since: 3.32
- */
-void
-ide_source_view_set_scroll_offset (IdeSourceView *self,
- guint scroll_offset)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- if (scroll_offset != priv->scroll_offset)
- {
- priv->scroll_offset = scroll_offset;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SCROLL_OFFSET]);
- }
-}
-
-/**
- * ide_source_view_get_visible_rect:
- * @self: An #IdeSourceView.
- * @visible_rect: (out): a #GdkRectangle.
- *
- * Gets the visible region in buffer coordinates that is the visible area of the buffer. This
- * is similar to gtk_text_view_get_visible_area() except that it takes into account the
- * #IdeSourceView:scroll-offset property to ensure there is space above and below the
- * visible_rect.
- *
- * Since: 3.32
- */
-void
-ide_source_view_get_visible_rect (IdeSourceView *self,
- GdkRectangle *visible_rect)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = (GtkTextView *)self;
- GdkRectangle area;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
- g_return_if_fail (visible_rect);
-
- gtk_text_view_get_visible_rect (text_view, &area);
-
- /*
- * If we don't have valid line height, not much we can do now. We can just adjust things
- * later once it becomes available.
- */
- if (priv->cached_char_height)
- {
- gint max_scroll_offset;
- gint scroll_offset;
- gint visible_lines;
- gint scroll_offset_height;
-
- visible_lines = area.height / priv->cached_char_height;
- max_scroll_offset = (visible_lines - 1) / 2;
- scroll_offset = MIN ((gint)priv->scroll_offset, max_scroll_offset);
- scroll_offset_height = priv->cached_char_height * scroll_offset;
-
- area.y += scroll_offset_height;
- area.height -= (2 * scroll_offset_height);
-
- /*
- * If we have an even number of visible lines and scrolloffset is less than our
- * desired scrolloffset, we need to remove an extra line so we don't have two
- * visible lines.
- */
- if ((scroll_offset < (gint)priv->scroll_offset) && (visible_lines & 1) == 0)
- area.height -= priv->cached_char_height;
-
- /*
- * Use a multiple of the line height so we don't jump around when
- * focusing the last line (due to Y2 not fitting in the visible area).
- */
- area.height = (area.height / priv->cached_char_height) * priv->cached_char_height;
- }
-
- *visible_rect = area;
-}
-
-void
-ide_source_view_scroll_mark_onscreen (IdeSourceView *self,
- GtkTextMark *mark,
- IdeSourceScrollAlign use_align,
- gdouble alignx,
- gdouble aligny)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GdkRectangle visible_rect;
- GdkRectangle mark_rect;
- GtkTextIter iter;
-
- IDE_ENTRY;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- ide_source_view_get_visible_rect (self, &visible_rect);
-
- buffer = gtk_text_view_get_buffer (text_view);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
- gtk_text_view_get_iter_location (text_view, &iter, &mark_rect);
-
- if (!_GDK_RECTANGLE_CONTAINS (&visible_rect, &mark_rect))
- ide_source_view_scroll_to_mark (self, mark, 0.0, use_align, alignx, aligny, TRUE);
-
- IDE_EXIT;
-}
-
-gboolean
-ide_source_view_move_mark_onscreen (IdeSourceView *self,
- GtkTextMark *mark)
-{
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GtkTextIter iter;
- GtkTextIter end;
- GdkRectangle visible_rect;
- GdkRectangle iter_rect;
-
- IDE_ENTRY;
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
- g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), FALSE);
-
- buffer = gtk_text_view_get_buffer (text_view);
-
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
- gtk_text_buffer_get_end_iter (buffer, &end);
-
- ide_source_view_get_visible_rect (self, &visible_rect);
- gtk_text_view_get_iter_location (text_view, &iter, &iter_rect);
-
- if (_GDK_RECTANGLE_CONTAINS (&visible_rect, &iter_rect))
- IDE_RETURN (FALSE);
-
- if (_GDK_RECTANGLE_Y2 (&iter_rect) > _GDK_RECTANGLE_Y2 (&visible_rect))
- gtk_text_view_get_iter_at_location (text_view, &iter,
- _GDK_RECTANGLE_X2 (&visible_rect),
- _GDK_RECTANGLE_Y2 (&visible_rect));
- else if (iter_rect.y < visible_rect.y)
- gtk_text_view_get_iter_at_location (text_view, &iter, visible_rect.x, visible_rect.y);
- else
- return gtk_text_view_move_mark_onscreen (text_view, mark);
-
- gtk_text_buffer_move_mark (buffer, mark, &iter);
-
- IDE_RETURN (TRUE);
-}
-
-static gboolean
-ide_source_view_mark_is_onscreen (IdeSourceView *self,
- GtkTextMark *mark)
-{
- GtkTextBuffer *buffer;
- GdkRectangle visible_rect;
- GdkRectangle mark_rect;
- GtkTextIter iter;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
- g_assert (GTK_IS_TEXT_MARK (mark));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
-
- ide_source_view_get_visible_rect (self, &visible_rect);
- gtk_text_view_get_iter_location (GTK_TEXT_VIEW (self), &iter, &mark_rect);
-
- return (_GDK_RECTANGLE_CONTAINS (&visible_rect, &mark_rect));
-}
-
-static void
-ide_source_view__vadj_animation_completed (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_SOURCE_VIEW (self));
-
- /*
- * If the mark we were scrolling to is not yet on screen, then just wait for another size
- * allocate so that we can continue making progress.
- */
- if (!ide_source_view_mark_is_onscreen (self, priv->scroll_mark))
- IDE_EXIT;
-
- priv->scrolling_to_scroll_mark = FALSE;
-
- IDE_EXIT;
-}
-
-/*
- * Many parts of this function were taken from gtk_text_view_scroll_to_iter ()
- * https://developer.gnome.org/gtk3/stable/GtkTextView.html#gtk-text-view-scroll-to-iter
- */
-void
-ide_source_view_scroll_to_iter (IdeSourceView *self,
- const GtkTextIter *iter,
- gdouble within_margin,
- IdeSourceScrollAlign use_align,
- gdouble xalign,
- gdouble yalign,
- gboolean animate_scroll)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextView *text_view = (GtkTextView *)self;
- GtkTextBuffer *buffer;
- GdkRectangle rect;
- GdkRectangle screen;
- gint xvalue = 0;
- gint yvalue = 0;
- gint scroll_dest;
- gint screen_bottom;
- gint screen_right;
- gint screen_xoffset;
- gint screen_yoffset;
- gint current_x_scroll;
- gint current_y_scroll;
- GtkAdjustment *hadj;
- GtkAdjustment *vadj;
-
- IDE_ENTRY;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
- g_return_if_fail (iter != NULL);
- g_return_if_fail (within_margin >= 0.0 && within_margin <= 0.5);
- g_return_if_fail (xalign >= 0.0 && xalign <= 1.0);
- g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
-
- if (!ide_source_view_can_animate (self))
- animate_scroll = FALSE;
-
- buffer = gtk_text_view_get_buffer (text_view);
- gtk_text_buffer_move_mark (buffer, priv->scroll_mark, iter);
-
- hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self));
- vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self));
-
- gtk_text_view_get_iter_location (text_view,
- iter,
- &rect);
-
- gtk_text_view_get_visible_rect (text_view, &screen);
-
- current_x_scroll = screen.x;
- current_y_scroll = screen.y;
-
- screen_xoffset = screen.width * within_margin;
- screen_yoffset = screen.height * within_margin;
-
- screen.x += screen_xoffset;
- screen.y += screen_yoffset;
- screen.width -= screen_xoffset * 2;
- screen.height -= screen_yoffset * 2;
-
-
- /* paranoia check */
- if (screen.width < 1)
- screen.width = 1;
- if (screen.height < 1)
- screen.height = 1;
-
- /* The -1 here ensures that we leave enough space to draw the cursor
- * when this function is used for horizontal scrolling.
- */
- screen_right = screen.x + screen.width - 1;
- screen_bottom = screen.y + screen.height;
-
-
- /* The alignment affects the point in the target character that we
- * choose to align. If we're doing right/bottom alignment, we align
- * the right/bottom edge of the character the mark is at; if we're
- * doing left/top we align the left/top edge of the character; if
- * we're doing center alignment we align the center of the
- * character.
- */
-
- /* Vertical alignment */
- scroll_dest = current_y_scroll;
- if (SCROLL_Y (use_align))
- {
- scroll_dest = rect.y + (rect.height * yalign) - (screen.height * yalign);
-
- /* if scroll_dest < screen.y, we move a negative increment (up),
- * else a positive increment (down)
- */
- yvalue = scroll_dest - screen.y + screen_yoffset;
- }
- else
- {
- /* move minimum to get onscreen */
- if (rect.y < screen.y)
- {
- scroll_dest = rect.y;
- yvalue = scroll_dest - screen.y - screen_yoffset;
- }
- else if ((rect.y + rect.height) > screen_bottom)
- {
- scroll_dest = rect.y + rect.height;
- yvalue = scroll_dest - screen_bottom + screen_yoffset;
- }
- }
- yvalue += current_y_scroll;
-
- /* Scroll offset adjustment */
- if (priv->cached_char_height)
- {
- gint max_scroll_offset;
- gint visible_lines;
- gint scroll_offset;
- gint scroll_offset_height;
-
- visible_lines = screen.height / priv->cached_char_height;
- max_scroll_offset = (visible_lines - 1) / 2;
- scroll_offset = MIN ((gint)priv->scroll_offset, max_scroll_offset);
- scroll_offset_height = priv->cached_char_height * scroll_offset;
-
- if (scroll_offset_height > 0)
- {
- if (rect.y - scroll_offset_height < yvalue)
- yvalue -= (scroll_offset_height - (rect.y - yvalue));
- else if (_GDK_RECTANGLE_Y2 (&rect) + scroll_offset_height > yvalue + screen.height)
- yvalue += (_GDK_RECTANGLE_Y2 (&rect) + scroll_offset_height) - (yvalue + screen.height);
- }
- }
-
- /* Horizontal alignment */
- scroll_dest = current_x_scroll;
- if (SCROLL_X (use_align))
- {
- scroll_dest = rect.x + (rect.width * xalign) - (screen.width * xalign);
-
- /* if scroll_dest < screen.y, we move a negative increment (left),
- * else a positive increment (right)
- */
- xvalue = scroll_dest - screen.x + screen_xoffset;
- }
- else
- {
- /* move minimum to get onscreen */
- if (rect.x < screen.x)
- {
- scroll_dest = rect.x;
- xvalue = scroll_dest - screen.x - screen_xoffset;
- }
- else if ((rect.x + rect.width) > screen_right)
- {
- scroll_dest = rect.x + rect.width;
- xvalue = scroll_dest - screen_right + screen_xoffset;
- }
- }
- xvalue += current_x_scroll;
-
- if (animate_scroll)
- {
- GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
- guint duration_msec = LARGE_SCROLL_DURATION_MSEC;
- gdouble difference;
- gdouble page_size;
- gdouble current;
-
- current = gtk_adjustment_get_value (vadj);
- page_size = gtk_adjustment_get_page_size (vadj);
- difference = ABS (current - yvalue);
-
- /*
- * Ignore animations if we are scrolling less than two full lines. This
- * helps when pressing up/down for key repeat. Also, if it's a partial
- * page scroll (less than page size), use less time to animate, so it
- * isn't so annoying.
- */
- if (difference < (priv->cached_char_height * 2))
- goto ignore_animation;
- else if (difference <= page_size)
- duration_msec = SMALL_SCROLL_DURATION_MSEC;
-
- priv->scrolling_to_scroll_mark = TRUE;
-
- if (priv->hadj_animation != NULL)
- {
- dzl_animation_stop (priv->hadj_animation);
- g_clear_weak_pointer (&priv->hadj_animation);
- }
-
- priv->hadj_animation =
- dzl_object_animate (hadj,
- DZL_ANIMATION_EASE_OUT_CUBIC,
- duration_msec,
- frame_clock,
- "value", (double)xvalue,
- NULL);
- g_object_add_weak_pointer (G_OBJECT (priv->hadj_animation),
- (gpointer *)&priv->hadj_animation);
-
- if (priv->vadj_animation != NULL)
- {
- dzl_animation_stop (priv->vadj_animation);
- g_clear_weak_pointer (&priv->vadj_animation);
- }
-
- priv->vadj_animation =
- dzl_object_animate_full (vadj,
- DZL_ANIMATION_EASE_OUT_CUBIC,
- duration_msec,
- frame_clock,
- (GDestroyNotify)ide_source_view__vadj_animation_completed,
- self,
- "value", (double)yvalue,
- NULL);
- g_object_add_weak_pointer (G_OBJECT (priv->vadj_animation),
- (gpointer *)&priv->vadj_animation);
- }
- else
- {
-ignore_animation:
- gtk_adjustment_set_value (hadj, xvalue);
- gtk_adjustment_set_value (vadj, yvalue);
- }
-
- IDE_EXIT;
-}
-
-void
-ide_source_view_scroll_to_mark (IdeSourceView *self,
- GtkTextMark *mark,
- gdouble within_margin,
- IdeSourceScrollAlign use_align,
- gdouble xalign,
- gdouble yalign,
- gboolean animate_scroll)
-{
- GtkTextBuffer *buffer;
- GtkTextIter iter;
-
- IDE_ENTRY;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
- g_return_if_fail (GTK_IS_TEXT_MARK (mark));
- g_return_if_fail (xalign >= 0.0);
- g_return_if_fail (xalign <= 1.0);
- g_return_if_fail (yalign >= 0.0);
- g_return_if_fail (yalign <= 1.0);
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
- ide_source_view_scroll_to_iter (self, &iter, within_margin, use_align, xalign, yalign,
- animate_scroll);
-
-#ifdef IDE_ENABLE_TRACE
- {
- const gchar *name = gtk_text_mark_get_name (mark);
- IDE_TRACE_MSG ("Scrolling to mark \"%s\" at %d:%d",
- name ? name : "unnamed",
- gtk_text_iter_get_line (&iter),
- gtk_text_iter_get_line_offset (&iter));
- }
-#endif
-
- IDE_EXIT;
-}
-
-gboolean
-ide_source_view_place_cursor_onscreen (IdeSourceView *self)
-{
- GtkTextBuffer *buffer;
- GtkTextMark *insert;
- gboolean ret;
-
- IDE_ENTRY;
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
- insert = gtk_text_buffer_get_insert (buffer);
-
- ret = ide_source_view_move_mark_onscreen (self, insert);
-
- IDE_RETURN (ret);
-}
-
-/**
- * ide_source_view_get_file_settings:
- * @self: an #IdeSourceView.
- *
- * Gets the #IdeSourceView:file-settings property. This contains various
- * settings for how the file should be rendered in the view, and preferences
- * such as spaces vs tabs.
- *
- * Returns: (transfer none) (nullable): An #IdeFileSettings or %NULL.
- *
- * Since: 3.32
- */
-IdeFileSettings *
-ide_source_view_get_file_settings (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
-
- return (IdeFileSettings *)ide_binding_group_get_source (priv->file_setting_bindings);
-}
-
-gboolean
-ide_source_view_get_highlight_current_line (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->highlight_current_line;
-}
-
-void
-ide_source_view_set_highlight_current_line (IdeSourceView *self,
- gboolean highlight_current_line)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- /*
- * This overrides the default GtkSourceView::highlight-current-line so that
- * we can turn off the line highlight when the IdeSourceView is not in focus.
- * See ide_source_view_real_focus_in_event() and
- * ide_source_view_real_focus_out_event() for the machinery.
- */
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- highlight_current_line = !!highlight_current_line;
-
- if (highlight_current_line != priv->highlight_current_line)
- {
- priv->highlight_current_line = highlight_current_line;
- g_object_notify (G_OBJECT (self), "highlight-current-line");
- }
-}
-
-guint
-ide_source_view_get_visual_column (IdeSourceView *self,
- const GtkTextIter *location)
-{
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), 0);
-
- return gtk_source_view_get_visual_column(GTK_SOURCE_VIEW (self), location);
-}
-
-void
-ide_source_view_get_visual_position (IdeSourceView *self,
- guint *line,
- guint *line_column)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkTextBuffer *buffer;
- GtkTextIter iter;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
-
- if (!gtk_widget_has_focus (GTK_WIDGET (self)))
- {
- gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, priv->saved_line, 0);
- ide_source_view_get_iter_at_visual_column (self, priv->saved_line_column, &iter);
- }
- else
- {
- GtkTextMark *mark;
-
- mark = gtk_text_buffer_get_insert (buffer);
- gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
- }
-
- if (line)
- *line = gtk_text_iter_get_line (&iter);
-
- if (line_column)
- *line_column = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (self), &iter);
-}
-
-gboolean
-ide_source_view_get_show_line_numbers (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->show_line_numbers;
-}
-
-void
-ide_source_view_set_show_line_numbers (IdeSourceView *self,
- gboolean show_line_numbers)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->show_line_numbers = !!show_line_numbers;
-
- if (priv->gutter)
- {
- ide_gutter_set_show_line_numbers (priv->gutter, show_line_numbers);
- g_object_notify (G_OBJECT (self), "show-line-numbers");
- }
-}
-
-gboolean
-ide_source_view_get_show_relative_line_numbers (IdeSourceView *self)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
-
- return priv->show_relative_line_numbers;
-}
-
-void
-ide_source_view_set_show_relative_line_numbers (IdeSourceView *self,
- gboolean show_relative_line_numbers)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
-
- priv->show_relative_line_numbers = !!show_relative_line_numbers;
-
- if (priv->gutter)
- {
- ide_gutter_set_show_relative_line_numbers (priv->gutter, show_relative_line_numbers);
- g_object_notify (G_OBJECT (self), "show-relative-line-numbers");
- }
-}
-
-/**
- * ide_source_view_set_gutter:
- * @self: a #IdeSourceView
- * @gutter: an #IdeGutter
- *
- * Allows setting the gutter for the sourceview.
- *
- * Generally, this will always be #IdeOmniGutterRenderer. However, to avoid
- * circular dependencies, an interface is used to allow plugins to set
- * this object.
- *
- * Since: 3.32
- */
-void
-ide_source_view_set_gutter (IdeSourceView *self,
- IdeGutter *gutter)
-{
- IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
- GtkSourceGutter *left_gutter;
-
- g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
- g_return_if_fail (!gutter || IDE_IS_GUTTER (gutter));
- g_return_if_fail (!gutter || GTK_SOURCE_IS_GUTTER_RENDERER (gutter));
-
- if (gutter == priv->gutter)
- return;
-
- left_gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (self),
- GTK_TEXT_WINDOW_LEFT);
-
- if (priv->gutter)
- {
- gtk_source_gutter_remove (left_gutter, GTK_SOURCE_GUTTER_RENDERER (priv->gutter));
- g_clear_object (&priv->gutter);
- }
-
- if (gutter)
- {
- priv->gutter = g_object_ref_sink (gutter);
- gtk_source_gutter_insert (left_gutter,
- GTK_SOURCE_GUTTER_RENDERER (gutter),
- 0);
- ide_gutter_set_show_line_numbers (priv->gutter, priv->show_line_numbers);
- ide_gutter_set_show_relative_line_numbers (priv->gutter, priv->show_relative_line_numbers);
- ide_gutter_set_show_line_changes (priv->gutter, priv->show_line_changes);
- ide_gutter_set_show_line_diagnostics (priv->gutter, priv->show_line_diagnostics);
- ide_gutter_style_changed (gutter);
- }
-
- g_object_notify (G_OBJECT (self), "show-line-changes");
- g_object_notify (G_OBJECT (self), "show-line-diagnostics");
- g_object_notify (G_OBJECT (self), "show-line-numbers");
- g_object_notify (G_OBJECT (self), "show-relative-line-numbers");
}
diff --git a/src/libide/sourceview/ide-source-view.h b/src/libide/sourceview/ide-source-view.h
index 74b1147b3..445da7e7f 100644
--- a/src/libide/sourceview/ide-source-view.h
+++ b/src/libide/sourceview/ide-source-view.h
@@ -1,6 +1,6 @@
/* ide-source-view.h
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,161 +20,18 @@
#pragma once
-#if !defined (IDE_SOURCEVIEW_INSIDE) && !defined (IDE_SOURCEVIEW_COMPILATION)
-# error "Only <libide-sourceview.h> can be included directly."
-#endif
+#include <libide-core.h>
#include <gtksourceview/gtksource.h>
-#include <libide-code.h>
-
-#include "ide-gutter.h"
G_BEGIN_DECLS
#define IDE_TYPE_SOURCE_VIEW (ide_source_view_get_type())
IDE_AVAILABLE_IN_ALL
-G_DECLARE_DERIVABLE_TYPE (IdeSourceView, ide_source_view, IDE, SOURCE_VIEW, GtkSourceView)
-
-typedef enum
-{
- IDE_CURSOR_COLUMN,
- IDE_CURSOR_SELECT,
- IDE_CURSOR_MATCH
-} IdeCursorType;
-
-typedef enum
-{
- IDE_SOURCE_SCROLL_NONE = 0,
- IDE_SOURCE_SCROLL_BOTH = 1,
- IDE_SOURCE_SCROLL_X = 1 << 1,
- IDE_SOURCE_SCROLL_Y = 1 << 2,
-} IdeSourceScrollAlign;
-
-struct _IdeSourceViewClass
-{
- GtkSourceViewClass parent_class;
-
- void (*focus_location) (IdeSourceView *self,
- IdeLocation *location);
- void (*goto_definition) (IdeSourceView *self);
- void (*move_error) (IdeSourceView *self,
- GtkDirectionType dir);
- void (*rebuild_highlight) (IdeSourceView *self);
- void (*request_documentation) (IdeSourceView *self);
- void (*sort) (IdeSourceView *self,
- gboolean ignore_case,
- gboolean reverse);
- void (*begin_rename) (IdeSourceView *self);
- void (*add_cursor) (IdeSourceView *self,
- guint type);
- void (*remove_cursors) (IdeSourceView *self);
-};
+G_DECLARE_FINAL_TYPE (IdeSourceView, ide_source_view, IDE, SOURCE_VIEW, GtkSourceView)
IDE_AVAILABLE_IN_ALL
-guint ide_source_view_get_visual_column (IdeSourceView *self,
- const GtkTextIter
*location);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_get_visual_position (IdeSourceView *self,
- guint *line,
- guint
*line_column);
-IDE_AVAILABLE_IN_ALL
-IdeFileSettings *ide_source_view_get_file_settings (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-const PangoFontDescription *ide_source_view_get_font_desc (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-PangoFontDescription *ide_source_view_get_scaled_font_desc (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_highlight_current_line (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_insert_matching_brace (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_get_iter_at_visual_column (IdeSourceView *self,
- guint
column,
- GtkTextIter
*location);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_overwrite_braces (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_overwrite (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-guint ide_source_view_get_scroll_offset (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_show_grid_lines (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_show_line_changes (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_show_line_diagnostics (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_show_line_numbers (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_show_relative_line_numbers (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_get_visible_rect (IdeSourceView *self,
- GdkRectangle
*visible_rect);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_font_desc (IdeSourceView *self,
- const PangoFontDescription
*font_desc);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_font_name (IdeSourceView *self,
- const gchar
*font_name);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_highlight_current_line (IdeSourceView *self,
- gboolean
highlight_current_line);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_insert_matching_brace (IdeSourceView *self,
- gboolean
insert_matching_brace);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_overwrite_braces (IdeSourceView *self,
- gboolean
overwrite_braces);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_scroll_offset (IdeSourceView *self,
- guint
scroll_offset);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_show_grid_lines (IdeSourceView *self,
- gboolean
show_grid_lines);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_show_line_changes (IdeSourceView *self,
- gboolean
show_line_changes);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_show_line_diagnostics (IdeSourceView *self,
- gboolean
show_line_diagnostics);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_show_line_numbers (IdeSourceView *self,
- gboolean
show_line_numbers);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_show_relative_line_numbers (IdeSourceView *self,
- gboolean
show_relative_line_numbers);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_move_mark_onscreen (IdeSourceView *self,
- GtkTextMark
*mark);
-IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_place_cursor_onscreen (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_scroll_mark_onscreen (IdeSourceView *self,
- GtkTextMark *mark,
- IdeSourceScrollAlign
use_align,
- gdouble
alignx,
- gdouble
aligny);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_scroll_to_mark (IdeSourceView *self,
- GtkTextMark *mark,
- gdouble
within_margin,
- IdeSourceScrollAlign
use_align,
- gdouble
xalign,
- gdouble
yalign,
- gboolean
animate_scroll);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_scroll_to_iter (IdeSourceView *self,
- const GtkTextIter *iter,
- gdouble
within_margin,
- IdeSourceScrollAlign
use_align,
- gdouble
xalign,
- gdouble
yalign,
- gboolean
animate_scroll);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_scroll_to_insert (IdeSourceView
*self);
-IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_gutter (IdeSourceView *self,
- IdeGutter
*gutter);
+GtkWidget *ide_source_view_new (void);
G_END_DECLS
diff --git a/src/libide/sourceview/libide-sourceview.gresource.xml
b/src/libide/sourceview/libide-sourceview.gresource.xml
index 33fffdf8b..203de6496 100644
--- a/src/libide/sourceview/libide-sourceview.gresource.xml
+++ b/src/libide/sourceview/libide-sourceview.gresource.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/libide-sourceview">
- <file preprocess="xml-stripblanks">gtk/menus.ui</file>
</gresource>
</gresources>
diff --git a/src/libide/sourceview/libide-sourceview.h b/src/libide/sourceview/libide-sourceview.h
index d8345b963..541bb49dd 100644
--- a/src/libide/sourceview/libide-sourceview.h
+++ b/src/libide/sourceview/libide-sourceview.h
@@ -26,27 +26,9 @@ G_BEGIN_DECLS
#define IDE_SOURCEVIEW_INSIDE
-#include"ide-completion-context.h"
-#include"ide-completion-display.h"
-#include"ide-completion-proposal.h"
-#include"ide-completion-list-box-row.h"
-#include"ide-completion-provider.h"
-#include"ide-completion-types.h"
-#include"ide-completion.h"
-#include"ide-hover-context.h"
-#include"ide-hover-provider.h"
-#include"ide-line-change-gutter-renderer.h"
-#include"ide-gutter.h"
-#include"ide-indenter.h"
-#include"ide-snippet-chunk.h"
-#include"ide-snippet-context.h"
-#include"ide-snippet-parser.h"
-#include"ide-snippet-storage.h"
-#include"ide-snippet-types.h"
-#include"ide-snippet.h"
-#include"ide-source-search-context.h"
-#include"ide-source-view.h"
-#include"ide-text-util.h"
+#include "ide-cursor.h"
+#include "ide-line-change-gutter-renderer.h"
+#include "ide-source-view.h"
#define IDE_SOURCEVIEW_INSIDE
diff --git a/src/libide/sourceview/meson.build b/src/libide/sourceview/meson.build
index 6e0b2ab9f..163b144b6 100644
--- a/src/libide/sourceview/meson.build
+++ b/src/libide/sourceview/meson.build
@@ -10,14 +10,12 @@ libide_sourceview_generated_headers = []
#
libide_sourceview_private_headers = [
- 'ide-cursor.h',
+ 'ide-text-util.h',
]
libide_sourceview_public_headers = [
- 'ide-gutter.h',
'ide-line-change-gutter-renderer.h',
'ide-source-view.h',
- 'ide-text-util.h',
'libide-sourceview.h',
]
@@ -32,13 +30,11 @@ install_headers(libide_sourceview_public_headers, subdir: libide_sourceview_head
#
libide_sourceview_private_sources = [
- 'ide-cursor.c',
- 'ide-line-change-gutter-renderer.c',
'ide-text-util.c',
]
libide_sourceview_public_sources = [
- 'ide-gutter.c',
+ 'ide-line-change-gutter-renderer.c',
'ide-source-view.c',
]
@@ -84,7 +80,6 @@ libide_sourceview_deps = [
libide_code_dep,
libide_plugins_dep,
libide_io_dep,
- libide_gui_dep,
]
#
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]