[gnome-builder] editor: hook up search bar
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] editor: hook up search bar
- Date: Wed, 19 Jul 2017 11:16:42 +0000 (UTC)
commit 3494df83e013ae4d1ecc3e766aa5b34b4d4870bd
Author: Christian Hergert <chergert redhat com>
Date: Tue Jul 11 23:04:34 2017 -0700
editor: hook up search bar
This starts hooking up the search bar. It is different than
previous in that we are using a new settings/context at the
editor layer rather than from the SourceView so that we can
easily destroy objects when not in use.
There is still a lot of work that can go on to refactor all of
this, but it is a start.
data/keybindings/default.css | 2 +-
data/keybindings/emacs.css | 4 +-
data/keybindings/vim.css | 33 ++-
libide/editor/ide-editor-private.h | 6 +
libide/editor/ide-editor-search-bar.c | 473 ++++++++++++++++++++-----------
libide/editor/ide-editor-search-bar.h | 14 +-
libide/editor/ide-editor-search-bar.ui | 18 +-
libide/editor/ide-editor-view-actions.c | 64 ++++-
libide/editor/ide-editor-view.c | 239 ++++++++++++++++-
libide/editor/ide-editor-view.h | 32 ++-
10 files changed, 674 insertions(+), 211 deletions(-)
---
diff --git a/data/keybindings/default.css b/data/keybindings/default.css
index b5aed2c..43382f5 100644
--- a/data/keybindings/default.css
+++ b/data/keybindings/default.css
@@ -12,7 +12,7 @@
bind "<shift>F7" { "action" ("frame", "show-spellcheck", "1") };
bind "<ctrl><shift>e" { "add-cursor" (column) };
bind "<ctrl><shift>d" { "add-cursor" (match) };
- bind "<ctrl>h" { "action" ("frame", "find-replace", "3") };
+ bind "<ctrl>h" { "action" ("editor-view", "find-replace", "") };
bind "<ctrl>o" { "action" ("win", "open-with-dialog", "") };
bind "<ctrl>s" { "action" ("view", "save", "") };
bind "<ctrl><shift>s" { "action" ("view", "save-as", "") };
diff --git a/data/keybindings/emacs.css b/data/keybindings/emacs.css
index 39ab044..9ac7fbb 100644
--- a/data/keybindings/emacs.css
+++ b/data/keybindings/emacs.css
@@ -61,8 +61,8 @@
bind "<ctrl>c" { "set-mode" ("emacs-c", transient) };
bind "<ctrl>underscore" { "undo" () };
bind "<alt>x" { "action" ("win", "show-command-bar", "") };
- bind "<ctrl>r" { "action" ("frame", "find", "2") };
- bind "<ctrl>s" { "action" ("frame", "find", "3") };
+ bind "<ctrl>r" { "action" ("editor-view", "find", "") };
+ bind "<ctrl>s" { "action" ("editor-view", "find", "") };
bind "<alt>dollar" { "action" ("frame", "spellcheck", "1") };
bind "<alt>period" { "goto-definition" () };
bind "<alt>n" { "move-error" (down) };
diff --git a/data/keybindings/vim.css b/data/keybindings/vim.css
index 905acf5..3c52fe7 100644
--- a/data/keybindings/vim.css
+++ b/data/keybindings/vim.css
@@ -104,7 +104,7 @@
bind "<ctrl>s" { "action" ("view", "save", "") };
bind "<ctrl><shift>s" { "action" ("view", "save-as", "") };
bind "<ctrl><shift>o" { "action" ("win", "open-with-dialog", "") };
- bind "<ctrl>k" { "action" ("view-stack", "show-list", "") };
+ bind "<ctrl>k" { "action" ("layoutstack", "show-list", "") };
bind "<ctrl>minus" { "decrease-font-size" () };
bind "<ctrl>plus" { "increase-font-size" () };
bind "<ctrl>equal" { "increase-font-size" () };
@@ -173,20 +173,21 @@
bind "colon" { "action" ("win", "show-command-bar", "") };
/* cycle "tabs" */
- bind "<ctrl><alt>Page_Up" { "action" ("view-stack", "previous-view", "") };
- bind "<ctrl><alt>KP_Page_Up" { "action" ("view-stack", "previous-view", "") };
- bind "<ctrl><alt>Page_Down" { "action" ("view-stack", "next-view", "") };
- bind "<ctrl><alt>KP_Page_Down" { "action" ("view-stack", "next-view", "") };
+ bind "<ctrl><alt>Page_Up" { "action" ("layoutstack", "previous-view", "") };
+ bind "<ctrl><alt>KP_Page_Up" { "action" ("layoutstack", "previous-view", "") };
+ bind "<ctrl><alt>Page_Down" { "action" ("layoutstack", "next-view", "") };
+ bind "<ctrl><alt>KP_Page_Down" { "action" ("layoutstack", "next-view", "") };
/* replay the last recording */
bind "period" { "replay-macro" (1) };
/* start search backward */
- bind "question" { "action" ("frame", "find", "2") };
+ /* TODO: use internal sourceview search */
+ bind "question" { "action" ("editor-view", "find", "") };
/* start search */
- bind "slash" { "action" ("frame", "find", "3") };
- bind "KP_Divide" { "action" ("frame", "find", "3") };
+ bind "slash" { "action" ("editor-view", "find", "") };
+ bind "KP_Divide" { "action" ("editor-view", "find", "") };
/* insert at cursor */
bind "i" { "begin-macro" ()
@@ -553,8 +554,8 @@
bind "<ctrl>v" { "set-mode" ("vim-visual-block", permanent) };
/* navigation */
- bind "<ctrl>o" { "action" ("view-stack", "go-backward", "") };
- bind "<ctrl>i" { "action" ("view-stack", "go-forward", "") };
+ bind "<ctrl>o" { "action" ("layoutstack", "go-backward", "") };
+ bind "<ctrl>i" { "action" ("layoutstack", "go-forward", "") };
/* window controls */
bind "<ctrl>w" { "set-mode" ("vim-normal-ctrl-w", transient) };
@@ -1517,8 +1518,8 @@
bind "<shift>u" { "set-mode" ("vim-normal-g-u", transient) };
/* cycle "tabs" */
- bind "<shift>t" { "action" ("view-stack", "previous-view", "") };
- bind "t" { "action" ("view-stack", "next-view", "") };
+ bind "<shift>t" { "action" ("layoutstack", "previous-view", "") };
+ bind "t" { "action" ("layoutstack", "next-view", "") };
}
@binding-set builder-vim-source-view-normal-g-u
@@ -1849,12 +1850,12 @@
@binding-set builder-vim-source-view-normal-ctrl-w
{
- bind "v" { "action" ("view-stack", "split-right", "") "grab_focus" () };
- bind "<ctrl>v" { "action" ("view-stack", "split-right", "") "grab_focus" () };
+ bind "v" { "action" ("layoutstack", "split-right", "") "grab_focus" () };
+ bind "<ctrl>v" { "action" ("layoutstack", "split-right", "") "grab_focus" () };
- bind "c" { "action" ("view", "close", "") };
+ bind "c" { "action" ("layoutstack", "close-view", "") };
- bind "s" { "action" ("view-stack", "split-down", "") };
+ bind "s" { "action" ("layoutstack", "split-view", "") };
bind "w" { "action" ("view-grid", "focus-neighbor", "0") };
bind "<ctrl>w" { "action" ("view-grid", "focus-neighbor", "0") };
diff --git a/libide/editor/ide-editor-private.h b/libide/editor/ide-editor-private.h
index b47da46..8e6b693 100644
--- a/libide/editor/ide-editor-private.h
+++ b/libide/editor/ide-editor-private.h
@@ -40,6 +40,11 @@ struct _IdeEditorView
DzlBindingGroup *buffer_bindings;
DzlSignalGroup *buffer_signals;
+ GtkSourceSearchSettings *search_settings;
+ GtkSourceSearchContext *search_context;
+
+ GCancellable *destroy_cancellable;
+
GtkSourceMap *map;
GtkRevealer *map_revealer;
GtkOverlay *overlay;
@@ -59,6 +64,7 @@ struct _IdeEditorView
void _ide_editor_view_init_actions (IdeEditorView *self);
void _ide_editor_view_init_settings (IdeEditorView *self);
void _ide_editor_view_init_shortcuts (IdeEditorView *self);
+void _ide_editor_view_update_actions (IdeEditorView *self);
void _ide_editor_sidebar_set_open_pages (IdeEditorSidebar *self,
GListModel *open_pages);
void _ide_editor_perspective_init_actions (IdeEditorPerspective *self);
diff --git a/libide/editor/ide-editor-search-bar.c b/libide/editor/ide-editor-search-bar.c
index 765f297..7b7b42b 100644
--- a/libide/editor/ide-editor-search-bar.c
+++ b/libide/editor/ide-editor-search-bar.c
@@ -30,15 +30,13 @@ struct _IdeEditorSearchBar
DzlBin parent_instance;
/* Owned references */
- GtkSourceSearchSettings *search_settings;
- GtkSourceSearchContext *search_context;
- DzlSignalGroup *search_context_signals;
+ DzlSignalGroup *buffer_signals;
+ GtkSourceSearchContext *context;
+ DzlSignalGroup *context_signals;
+ GtkSourceSearchSettings *settings;
+ DzlSignalGroup *settings_signals;
GdTaggedEntryTag *search_entry_tag;
- /* Weak pointers */
- IdeBuffer *buffer;
- IdeSourceView *view;
-
/* Template widgets */
GtkCheckButton *case_sensitive;
GtkButton *close_button;
@@ -53,8 +51,8 @@ struct _IdeEditorSearchBar
enum {
PROP_0,
- PROP_BUFFER,
- PROP_VIEW,
+ PROP_CONTEXT,
+ PROP_SETTINGS,
N_PROPS
};
@@ -62,6 +60,131 @@ G_DEFINE_TYPE (IdeEditorSearchBar, ide_editor_search_bar, DZL_TYPE_BIN)
static GParamSpec *properties [N_PROPS];
+static void
+ide_editor_search_bar_toggle_search_options (GSimpleAction *action,
+ GVariant *state,
+ gpointer user_data)
+{
+ IdeEditorSearchBar *self = user_data;
+ gboolean visible;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ visible = !gtk_widget_get_visible (GTK_WIDGET (self->search_options));
+ gtk_widget_set_visible (GTK_WIDGET (self->search_options), visible);
+}
+
+gboolean
+ide_editor_search_bar_get_replace_mode (IdeEditorSearchBar *self)
+{
+ g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), FALSE);
+
+ return gtk_widget_get_visible (GTK_WIDGET (self->replace_entry));
+}
+
+void
+ide_editor_search_bar_set_replace_mode (IdeEditorSearchBar *self,
+ gboolean replace_mode)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ gtk_widget_set_visible (GTK_WIDGET (self->replace_entry), replace_mode);
+ gtk_widget_set_visible (GTK_WIDGET (self->replace_button), replace_mode);
+ gtk_widget_set_visible (GTK_WIDGET (self->replace_all_button), replace_mode);
+}
+
+static void
+ide_editor_search_bar_toggle_search_replace (GSimpleAction *action,
+ GVariant *state,
+ gpointer user_data)
+{
+ IdeEditorSearchBar *self = user_data;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ ide_editor_search_bar_set_replace_mode (self, !ide_editor_search_bar_get_replace_mode (self));
+}
+
+static void
+ide_editor_search_bar_replace (GSimpleAction *action,
+ GVariant *state,
+ gpointer user_data)
+{
+ IdeEditorSearchBar *self = user_data;
+ g_autofree gchar *unescaped_replace_text = NULL;
+ g_autoptr(GError) error = NULL;
+ GtkSourceBuffer *buffer;
+ const gchar *replace_text;
+ const gchar *search_text;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gint position;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (self->settings == NULL || self->context == NULL)
+ return;
+
+ search_text = gtk_source_search_settings_get_search_text (self->settings);
+ replace_text = gtk_entry_get_text (GTK_ENTRY (self->replace_entry));
+
+ if (ide_str_empty0 (search_text) || replace_text == NULL)
+ return;
+
+ unescaped_replace_text = gtk_source_utils_unescape_search_text (replace_text);
+
+ buffer = gtk_source_search_context_get_buffer (self->context);
+ gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
+ position = gtk_source_search_context_get_occurrence_position (self->context, &begin, &end);
+
+ if (position > 0)
+ {
+ /* Temporarily disable updating the search position label to prevent flickering */
+ dzl_signal_group_block (self->buffer_signals);
+
+ gtk_source_search_context_replace2 (self->context, &begin, &end,
+ unescaped_replace_text, -1, &error);
+
+ /* Re-enable updating the search position label. The next-search-result action
+ * below will cause it to update. */
+ dzl_signal_group_unblock (self->buffer_signals);
+
+ if (error != NULL)
+ g_warning ("%s", error->message);
+
+ dzl_gtk_widget_action (GTK_WIDGET (self), "editor-view", "move-next-search-result", NULL);
+ }
+}
+
+static void
+ide_editor_search_bar_replace_all (GSimpleAction *action,
+ GVariant *state,
+ gpointer user_data)
+{
+ IdeEditorSearchBar *self = user_data;
+ g_autofree gchar *unescaped_replace_text = NULL;
+ g_autoptr(GError) error = NULL;
+ const gchar *replace_text;
+ const gchar *search_text;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+ if (self->settings == NULL || self->context == NULL)
+ return;
+
+ search_text = gtk_source_search_settings_get_search_text (self->settings);
+ replace_text = gtk_entry_get_text (GTK_ENTRY (self->replace_entry));
+
+ if (ide_str_empty0 (search_text) || replace_text == NULL)
+ return;
+
+ unescaped_replace_text = gtk_source_utils_unescape_search_text (replace_text);
+ gtk_source_search_context_replace_all (self->context, unescaped_replace_text, -1, &error);
+
+ if (error != NULL)
+ g_warning ("%s", error->message);
+}
+
static gboolean
maybe_escape_regex (GBinding *binding,
const GValue *from_value,
@@ -81,7 +204,7 @@ maybe_escape_regex (GBinding *binding,
const gchar *entry_text = g_value_get_string (from_value);
g_autofree gchar *unescaped = NULL;
- if (!gtk_source_search_settings_get_regex_enabled (self->search_settings))
+ if (!gtk_source_search_settings_get_regex_enabled (self->settings))
entry_text = unescaped = gtk_source_utils_unescape_search_text (entry_text);
g_value_set_string (to_value, entry_text);
@@ -112,7 +235,7 @@ update_replace_actions_sensitivity (IdeEditorSearchBar *self)
{
g_autoptr(GError) regex_error = NULL;
g_autoptr(GError) replace_regex_error = NULL;
- GtkTextBuffer *buffer;
+ GtkSourceBuffer *buffer;
GtkTextIter begin;
GtkTextIter end;
const gchar *search_text;
@@ -125,23 +248,20 @@ update_replace_actions_sensitivity (IdeEditorSearchBar *self)
g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
- if (self->search_context == NULL ||
- self->view == NULL ||
- self->buffer == NULL ||
- self->search_settings == NULL)
+ if (self->context == NULL || self->settings == NULL)
return;
- buffer = GTK_TEXT_BUFFER (self->buffer);
+ buffer = gtk_source_search_context_get_buffer (self->context);
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+ gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
replace_text = gtk_entry_get_text (GTK_ENTRY (self->replace_entry));
/* Gather enough info to determine if Replace or Replace All would make sense */
search_text = gtk_entry_get_text (GTK_ENTRY (self->search_entry));
- pos = gtk_source_search_context_get_occurrence_position (self->search_context, &begin, &end);
- count = gtk_source_search_context_get_occurrences_count (self->search_context);
- regex_error = gtk_source_search_context_get_regex_error (self->search_context);
- replace_regex_valid = gtk_source_search_settings_get_regex_enabled (self->search_settings) ?
+ pos = gtk_source_search_context_get_occurrence_position (self->context, &begin, &end);
+ count = gtk_source_search_context_get_occurrences_count (self->context);
+ regex_error = gtk_source_search_context_get_regex_error (self->context);
+ replace_regex_valid = gtk_source_search_settings_get_regex_enabled (self->settings) ?
g_regex_check_replacement (replace_text, NULL, &replace_regex_error) :
TRUE;
@@ -155,10 +275,10 @@ update_replace_actions_sensitivity (IdeEditorSearchBar *self)
replace_regex_valid &&
count > 0);
- dzl_gtk_widget_action_set (GTK_WIDGET (self), "search-entry", "replace",
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "search-bar", "replace",
"enabled", enable_replace,
NULL);
- dzl_gtk_widget_action_set (GTK_WIDGET (self), "search-entry", "replace-all",
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "search-bar", "replace-all",
"enabled", enable_replace_all,
NULL);
}
@@ -196,7 +316,7 @@ set_position_label (IdeEditorSearchBar *self,
self->search_entry_tag = gd_tagged_entry_tag_new ("");
gd_tagged_entry_add_tag (self->search_entry, self->search_entry_tag);
gd_tagged_entry_tag_set_style (self->search_entry_tag,
- "gb-search-entry-occurrences-tag");
+ "search-occurrences-tag");
}
gd_tagged_entry_tag_set_label (self->search_entry_tag, text);
@@ -207,7 +327,7 @@ update_search_position_label (IdeEditorSearchBar *self)
{
g_autofree gchar *text = NULL;
GtkStyleContext *context;
- GtkTextBuffer *buffer;
+ GtkSourceBuffer *buffer;
GtkTextIter begin;
GtkTextIter end;
const gchar *search_text;
@@ -216,14 +336,14 @@ update_search_position_label (IdeEditorSearchBar *self)
g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
- if (self->buffer == NULL || self->search_context == NULL)
+ if (self->settings == NULL || self->context == NULL)
return;
- buffer = GTK_TEXT_BUFFER (self->buffer);
+ buffer = gtk_source_search_context_get_buffer (self->context);
- gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
- pos = gtk_source_search_context_get_occurrence_position (self->search_context, &begin, &end);
- count = gtk_source_search_context_get_occurrences_count (self->search_context);
+ gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
+ pos = gtk_source_search_context_get_occurrence_position (self->context, &begin, &end);
+ count = gtk_source_search_context_get_occurrences_count (self->context);
if ((pos == -1) || (count == -1))
{
@@ -264,6 +384,26 @@ on_notify_occurrences_count (IdeEditorSearchBar *self,
}
static void
+on_cursor_moved (IdeEditorSearchBar *self,
+ const GtkTextIter *iter,
+ IdeBuffer *buffer)
+{
+ gint count;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (iter != NULL);
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ count = gtk_source_search_context_get_occurrences_count (self->context);
+
+ if (count != -1)
+ {
+ update_search_position_label (self);
+ update_replace_actions_sensitivity (self);
+ }
+}
+
+static void
on_notify_regex_error (IdeEditorSearchBar *self,
GParamSpec *pspec,
GtkSourceSearchContext *search_context)
@@ -307,9 +447,9 @@ check_replace_text (IdeEditorSearchBar *self)
const gchar *tooltip_text = NULL;
g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
- g_assert (self->search_settings != NULL);
+ g_assert (self->settings != NULL);
- if (self->search_context == NULL)
+ if (self->context == NULL)
return;
/*
@@ -317,7 +457,7 @@ check_replace_text (IdeEditorSearchBar *self)
* otherwise remove it. Also set the error message to the tooltip text
* so that the user can get some info on the error.
*/
- if (gtk_source_search_settings_get_regex_enabled (self->search_settings))
+ if (gtk_source_search_settings_get_regex_enabled (self->settings))
{
const gchar *replace_text;
@@ -360,19 +500,68 @@ ide_editor_search_bar_grab_focus (GtkWidget *widget)
}
static void
-ide_editor_search_bar_finalize (GObject *object)
+ide_editor_search_bar_bind_context (IdeEditorSearchBar *self,
+ GtkSourceSearchContext *context,
+ DzlSignalGroup *context_signals)
+{
+ GtkSourceBuffer *buffer;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (context));
+ g_assert (DZL_IS_SIGNAL_GROUP (context_signals));
+
+ buffer = gtk_source_search_context_get_buffer (context);
+ dzl_signal_group_set_target (self->buffer_signals, buffer);
+}
+
+static void
+ide_editor_search_bar_unbind_context (IdeEditorSearchBar *self,
+ DzlSignalGroup *context_signals)
+{
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (DZL_IS_SIGNAL_GROUP (context_signals));
+
+ if (self->buffer_signals != NULL)
+ dzl_signal_group_set_target (self->buffer_signals, NULL);
+}
+
+static void
+ide_editor_search_bar_bind_settings (IdeEditorSearchBar *self,
+ GtkSourceSearchSettings *settings,
+ DzlSignalGroup *settings_signals)
{
- IdeEditorSearchBar *self = (IdeEditorSearchBar *)object;
+ g_autoptr(DzlPropertiesGroup) group = NULL;
+
+ g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+ g_assert (GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
+ g_assert (DZL_IS_SIGNAL_GROUP (settings_signals));
+
+ g_object_bind_property_full (self->search_entry, "text",
+ settings, "search-text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ maybe_escape_regex, pacify_null_text,
+ self, NULL);
- ide_clear_weak_pointer (&self->buffer);
- ide_clear_weak_pointer (&self->view);
+ group = dzl_properties_group_new (G_OBJECT (settings));
+ dzl_properties_group_add_all_properties (group);
+ gtk_widget_insert_action_group (GTK_WIDGET (self),
+ "search-settings",
+ G_ACTION_GROUP (group));
+}
- g_clear_object (&self->search_context);
- g_clear_object (&self->search_settings);
- g_clear_object (&self->search_context_signals);
+static void
+ide_editor_search_bar_destroy (GtkWidget *widget)
+{
+ IdeEditorSearchBar *self = (IdeEditorSearchBar *)widget;
+
+ g_clear_object (&self->buffer_signals);
+ g_clear_object (&self->context);
+ g_clear_object (&self->context_signals);
g_clear_object (&self->search_entry_tag);
+ g_clear_object (&self->settings);
+ g_clear_object (&self->settings_signals);
- G_OBJECT_CLASS (ide_editor_search_bar_parent_class)->finalize (object);
+ GTK_WIDGET_CLASS (ide_editor_search_bar_parent_class)->destroy (widget);
}
static void
@@ -385,12 +574,12 @@ ide_editor_search_bar_get_property (GObject *object,
switch (prop_id)
{
- case PROP_BUFFER:
- g_value_set_object (value, ide_editor_search_bar_get_buffer (self));
+ case PROP_CONTEXT:
+ g_value_set_object (value, self->context);
break;
- case PROP_VIEW:
- g_value_set_object (value, ide_editor_search_bar_get_view (self));
+ case PROP_SETTINGS:
+ g_value_set_object (value, self->settings);
break;
default:
@@ -408,12 +597,12 @@ ide_editor_search_bar_set_property (GObject *object,
switch (prop_id)
{
- case PROP_BUFFER:
- ide_editor_search_bar_set_buffer (self, g_value_get_object (value));
+ case PROP_CONTEXT:
+ ide_editor_search_bar_set_context (self, g_value_get_object (value));
break;
- case PROP_VIEW:
- ide_editor_search_bar_set_view (self, g_value_get_object (value));
+ case PROP_SETTINGS:
+ ide_editor_search_bar_set_settings (self, g_value_get_object (value));
break;
default:
@@ -427,14 +616,29 @@ ide_editor_search_bar_class_init (IdeEditorSearchBarClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- object_class->finalize = ide_editor_search_bar_finalize;
object_class->get_property = ide_editor_search_bar_get_property;
object_class->set_property = ide_editor_search_bar_set_property;
+ widget_class->destroy = ide_editor_search_bar_destroy;
widget_class->grab_focus = ide_editor_search_bar_grab_focus;
- gtk_widget_class_set_template_from_resource (widget_class,
- "/org/gnome/builder/ui/ide-editor-search-bar.ui");
+ properties [PROP_CONTEXT] =
+ g_param_spec_object ("context",
+ "Context",
+ "The search context for locating matches",
+ GTK_SOURCE_TYPE_SEARCH_CONTEXT,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SETTINGS] =
+ g_param_spec_object ("settings",
+ "Settings",
+ "The search settings for locating matches",
+ GTK_SOURCE_TYPE_SEARCH_SETTINGS,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-editor-search-bar.ui");
gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, case_sensitive);
gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, close_button);
gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_all_button);
@@ -450,63 +654,72 @@ ide_editor_search_bar_class_init (IdeEditorSearchBarClass *klass)
g_type_ensure (GD_TYPE_TAGGED_ENTRY);
}
+static const GActionEntry search_bar_actions[] = {
+ { "toggle-search-options", NULL, "b", "false", ide_editor_search_bar_toggle_search_options },
+ { "toggle-search-replace", NULL, "b", "false", ide_editor_search_bar_toggle_search_replace },
+ { "replace", ide_editor_search_bar_replace },
+ { "replace-all", ide_editor_search_bar_replace_all },
+};
+
static void
ide_editor_search_bar_init (IdeEditorSearchBar *self)
{
- g_autoptr(GSimpleActionGroup) group = NULL;
- static const gchar *proxy_names[] = {
- "case-sensitive",
- "at-word-boundaries",
- "regex-enabled",
- "wrap-around",
- };
+ g_autoptr(GSimpleActionGroup) actions = NULL;
gtk_widget_init_template (GTK_WIDGET (self));
- self->search_settings = gtk_source_search_settings_new ();
+ self->buffer_signals = dzl_signal_group_new (IDE_TYPE_BUFFER);
- g_object_bind_property_full (self->search_entry, "text",
- self->search_settings, "search-text",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
- maybe_escape_regex, pacify_null_text,
- self, NULL);
+ dzl_signal_group_connect_swapped (self->buffer_signals,
+ "cursor-moved",
+ G_CALLBACK (on_cursor_moved),
+ self);
- self->search_context_signals = dzl_signal_group_new (GTK_SOURCE_TYPE_SEARCH_CONTEXT);
+ self->context_signals = dzl_signal_group_new (GTK_SOURCE_TYPE_SEARCH_CONTEXT);
- dzl_signal_group_connect_swapped (self->search_context_signals,
+ dzl_signal_group_connect_swapped (self->context_signals,
"notify::occurrences-count",
G_CALLBACK (on_notify_occurrences_count),
self);
- dzl_signal_group_connect_swapped (self->search_context_signals,
+ dzl_signal_group_connect_swapped (self->context_signals,
"notify::regex-error",
G_CALLBACK (on_notify_regex_error),
self);
- g_signal_connect_object (self->search_settings,
- "notify::search-text",
- G_CALLBACK (on_notify_search_text),
- self,
- G_CONNECT_SWAPPED);
+ g_signal_connect_swapped (self->context_signals,
+ "bind",
+ G_CALLBACK (ide_editor_search_bar_bind_context),
+ self);
- g_signal_connect_object (self->search_settings,
- "notify::regex-enabled",
- G_CALLBACK (on_notify_regex_enabled),
- self,
- G_CONNECT_SWAPPED);
+ g_signal_connect_swapped (self->context_signals,
+ "unbind",
+ G_CALLBACK (ide_editor_search_bar_unbind_context),
+ self);
- group = g_simple_action_group_new ();
+ self->settings_signals = dzl_signal_group_new (GTK_SOURCE_TYPE_SEARCH_SETTINGS);
- for (guint i = 0; i < G_N_ELEMENTS (proxy_names); i++)
- {
- g_autoptr(GPropertyAction) action = NULL;
- const gchar *name = proxy_names[i];
+ dzl_signal_group_connect_swapped (self->settings_signals,
+ "notify::search-text",
+ G_CALLBACK (on_notify_search_text),
+ self);
- action = g_property_action_new (name, self->search_settings, name);
- g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
- }
+ dzl_signal_group_connect_swapped (self->settings_signals,
+ "notify::regex-enabled",
+ G_CALLBACK (on_notify_regex_enabled),
+ self);
- gtk_widget_insert_action_group (GTK_WIDGET (self), "search-entry", G_ACTION_GROUP (group));
+ g_signal_connect_swapped (self->settings_signals,
+ "bind",
+ G_CALLBACK (ide_editor_search_bar_bind_settings),
+ self);
+
+ actions = g_simple_action_group_new ();
+ g_action_map_add_action_entries (G_ACTION_MAP (actions),
+ search_bar_actions,
+ G_N_ELEMENTS (search_bar_actions),
+ self);
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "search-bar", G_ACTION_GROUP (actions));
}
GtkWidget *
@@ -515,88 +728,30 @@ ide_editor_search_bar_new (void)
return g_object_new (IDE_TYPE_EDITOR_SEARCH_BAR, NULL);
}
-/**
- * ide_editor_search_bar_get_buffer:
- * @self: a #IdeEditorSearchBar
- *
- * Gets the buffer used by the search bar.
- *
- * Returns: (nullable) (transfer none): An #IdeBuffer or %NULL
- *
- * Since: 3.26
- */
-IdeBuffer *
-ide_editor_search_bar_get_buffer (IdeEditorSearchBar *self)
-{
- g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), NULL);
-
- return self->buffer;
-}
-
-/**
- * ide_editor_search_bar_set_buffer:
- * @self: a #IdeEditorSearchBar
- *
- * Sets the buffer used by the search bar.
- *
- * Since: 3.26
- */
void
-ide_editor_search_bar_set_buffer (IdeEditorSearchBar *self,
- IdeBuffer *buffer)
+ide_editor_search_bar_set_settings (IdeEditorSearchBar *self,
+ GtkSourceSearchSettings *settings)
{
g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
- g_return_if_fail (!buffer || IDE_IS_BUFFER (buffer));
+ g_return_if_fail (!settings || GTK_SOURCE_IS_SEARCH_SETTINGS (settings));
- if (ide_set_weak_pointer (&self->buffer, buffer))
+ if (g_set_object (&self->settings, settings))
{
- g_clear_object (&self->search_context);
- dzl_signal_group_set_target (self->search_context_signals, NULL);
-
- if (buffer != NULL)
- {
- self->search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (buffer),
- self->search_settings);
- dzl_signal_group_set_target (self->search_context_signals, self->search_context);
- }
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUFFER]);
+ dzl_signal_group_set_target (self->settings_signals, settings);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SETTINGS]);
}
}
-/**
- * ide_editor_search_bar_get_view:
- * @self: a #IdeEditorSearchBar
- *
- * Gets the view used by the search bar.
- *
- * Returns: (nullable) (transfer none): An #IdeBuffer or %NULL
- *
- * Since: 3.26
- */
-IdeSourceView *
-ide_editor_search_bar_get_view (IdeEditorSearchBar *self)
-{
- g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), NULL);
-
- return self->view;
-}
-
-/**
- * ide_editor_search_bar_set_view:
- * @self: a #IdeEditorSearchBar
- *
- * Sets the view used by the search bar.
- *
- * Since: 3.26
- */
void
-ide_editor_search_bar_set_view (IdeEditorSearchBar *self,
- IdeSourceView *view)
+ide_editor_search_bar_set_context (IdeEditorSearchBar *self,
+ GtkSourceSearchContext *context)
{
g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
- g_return_if_fail (!view || IDE_IS_SOURCE_VIEW (view));
+ g_return_if_fail (!context || GTK_SOURCE_IS_SEARCH_CONTEXT (context));
- if (ide_set_weak_pointer (&self->view, view))
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VIEW]);
+ if (g_set_object (&self->context, context))
+ {
+ dzl_signal_group_set_target (self->context_signals, context);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONTEXT]);
+ }
}
diff --git a/libide/editor/ide-editor-search-bar.h b/libide/editor/ide-editor-search-bar.h
index 6dd67ad..e727cd3 100644
--- a/libide/editor/ide-editor-search-bar.h
+++ b/libide/editor/ide-editor-search-bar.h
@@ -29,12 +29,12 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (IdeEditorSearchBar, ide_editor_search_bar, IDE, EDITOR_SEARCH_BAR, DzlBin)
-GtkWidget *ide_editor_search_bar_new (void);
-IdeBuffer *ide_editor_search_bar_get_buffer (IdeEditorSearchBar *self);
-void ide_editor_search_bar_set_buffer (IdeEditorSearchBar *self,
- IdeBuffer *buffer);
-IdeSourceView *ide_editor_search_bar_get_view (IdeEditorSearchBar *self);
-void ide_editor_search_bar_set_view (IdeEditorSearchBar *self,
- IdeSourceView *view);
+GtkWidget *ide_editor_search_bar_new (void);
+void ide_editor_search_bar_set_replace_mode (IdeEditorSearchBar *self,
+ gboolean replace_mode);
+void ide_editor_search_bar_set_context (IdeEditorSearchBar *self,
+ GtkSourceSearchContext *context);
+void ide_editor_search_bar_set_settings (IdeEditorSearchBar *self,
+ GtkSourceSearchSettings *settings);
G_END_DECLS
diff --git a/libide/editor/ide-editor-search-bar.ui b/libide/editor/ide-editor-search-bar.ui
index 464fc73..cca5f73 100644
--- a/libide/editor/ide-editor-search-bar.ui
+++ b/libide/editor/ide-editor-search-bar.ui
@@ -57,7 +57,7 @@
</style>
<child>
<object class="GtkButton">
- <property name="action-name">frame.previous-search-result</property>
+ <property name="action-name">editor-view.move-previous-search-result</property>
<property name="visible">true</property>
<property name="can_focus">false</property>
<property name="receives_default">true</property>
@@ -78,7 +78,7 @@
</child>
<child>
<object class="GtkButton">
- <property name="action-name">frame.next-search-result</property>
+ <property name="action-name">editor-view.move-next-search-result</property>
<property name="visible">true</property>
<property name="can_focus">false</property>
<property name="receives_default">true</property>
@@ -106,7 +106,7 @@
<child>
<object class="GtkButton" id="replace_button">
<property name="label" translatable="yes">Replace</property>
- <property name="action-name">search-entry.replace</property>
+ <property name="action-name">search-bar.replace</property>
<property name="visible">false</property>
<property name="can_focus">true</property>
<property name="receives_default">true</property>
@@ -119,7 +119,7 @@
<child>
<object class="GtkButton" id="replace_all_button">
<property name="label" translatable="yes">Replace All</property>
- <property name="action-name">search-entry.replace-all</property>
+ <property name="action-name">search-bar.replace-all</property>
<property name="visible">false</property>
<property name="can_focus">true</property>
<property name="receives_default">true</property>
@@ -138,7 +138,7 @@
<property name="spacing">8</property>
<child>
<object class="GtkToggleButton">
- <property name="action-name">search-entry.toggle-search-replace</property>
+ <property name="action-name">search-bar.toggle-search-replace</property>
<property name="action-target">true</property>
<property name="tooltip-text" translatable="yes">Switch between Search and
Search-and-Replace</property>
<property name="visible">true</property>
@@ -161,7 +161,7 @@
</child>
<child>
<object class="GtkToggleButton">
- <property name="action-name">search-entry.toggle-search-options</property>
+ <property name="action-name">search-bar.toggle-search-options</property>
<property name="action-target">true</property>
<property name="tooltip-text" translatable="yes">Show or hide search options such as
case sensitivity</property>
<property name="visible">true</property>
@@ -223,7 +223,7 @@
<property name="column_spacing">8</property>
<child>
<object class="GtkCheckButton" id="use_regex">
- <property name="action-name">search-entry.regex-enabled</property>
+ <property name="action-name">search-settings.regex-enabled</property>
<property name="label" translatable="yes">Regular expressions</property>
<property name="visible">true</property>
<property name="can_focus">false</property>
@@ -238,7 +238,7 @@
</child>
<child>
<object class="GtkCheckButton" id="case_sensitive">
- <property name="action-name">search-entry.case-sensitive</property>
+ <property name="action-name">search-settings.case-sensitive</property>
<property name="label" translatable="yes">Case sensitive</property>
<property name="visible">true</property>
<property name="can_focus">false</property>
@@ -253,7 +253,7 @@
</child>
<child>
<object class="GtkCheckButton" id="whole_word">
- <property name="action-name">search-entry.at-word-boundaries</property>
+ <property name="action-name">search-settings.at-word-boundaries</property>
<property name="label" translatable="yes">Match whole word only</property>
<property name="visible">true</property>
<property name="can_focus">false</property>
diff --git a/libide/editor/ide-editor-view-actions.c b/libide/editor/ide-editor-view-actions.c
index 880cf7c..a8e915b 100644
--- a/libide/editor/ide-editor-view-actions.c
+++ b/libide/editor/ide-editor-view-actions.c
@@ -388,7 +388,22 @@ ide_editor_view_actions_save_as (GSimpleAction *action,
}
static void
-ide_editor_view_actions_focus_search (GSimpleAction *action,
+ide_editor_view_actions_find (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeEditorView *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_EDITOR_VIEW (self));
+
+ ide_editor_search_bar_set_replace_mode (self->search_bar, FALSE);
+ gtk_revealer_set_reveal_child (self->search_revealer, TRUE);
+ gtk_widget_grab_focus (GTK_WIDGET (self->search_bar));
+}
+
+static void
+ide_editor_view_actions_find_replace (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
@@ -397,6 +412,7 @@ ide_editor_view_actions_focus_search (GSimpleAction *action,
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (IDE_IS_EDITOR_VIEW (self));
+ ide_editor_search_bar_set_replace_mode (self->search_bar, TRUE);
gtk_revealer_set_reveal_child (self->search_revealer, TRUE);
gtk_widget_grab_focus (GTK_WIDGET (self->search_bar));
}
@@ -435,9 +451,46 @@ ide_editor_view_actions_notify_file_settings (IdeEditorView *self,
g_object_set (group, "object", file_settings, NULL);
}
+static void
+ide_editor_view_actions_move_next_error (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ ide_editor_view_move_next_error (user_data);
+}
+
+static void
+ide_editor_view_actions_move_previous_error (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ ide_editor_view_move_previous_error (user_data);
+}
+
+static void
+ide_editor_view_actions_move_next_search_result (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ ide_editor_view_move_next_search_result (user_data);
+}
+
+static void
+ide_editor_view_actions_move_previous_search_result (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ ide_editor_view_move_previous_search_result (user_data);
+}
+
static const GActionEntry editor_view_entries[] = {
- { "focus-search", ide_editor_view_actions_focus_search },
+ { "find", ide_editor_view_actions_find },
+ { "find-replace", ide_editor_view_actions_find_replace },
{ "hide-search", ide_editor_view_actions_hide_search },
+ { "move-next-error", ide_editor_view_actions_move_next_error },
+ { "move-next-search-result", ide_editor_view_actions_move_next_search_result },
+ { "move-previous-error", ide_editor_view_actions_move_previous_error },
+ { "move-previous-search-result", ide_editor_view_actions_move_previous_search_result },
{ "print", ide_editor_view_actions_print },
{ "reload", ide_editor_view_actions_reload },
{ "save", ide_editor_view_actions_save },
@@ -502,3 +555,10 @@ _ide_editor_view_init_actions (IdeEditorView *self)
gtk_widget_insert_action_group (GTK_WIDGET (self), "file-settings", G_ACTION_GROUP (file_props));
ide_editor_view_actions_notify_file_settings (self, NULL, source_view);
}
+
+void
+_ide_editor_view_update_actions (IdeEditorView *self)
+{
+ g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+
+}
diff --git a/libide/editor/ide-editor-view.c b/libide/editor/ide-editor-view.c
index 1f5e7ed..ad8210d 100644
--- a/libide/editor/ide-editor-view.c
+++ b/libide/editor/ide-editor-view.c
@@ -384,12 +384,21 @@ ide_editor_view_hierarchy_changed (GtkWidget *widget,
g_assert (IDE_IS_EDITOR_VIEW (self));
g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
- /* Make sure we chain up if things change in the future */
+ /*
+ * We don't need to chain up today, but if IdeLayoutView starts
+ * using the hierarchy_changed signal to handle anything, we want
+ * to make sure we aren't surprised.
+ */
if (GTK_WIDGET_CLASS (ide_editor_view_parent_class)->hierarchy_changed)
GTK_WIDGET_CLASS (ide_editor_view_parent_class)->hierarchy_changed (widget, old_toplevel);
context = ide_widget_get_context (GTK_WIDGET (self));
+ /*
+ * We don't want to create addins until the widget has been placed into
+ * the widget tree. That way the addins can get access to the context
+ * or other useful details.
+ */
if (context != NULL && self->addins == NULL)
{
self->addins = ide_extension_set_adapter_new (context,
@@ -442,6 +451,52 @@ ide_editor_view_update_map (IdeEditorView *self)
}
static void
+search_revealer_notify_reveal_child (IdeEditorView *self,
+ GParamSpec *pspec,
+ GtkRevealer *revealer)
+{
+ GtkSourceCompletion *completion;
+
+ g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+ g_return_if_fail (pspec != NULL);
+ g_return_if_fail (GTK_IS_REVEALER (revealer));
+
+ completion = gtk_source_view_get_completion (GTK_SOURCE_VIEW (self->source_view));
+
+ if (!gtk_revealer_get_reveal_child (revealer))
+ {
+ /*
+ * Cancel any pending work by the context and release it. We don't need
+ * to hold onto these when they aren't being used because they handle
+ * buffer signals and other extraneous operations.
+ */
+ ide_editor_search_bar_set_context (self->search_bar, NULL);
+ g_clear_object (&self->search_context);
+
+ /* Restore completion that we blocked below. */
+ gtk_source_completion_unblock_interactive (completion);
+ }
+ else
+ {
+ g_assert (self->search_context == NULL);
+
+ self->search_context = g_object_new (GTK_SOURCE_TYPE_SEARCH_CONTEXT,
+ "buffer", self->buffer,
+ "highlight", TRUE,
+ "settings", self->search_settings,
+ NULL);
+ ide_editor_search_bar_set_context (self->search_bar, self->search_context);
+
+ /*
+ * Block the completion while the search bar is set. It only
+ * slows things down like search/replace functionality. We'll
+ * restore it above when we clear state.
+ */
+ gtk_source_completion_block_interactive (completion);
+ }
+}
+
+static void
ide_editor_view_constructed (GObject *object)
{
IdeEditorView *self = (IdeEditorView *)object;
@@ -479,6 +534,18 @@ ide_editor_view_constructed (GObject *object)
G_CALLBACK (ide_editor_view_source_view_event),
self);
+ /*
+ * We want to track when the search revealer is visible. We will discard
+ * the search context when the revealer is not visible so that we don't
+ * continue performing expensive buffer operations.
+ */
+ g_signal_connect_swapped (self->search_revealer,
+ "notify::reveal-child",
+ G_CALLBACK (search_revealer_notify_reveal_child),
+ self);
+
+ ide_editor_search_bar_set_settings (self->search_bar, self->search_settings);
+
ide_editor_view_load_fonts (self);
ide_editor_view_update_map (self);
}
@@ -496,6 +563,11 @@ ide_editor_view_destroy (GtkWidget *widget)
g_clear_object (&self->addins);
+ g_cancellable_cancel (self->destroy_cancellable);
+ g_clear_object (&self->destroy_cancellable);
+
+ g_clear_object (&self->search_settings);
+ g_clear_object (&self->search_context);
g_clear_object (&self->editor_settings);
g_clear_object (&self->insight_settings);
@@ -657,6 +729,8 @@ ide_editor_view_init (IdeEditorView *self)
ide_layout_view_set_can_split (IDE_LAYOUT_VIEW (self), TRUE);
ide_layout_view_set_menu_id (IDE_LAYOUT_VIEW (self), "ide-editor-view-document-menu");
+ self->destroy_cancellable = g_cancellable_new ();
+
/* Setup signals to monitor on the buffer. */
self->buffer_signals = dzl_signal_group_new (IDE_TYPE_BUFFER);
@@ -685,6 +759,23 @@ ide_editor_view_init (IdeEditorView *self)
G_CALLBACK (ide_editor_view_bind_signals),
self);
+ /*
+ * Setup our search context. The sourceview has it's own search
+ * infrastructure that we want to reserve for use by vim keybindings
+ * and other transient keybinding features. Instead, we have our own
+ * that can have separate state from those.
+ *
+ * We try to avoid creating/maintaining the search-context except
+ * when necessary because has some expensive operations associated
+ * with it's handling of changes to the underlying buffer.
+ */
+ self->search_settings = g_object_new (GTK_SOURCE_TYPE_SEARCH_SETTINGS,
+ "at-word-boundaries", FALSE,
+ "case-sensitive", FALSE,
+ "wrap-around", TRUE,
+ NULL);
+
+
/* Setup bindings for the buffer. */
self->buffer_bindings = dzl_binding_group_new ();
dzl_binding_group_bind (self->buffer_bindings, "title", self, "title", 0);
@@ -906,3 +997,149 @@ ide_editor_view_get_language (IdeEditorView *self)
return gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (self->buffer));
}
+
+/**
+ * ide_editor_view_move_next_error:
+ * @self: a #IdeEditorView
+ *
+ * Moves to the next error, if any.
+ *
+ * If there is no error, the insertion cursor is not moved.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_view_move_next_error (IdeEditorView *self)
+{
+ g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+
+ g_signal_emit_by_name (self->source_view, "move-error", GTK_DIR_DOWN);
+}
+
+/**
+ * ide_editor_view_move_previous_error:
+ * @self: a #IdeEditorView
+ *
+ * Moves the insertion cursor to the previous error.
+ *
+ * If there is no error, the insertion cursor is not moved.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_view_move_previous_error (IdeEditorView *self)
+{
+ g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+
+ g_signal_emit_by_name (self->source_view, "move-error", GTK_DIR_UP);
+}
+
+static void
+ide_editor_view_move_next_search_result_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkSourceSearchContext *context = (GtkSourceSearchContext *)object;
+ g_autoptr(IdeEditorView) self = user_data;
+ g_autoptr(GError) error = NULL;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gboolean has_wrapped = FALSE;
+
+ g_assert (IDE_IS_EDITOR_VIEW (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (self->buffer == NULL)
+ return;
+
+ if (gtk_source_search_context_forward_finish2 (context, result, &begin, &end, &has_wrapped, &error))
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (self->buffer), &begin, &end);
+}
+
+/**
+ * ide_editor_view_move_next_search_result:
+ * @self: a #IdeEditorView
+ *
+ * Moves the insertion cursor to the next search result.
+ *
+ * If there is no search result, the insertion cursor is not moved.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_view_move_next_search_result (IdeEditorView *self)
+{
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+ g_return_if_fail (self->destroy_cancellable != NULL);
+ g_return_if_fail (self->buffer != NULL);
+
+ if (self->search_context == NULL)
+ return;
+
+ if (gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (self->buffer), &begin, &end))
+ gtk_text_iter_order (&begin, &end);
+
+ gtk_source_search_context_forward_async (self->search_context,
+ &end,
+ self->destroy_cancellable,
+ ide_editor_view_move_next_search_result_cb,
+ g_object_ref (self));
+}
+
+static void
+ide_editor_view_move_previous_search_result_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkSourceSearchContext *context = (GtkSourceSearchContext *)object;
+ g_autoptr(IdeEditorView) self = user_data;
+ g_autoptr(GError) error = NULL;
+ GtkTextIter begin;
+ GtkTextIter end;
+ gboolean has_wrapped = FALSE;
+
+ g_assert (IDE_IS_EDITOR_VIEW (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (self->buffer == NULL)
+ return;
+
+ if (gtk_source_search_context_backward_finish2 (context, result, &begin, &end, &has_wrapped, &error))
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (self->buffer), &begin, &end);
+}
+
+/**
+ * ide_editor_view_move_previous_search_result:
+ * @self: a #IdeEditorView
+ *
+ * Moves the insertion cursor to the previous search result.
+ *
+ * If there is no search result, the insertion cursor is not moved.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_view_move_previous_search_result (IdeEditorView *self)
+{
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_return_if_fail (IDE_IS_EDITOR_VIEW (self));
+ g_return_if_fail (self->destroy_cancellable != NULL);
+ g_return_if_fail (self->buffer != NULL);
+
+ if (self->search_context == NULL)
+ return;
+
+ if (gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (self->buffer), &begin, &end))
+ gtk_text_iter_order (&begin, &end);
+
+ gtk_source_search_context_backward_async (self->search_context,
+ &begin,
+ self->destroy_cancellable,
+ ide_editor_view_move_previous_search_result_cb,
+ g_object_ref (self));
+}
diff --git a/libide/editor/ide-editor-view.h b/libide/editor/ide-editor-view.h
index 443c322..1a36b49 100644
--- a/libide/editor/ide-editor-view.h
+++ b/libide/editor/ide-editor-view.h
@@ -30,19 +30,23 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (IdeEditorView, ide_editor_view, IDE, EDITOR_VIEW, IdeLayoutView)
-IdeBuffer *ide_editor_view_get_buffer (IdeEditorView *self);
-IdeSourceView *ide_editor_view_get_view (IdeEditorView *self);
-const gchar *ide_editor_view_get_language_id (IdeEditorView *self);
-void ide_editor_view_scroll_to_line (IdeEditorView *self,
- guint line);
-gboolean ide_editor_view_get_auto_hide_map (IdeEditorView *self);
-void ide_editor_view_set_auto_hide_map (IdeEditorView *self,
- gboolean auto_hide_map);
-gboolean ide_editor_view_get_show_map (IdeEditorView *self);
-void ide_editor_view_set_show_map (IdeEditorView *self,
- gboolean show_map);
-GtkSourceLanguage *ide_editor_view_get_language (IdeEditorView *self);
-void ide_editor_view_set_language (IdeEditorView *self,
- GtkSourceLanguage *language);
+IdeBuffer *ide_editor_view_get_buffer (IdeEditorView *self);
+IdeSourceView *ide_editor_view_get_view (IdeEditorView *self);
+const gchar *ide_editor_view_get_language_id (IdeEditorView *self);
+void ide_editor_view_scroll_to_line (IdeEditorView *self,
+ guint line);
+gboolean ide_editor_view_get_auto_hide_map (IdeEditorView *self);
+void ide_editor_view_set_auto_hide_map (IdeEditorView *self,
+ gboolean auto_hide_map);
+gboolean ide_editor_view_get_show_map (IdeEditorView *self);
+void ide_editor_view_set_show_map (IdeEditorView *self,
+ gboolean show_map);
+GtkSourceLanguage *ide_editor_view_get_language (IdeEditorView *self);
+void ide_editor_view_set_language (IdeEditorView *self,
+ GtkSourceLanguage *language);
+void ide_editor_view_move_next_error (IdeEditorView *self);
+void ide_editor_view_move_previous_error (IdeEditorView *self);
+void ide_editor_view_move_next_search_result (IdeEditorView *self);
+void ide_editor_view_move_previous_search_result (IdeEditorView *self);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]