diff --git a/src/Widgets/SearchBar.vala b/src/Widgets/SearchBar.vala index 54efbf8f30..da381ccedc 100644 --- a/src/Widgets/SearchBar.vala +++ b/src/Widgets/SearchBar.vala @@ -1,23 +1,9 @@ /* - * Copyright (C) 2011-2012 Lucas Baudin - * 2013 Mario Guerriero - 2014-2026 elementary, Inc. (https://elementary.io) - * - * This file is part of Code. - * - * Code 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. - * - * Code 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 . - */ +* SPDX-License-Identifier: GPL-3.0-or-later +* SPDX-FileCopyrightText: 2014-2026 elementary, Inc. (https://elementary.io) +* 2013 Mario Guerriero +* 2011-2012 Lucas Baudin +*/ public enum Scratch.CaseSensitiveMode { NEVER, @@ -25,662 +11,627 @@ public enum Scratch.CaseSensitiveMode { ALWAYS } -namespace Scratch.Widgets { - public class SearchBar : Gtk.Box { //TODO In Gtk4 use a BinLayout Widget - - public weak MainWindow window { get; construct; } - - - private Gtk.Button tool_arrow_up; - private Gtk.Button tool_arrow_down; - - /** - * Is the search cyclic? e.g., when you are at the bottom, if you press - * "Down", it will go at the start of the file to search for the content - * of the search entry. - **/ - private Granite.SwitchModelButton cycle_search_button ; - private Gtk.ComboBoxText case_sensitive_search_button; - private Granite.SwitchModelButton regex_search_button; - private Granite.SwitchModelButton whole_word_search_button; - private Gtk.SearchEntry search_entry; - private Gtk.SearchEntry replace_entry; - private Gtk.Label search_occurence_count_label; - private Gtk.Button replace_tool_button; - private Gtk.Button replace_all_tool_button; - private Scratch.Widgets.SourceView? text_view = null; - private Gtk.TextBuffer? text_buffer = null; - private Gtk.SourceSearchContext? search_context; - private uint update_search_label_timeout_id = 0; - private Gtk.Revealer revealer; - private Gtk.EventControllerKey key_controller; - - public bool is_focused { - get { - return search_is_focused || replace_is_focused; - } +public class Scratch.Widgets.SearchBar : Gtk.Box { //TODO In Gtk4 use a BinLayout Widget + + public weak MainWindow window { get; construct; } + + public bool is_focused { + get { + return search_is_focused || replace_is_focused; } + } - public bool search_is_focused { - get { - return search_entry.has_focus; - } + public bool search_is_focused { + get { + return search_entry.has_focus; } + } - public bool replace_is_focused { - get { - return replace_entry.has_focus; - } + public bool replace_is_focused { + get { + return replace_entry.has_focus; } + } - public bool is_revealed { - get { - return revealer.child_revealed; - } + public bool is_revealed { + get { + return revealer.child_revealed; } + } - public string entry_text { - get { - return search_entry.text; - } + public string entry_text { + get { + return search_entry.text; } + } - public bool has_search_term { - get { - return search_entry.text != ""; - } + public bool has_search_term { + get { + return search_entry.text != ""; } + } - public uint search_occurrences { - get { - if (search_context == null || - search_context.settings.search_text == "") { + public uint search_occurrences { + get { + if (search_context == null || + search_context.settings.search_text == "") { - return 0; - } else { - return search_context.get_occurrences_count (); - } + return 0; + } else { + return search_context.get_occurrences_count (); } } + } - public uint transition_time_msec { - get { - return revealer.transition_duration + 10; - } + public uint transition_time_msec { + get { + return revealer.transition_duration + 10; } + } - public SearchBar (MainWindow window) { - Object (window: window); - } + private Gtk.Button tool_arrow_up; + private Gtk.Button tool_arrow_down; + + /** + * Is the search cyclic? e.g., when you are at the bottom, if you press + * "Down", it will go at the start of the file to search for the content + * of the search entry. + **/ + private Granite.SwitchModelButton cycle_search_button ; + private Gtk.ComboBoxText case_sensitive_search_button; + private Granite.SwitchModelButton regex_search_button; + private Granite.SwitchModelButton whole_word_search_button; + private Gtk.SearchEntry search_entry; + private Gtk.SearchEntry replace_entry; + private Gtk.Label search_occurence_count_label; + private Gtk.Button replace_tool_button; + private Gtk.Button replace_all_tool_button; + private Scratch.Widgets.SourceView? text_view = null; + private Gtk.TextBuffer? text_buffer = null; + private Gtk.SourceSearchContext? search_context; + private uint update_search_label_timeout_id = 0; + private Gtk.Revealer revealer; + private Gtk.EventControllerKey key_controller; + + public SearchBar (MainWindow window) { + Object (window: window); + } - construct { - this.orientation = HORIZONTAL; - search_entry = new Gtk.SearchEntry () { - hexpand = true, - placeholder_text = _("Find") - }; + construct { + this.orientation = HORIZONTAL; + search_entry = new Gtk.SearchEntry () { + hexpand = true, + placeholder_text = _("Find") + }; - search_occurence_count_label = new Gtk.Label (_("No Results")); - search_occurence_count_label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); + search_occurence_count_label = new Gtk.Label (_("No Results")); + search_occurence_count_label.get_style_context ().add_class (Granite.STYLE_CLASS_SMALL_LABEL); - var app_instance = (Scratch.Application) GLib.Application.get_default (); + var app_instance = (Scratch.Application) GLib.Application.get_default (); - tool_arrow_down = new Gtk.Button.from_icon_name ("go-down-symbolic", Gtk.IconSize.SMALL_TOOLBAR); - tool_arrow_down.clicked.connect (search_next); - tool_arrow_down.sensitive = false; - tool_arrow_down.tooltip_markup = Granite.markup_accel_tooltip ( + tool_arrow_down = new Gtk.Button.from_icon_name ("go-down-symbolic", SMALL_TOOLBAR) { + sensitive = false, + tooltip_markup = Granite.markup_accel_tooltip ( app_instance.get_accels_for_action ( - Scratch.MainWindow.ACTION_PREFIX + Scratch.MainWindow.ACTION_FIND_NEXT + MainWindow.ACTION_PREFIX + MainWindow.ACTION_FIND_NEXT ), _("Search next") - ); + ) + }; + tool_arrow_down.clicked.connect (search_next); - tool_arrow_up = new Gtk.Button.from_icon_name ("go-up-symbolic", Gtk.IconSize.SMALL_TOOLBAR); - tool_arrow_up.clicked.connect (search_previous); - tool_arrow_up.sensitive = false; - tool_arrow_up.tooltip_markup = Granite.markup_accel_tooltip ( + tool_arrow_up = new Gtk.Button.from_icon_name ("go-up-symbolic", SMALL_TOOLBAR) { + sensitive = false, + tooltip_markup = Granite.markup_accel_tooltip ( app_instance.get_accels_for_action ( - Scratch.MainWindow.ACTION_PREFIX + Scratch.MainWindow.ACTION_FIND_PREVIOUS + MainWindow.ACTION_PREFIX + MainWindow.ACTION_FIND_PREVIOUS ), _("Search previous") - ); - - cycle_search_button = new Granite.SwitchModelButton (_("Cyclic Search")); - - case_sensitive_search_button = new Gtk.ComboBoxText (); - case_sensitive_search_button.append ("never", _("Never")); - case_sensitive_search_button.append ("mixed", _("Mixed Case")); - case_sensitive_search_button.append ("always", _("Always")); - case_sensitive_search_button.active = 1; - - var case_sensitive_search_label = new Gtk.Label (_("Case Sensitive")); - - var case_sensitive_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12); - case_sensitive_box.add (case_sensitive_search_label); - case_sensitive_box.add (case_sensitive_search_button); - case_sensitive_box.get_style_context ().add_class (Gtk.STYLE_CLASS_MENUITEM); - - regex_search_button = new Granite.SwitchModelButton (_("Use Regular Expressions")); - whole_word_search_button = new Granite.SwitchModelButton (_("Match Whole Words")); - - var search_option_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { - margin_top = 3, - margin_bottom = 3 - }; - search_option_box.add (cycle_search_button); - search_option_box.add (case_sensitive_box); - search_option_box.add (whole_word_search_button); - search_option_box.add (regex_search_button); - - var search_popover = new Gtk.Popover (null) { - child = search_option_box - }; - search_popover.show_all (); - - var search_buttonbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); - search_buttonbox.add (search_occurence_count_label); - search_buttonbox.add (new Gtk.Image.from_icon_name ("pan-down-symbolic", Gtk.IconSize.SMALL_TOOLBAR)); - - var search_menubutton = new Gtk.MenuButton () { - popover = search_popover, - tooltip_text = _("Search Options") - }; - search_menubutton.add (search_buttonbox); - - cycle_search_button.toggled.connect (on_search_parameters_changed); - case_sensitive_search_button.changed.connect (on_search_parameters_changed); - whole_word_search_button.toggled.connect (on_search_parameters_changed); - regex_search_button.toggled.connect (on_search_parameters_changed); - - Scratch.settings.bind ("cyclic-search", cycle_search_button, "active", SettingsBindFlags.DEFAULT); - Scratch.settings.bind ("wholeword-search", whole_word_search_button, "active", SettingsBindFlags.DEFAULT); - Scratch.settings.bind ("case-sensitive-search", case_sensitive_search_button, "active-id", SettingsBindFlags.DEFAULT); - Scratch.settings.bind ("regex-search", regex_search_button, "active", SettingsBindFlags.DEFAULT); - // These settings are ignored when regex searching - regex_search_button.bind_property ("active", cycle_search_button, "sensitive", SYNC_CREATE | INVERT_BOOLEAN); - regex_search_button.bind_property ("active", whole_word_search_button, "sensitive", SYNC_CREATE | INVERT_BOOLEAN); - regex_search_button.bind_property ("active", case_sensitive_search_label, "sensitive", SYNC_CREATE | INVERT_BOOLEAN); - regex_search_button.bind_property ("active", case_sensitive_search_button, "sensitive", SYNC_CREATE | INVERT_BOOLEAN); - - var search_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) { - margin_top = 3, - margin_end = 3, - margin_bottom = 3, - margin_start = 6 - }; - search_box.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); - search_box.add (search_entry); - search_box.add (tool_arrow_down); - search_box.add (tool_arrow_up); - search_box.add (search_menubutton); - - var search_flow_box_child = new Gtk.FlowBoxChild (); - search_flow_box_child.can_focus = false; - search_flow_box_child.add (search_box); - - replace_entry = new Gtk.SearchEntry (); - replace_entry.hexpand = true; - replace_entry.placeholder_text = _("Replace With"); - replace_entry.set_icon_from_icon_name (Gtk.EntryIconPosition.PRIMARY, "edit-symbolic"); - - replace_tool_button = new Gtk.Button.with_label (_("Replace")); - replace_tool_button.clicked.connect (on_replace_entry_activate); - - replace_all_tool_button = new Gtk.Button.with_label (_("Replace all")); - replace_all_tool_button.clicked.connect (on_replace_all_entry_activate); - - var replace_grid = new Gtk.Grid () { - margin_top = 3, - margin_end = 6, - margin_bottom = 3, - margin_start = 3 - }; - replace_grid.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); - replace_grid.add (replace_entry); - replace_grid.add (replace_tool_button); - replace_grid.add (replace_all_tool_button); - - var replace_flow_box_child = new Gtk.FlowBoxChild (); - replace_flow_box_child.can_focus = false; - replace_flow_box_child.add (replace_grid); - - // Connecting to some signals - search_entry.changed.connect (on_search_parameters_changed); - search_entry.notify["is-focus"].connect (() => { - if (search_entry.is_focus && text_buffer != null) { - Idle.add (() => { - update_search_widgets (); - search_entry.select_region (0, -1); - return Source.REMOVE; - }); - } - }); - search_entry.icon_release.connect ((p0, p1) => { - if (p0 == Gtk.EntryIconPosition.PRIMARY) { - search_next (); - } - }); - replace_entry.activate.connect (on_replace_entry_activate); - - var flowbox = new Gtk.FlowBox () { - selection_mode = Gtk.SelectionMode.NONE, - column_spacing = 6, - max_children_per_line = 2 - }; - flowbox.get_style_context ().add_class ("search-bar"); - flowbox.add (search_flow_box_child); - flowbox.add (replace_flow_box_child); - - revealer = new Gtk.Revealer () { - child = flowbox, - reveal_child = false - }; - - add (revealer); - update_search_widgets (); - - key_controller = new Gtk.EventControllerKey (window) { - propagation_phase = CAPTURE - }; - key_controller.key_pressed.connect (on_key_pressed); - } - - public void set_text_view (Scratch.Widgets.SourceView? text_view) { - if (this.text_view == text_view) { - // Do not needlessly recreate SearchContext - may interfere with ongoing search - return; - } - - cancel_update_search_widgets (); - this.text_view = text_view; - if (text_view == null) { - warning ("No SourceView is associated with SearchManager!"); - search_context = null; - return; - } else if (this.text_buffer != null) { - this.text_buffer.changed.disconnect (on_text_buffer_changed); - } + ) + }; + tool_arrow_up.clicked.connect (search_previous); + + cycle_search_button = new Granite.SwitchModelButton (_("Cyclic Search")); + + case_sensitive_search_button = new Gtk.ComboBoxText (); + case_sensitive_search_button.append ("never", _("Never")); + case_sensitive_search_button.append ("mixed", _("Mixed Case")); + case_sensitive_search_button.append ("always", _("Always")); + case_sensitive_search_button.active = 1; + + var case_sensitive_search_label = new Gtk.Label (_("Case Sensitive")); + + var case_sensitive_box = new Gtk.Box (HORIZONTAL, 12); + case_sensitive_box.add (case_sensitive_search_label); + case_sensitive_box.add (case_sensitive_search_button); + case_sensitive_box.get_style_context ().add_class (Gtk.STYLE_CLASS_MENUITEM); + + regex_search_button = new Granite.SwitchModelButton (_("Use Regular Expressions")); + whole_word_search_button = new Granite.SwitchModelButton (_("Match Whole Words")); + + var search_option_box = new Gtk.Box (VERTICAL, 0) { + margin_top = 3, + margin_bottom = 3 + }; + search_option_box.add (cycle_search_button); + search_option_box.add (case_sensitive_box); + search_option_box.add (whole_word_search_button); + search_option_box.add (regex_search_button); + + var search_popover = new Gtk.Popover (null) { + child = search_option_box + }; + search_popover.show_all (); + + var search_buttonbox = new Gtk.Box (HORIZONTAL, 6); + search_buttonbox.add (search_occurence_count_label); + search_buttonbox.add (new Gtk.Image.from_icon_name ("pan-down-symbolic", SMALL_TOOLBAR)); + + var search_menubutton = new Gtk.MenuButton () { + popover = search_popover, + tooltip_text = _("Search Options") + }; + search_menubutton.add (search_buttonbox); + + cycle_search_button.toggled.connect (on_search_parameters_changed); + case_sensitive_search_button.changed.connect (on_search_parameters_changed); + whole_word_search_button.toggled.connect (on_search_parameters_changed); + regex_search_button.toggled.connect (on_search_parameters_changed); + + // Bind some application settings + settings.bind ("cyclic-search", cycle_search_button, "active", DEFAULT); + settings.bind ("wholeword-search", whole_word_search_button, "active", DEFAULT); + settings.bind ("case-sensitive-search", case_sensitive_search_button, "active-id", DEFAULT); + settings.bind ("regex-search", regex_search_button, "active", DEFAULT); + + // These settings are ignored when regex searching + regex_search_button.bind_property ( + "active", cycle_search_button, "sensitive", SYNC_CREATE | INVERT_BOOLEAN + ); + regex_search_button.bind_property ( + "active", whole_word_search_button, "sensitive", SYNC_CREATE | INVERT_BOOLEAN + ); + regex_search_button.bind_property ( + "active", case_sensitive_search_label, "sensitive", SYNC_CREATE | INVERT_BOOLEAN + ); + regex_search_button.bind_property ( + "active", case_sensitive_search_button, "sensitive", SYNC_CREATE | INVERT_BOOLEAN + ); + + var search_box = new Gtk.Box (HORIZONTAL, 0) { + margin_top = 3, + margin_end = 3, + margin_bottom = 3, + margin_start = 6 + }; + search_box.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); + search_box.add (search_entry); + search_box.add (tool_arrow_down); + search_box.add (tool_arrow_up); + search_box.add (search_menubutton); + + var search_flow_box_child = new Gtk.FlowBoxChild (); + search_flow_box_child.can_focus = false; + search_flow_box_child.add (search_box); + + replace_entry = new Gtk.SearchEntry () { + hexpand = true, + placeholder_text = _("Replace With"), + primary_icon_name = "edit-symbolic" + }; + + replace_tool_button = new Gtk.Button.with_label (_("Replace")); + replace_tool_button.clicked.connect (on_replace_entry_activate); + + replace_all_tool_button = new Gtk.Button.with_label (_("Replace all")); + replace_all_tool_button.clicked.connect (on_replace_all_entry_activate); + + var replace_grid = new Gtk.Grid () { + margin_top = 3, + margin_end = 6, + margin_bottom = 3, + margin_start = 3 + }; + replace_grid.get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED); + replace_grid.add (replace_entry); + replace_grid.add (replace_tool_button); + replace_grid.add (replace_all_tool_button); + + var replace_flow_box_child = new Gtk.FlowBoxChild (); + replace_flow_box_child.can_focus = false; + replace_flow_box_child.add (replace_grid); + + // Connecting to some signals + search_entry.changed.connect (on_search_parameters_changed); + search_entry.notify["is-focus"].connect (() => { + if (search_entry.is_focus && text_buffer != null) { + Idle.add (() => { + update_search_widgets (); + search_entry.select_region (0, -1); + return Source.REMOVE; + }); + } + }); + search_entry.icon_release.connect ((p0, p1) => { + if (p0 == Gtk.EntryIconPosition.PRIMARY) { + search_next (); + } + }); + replace_entry.activate.connect (on_replace_entry_activate); + + var flowbox = new Gtk.FlowBox () { + selection_mode = Gtk.SelectionMode.NONE, + column_spacing = 6, + max_children_per_line = 2 + }; + flowbox.get_style_context ().add_class ("search-bar"); + flowbox.add (search_flow_box_child); + flowbox.add (replace_flow_box_child); + + revealer = new Gtk.Revealer () { + child = flowbox, + reveal_child = false + }; + + add (revealer); + update_search_widgets (); + + key_controller = new Gtk.EventControllerKey (window) { + propagation_phase = CAPTURE + }; + key_controller.key_pressed.connect (on_key_pressed); + } - this.text_view = text_view; - this.text_buffer = text_view.get_buffer (); - this.text_buffer.changed.connect (on_text_buffer_changed); - this.search_context = new Gtk.SourceSearchContext (text_buffer as Gtk.SourceBuffer, null); - search_context.settings.wrap_around = cycle_search_button.active; - search_context.settings.regex_enabled = regex_search_button.active; - search_context.settings.search_text = search_entry.text; - on_text_buffer_changed (); + public void set_text_view (Scratch.Widgets.SourceView? text_view) { + if (this.text_view == text_view) { + // Do not needlessly recreate SearchContext - may interfere with ongoing search + return; } - private void on_text_buffer_changed () { - update_search_widgets (); + cancel_update_search_widgets (); + this.text_view = text_view; + if (text_view == null) { + warning ("No SourceView is associated with SearchManager!"); + search_context = null; + return; + } else if (this.text_buffer != null) { + this.text_buffer.changed.disconnect (on_text_buffer_changed); } - private void on_replace_entry_activate () { - if (text_buffer == null) { - warning ("No valid buffer to replace"); - return; - } - - Gtk.TextIter? start_iter, end_iter; - text_buffer.get_iter_at_offset (out start_iter, text_buffer.cursor_position); + this.text_view = text_view; + this.text_buffer = text_view.get_buffer (); + this.text_buffer.changed.connect (on_text_buffer_changed); + this.search_context = new Gtk.SourceSearchContext (text_buffer as Gtk.SourceBuffer, null); + search_context.highlight = true; // There are no circumstances where we do not highlight + search_context.settings.wrap_around = cycle_search_button.active; + search_context.settings.regex_enabled = regex_search_button.active; + search_context.settings.search_text = search_entry.text; + on_text_buffer_changed (); + } - if (search_for_iter (start_iter, out end_iter)) { - string replace_string = replace_entry.text; - try { - cancel_update_search_widgets (); - search_context.replace (start_iter, end_iter, replace_string, replace_string.length); - update_search_widgets (); - debug ("Replaced \"%s\" with \"%s\"", search_entry.text, replace_entry.text); - } catch (Error e) { - critical (e.message); - } - } - } + public bool search () { + search_entry.grab_focus (); + update_search_widgets (); - private void on_replace_all_entry_activate () { - if (text_buffer == null || this.window.get_current_document () == null) { - debug ("No valid buffer to replace"); - return; - } + return true; + } - string replace_string = replace_entry.text; - this.window.get_current_document ().toggle_changed_handlers (false); - try { - cancel_update_search_widgets (); - search_context.replace_all (replace_string, replace_string.length); - update_search_widgets (); - } catch (Error e) { - critical (e.message); + public void search_previous () { + /* Get selection range */ + Gtk.TextIter? start_iter, end_iter; + if (text_buffer != null) { + text_buffer.get_selection_bounds (out start_iter, out end_iter); + if (!search_for_iter_backward (start_iter, out end_iter) && cycle_search_button.active) { + text_buffer.get_end_iter (out start_iter); + search_for_iter_backward (start_iter, out end_iter); } - this.window.get_current_document ().toggle_changed_handlers (true); + update_search_widgets (); } + } - // Called when one of the settings buttons or the search term changes - private void on_search_parameters_changed () { - if (search_context != null) { - var search_string = search_entry.text; - search_context.settings.search_text = search_string; - var case_mode = (CaseSensitiveMode)(case_sensitive_search_button.active); - switch (case_mode) { - case CaseSensitiveMode.NEVER: - search_context.settings.case_sensitive = false; - break; - case CaseSensitiveMode.MIXED: - search_context.settings.case_sensitive = !((search_string.up () == search_string) || (search_string.down () == search_string)); - break; - case CaseSensitiveMode.ALWAYS: - search_context.settings.case_sensitive = true; - break; - default: - assert_not_reached (); - } - - search_context.settings.at_word_boundaries = whole_word_search_button.active; - search_context.settings.regex_enabled = regex_search_button.active; + public void search_next () { + /* Get selection range */ + Gtk.TextIter? start_iter, end_iter, end_iter_tmp; + if (text_buffer != null) { + text_buffer.get_selection_bounds (out start_iter, out end_iter); + if (!search_for_iter (end_iter, out end_iter_tmp) && cycle_search_button.active) { + text_buffer.get_start_iter (out start_iter); + search_for_iter (start_iter, out end_iter); } update_search_widgets (); } + } - public bool search () { - search_entry.grab_focus (); - if (search_context == null) { - return false; - } - - search_context.highlight = false; - - if (!has_matches ()) { - debug ("Can't search anything in a non-existent buffer and/or without anything to search."); - return false; - } + public void focus_search_entry () { + search_entry.grab_focus (); + } - search_context.highlight = true; + public void focus_replace_entry () { + replace_entry.grab_focus (); + } - Gtk.TextIter? start_iter, end_iter; - text_buffer.get_iter_at_offset (out start_iter, text_buffer.cursor_position); + public void reveal (bool to_reveal) { + revealer.reveal_child = to_reveal; + // Clear entry when searchbar is hidden + if (is_revealed && !to_reveal) { + set_search_entry_text (""); + } + } - if (search_for_iter (start_iter, out end_iter)) { - search_entry.get_style_context ().remove_class (Gtk.STYLE_CLASS_ERROR); - search_entry.primary_icon_name = "edit-find-symbolic"; - } else { - text_buffer.get_start_iter (out start_iter); - if (search_for_iter (start_iter, out end_iter)) { - search_entry.get_style_context ().remove_class (Gtk.STYLE_CLASS_ERROR); - search_entry.primary_icon_name = "edit-find-symbolic"; - } else { - debug ("Not found: \"%s\"", search_entry.text); - start_iter.set_offset (-1); - text_buffer.select_range (start_iter, start_iter); - search_entry.get_style_context ().add_class (Gtk.STYLE_CLASS_ERROR); - search_entry.primary_icon_name = "dialog-error-symbolic"; - return false; - } - } + public void set_search_entry_text (string text) { + search_entry.text = text; + } - return true; + private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { + if (!(search_is_focused || replace_is_focused)) { + return false; } - - public void highlight_none () { - if (search_context != null) { - search_context.highlight = false; - } + /* We don't need to perform search if there is nothing to search... */ + if (!has_search_term) { + return false; } - private bool has_matches () { - if (text_buffer == null || search_entry.text == "") { - return false; - } - - bool has_wrapped_around; - Gtk.TextIter? start_iter, end_iter; - text_buffer.get_start_iter (out start_iter); - return search_context.forward (start_iter, out start_iter, out end_iter, out has_wrapped_around); + string key = Gdk.keyval_name (keyval); + if (Gdk.ModifierType.SHIFT_MASK in state) { + key = "" + key; } - private bool search_for_iter (Gtk.TextIter? start_iter, out Gtk.TextIter? end_iter) { - end_iter = start_iter; - - if (search_context == null) { - critical ("Trying to search forwards with no search context"); - return false; - } - - bool has_wrapped_around; - bool found = search_context.forward (start_iter, out start_iter, out end_iter, out has_wrapped_around); - if (found) { - text_buffer.select_range (start_iter, end_iter); - if (has_wrapped_around) { - start_iter.backward_lines (3); - } else { - start_iter.forward_lines (3); - } - text_view.scroll_to_iter (start_iter, 0, false, 0, 0); + if (search_is_focused) { + switch (key) { + case "Return": + case "Up": + search_previous (); + return true; + case "Return": + case "Down": + search_next (); + return true; + case "Tab": + focus_replace_entry (); + return true; + } + } else { + switch (Gdk.keyval_name (keyval)) { + case "Up": + search_previous (); + return true; + case "Down": + search_next (); + return true; + case "Tab": + focus_search_entry (); + return true; } - return found; + return false; } - private bool search_for_iter_backward (Gtk.TextIter? start_iter, out Gtk.TextIter? end_iter) { - end_iter = start_iter; + return false; + } - if (search_context == null) { - critical ("Trying to search backwards with no search context"); - return false; - } + private void on_text_buffer_changed () { + update_search_widgets (); + } - bool has_wrapped_around; - bool found = search_context.backward (start_iter, out start_iter, out end_iter, out has_wrapped_around); - if (found) { - text_buffer.select_range (start_iter, end_iter); - if (has_wrapped_around) { - start_iter.forward_lines (3); - } else { - start_iter.backward_lines (3); - } - text_view.scroll_to_iter (start_iter, 0, false, 0, 0); - } - return found; + private void on_replace_entry_activate () { + if (text_buffer == null) { + warning ("No valid buffer to replace"); + return; } - public void search_previous () { - /* Get selection range */ - Gtk.TextIter? start_iter, end_iter; - if (text_buffer != null) { - text_buffer.get_selection_bounds (out start_iter, out end_iter); - if (!search_for_iter_backward (start_iter, out end_iter) && cycle_search_button.active) { - text_buffer.get_end_iter (out start_iter); - search_for_iter_backward (start_iter, out end_iter); - } + Gtk.TextIter? start_iter, end_iter; + text_buffer.get_iter_at_offset (out start_iter, text_buffer.cursor_position); + if (search_for_iter (start_iter, out end_iter)) { + string replace_string = replace_entry.text; + try { + cancel_update_search_widgets (); + search_context.replace (start_iter, end_iter, replace_string, replace_string.length); update_search_widgets (); + debug ("Replaced \"%s\" with \"%s\"", search_entry.text, replace_entry.text); + } catch (Error e) { + critical (e.message); } } + } - public void search_next () { - /* Get selection range */ - Gtk.TextIter? start_iter, end_iter, end_iter_tmp; - if (text_buffer != null) { - text_buffer.get_selection_bounds (out start_iter, out end_iter); - if (!search_for_iter (end_iter, out end_iter_tmp) && cycle_search_button.active) { - text_buffer.get_start_iter (out start_iter); - search_for_iter (start_iter, out end_iter); - } - - update_search_widgets (); - } + private void on_replace_all_entry_activate () { + if (text_buffer == null || this.window.get_current_document () == null) { + debug ("No valid buffer to replace"); + return; } - public void focus_search_entry () { - search_entry.grab_focus (); + string replace_string = replace_entry.text; + this.window.get_current_document ().toggle_changed_handlers (false); + try { + cancel_update_search_widgets (); + search_context.replace_all (replace_string, replace_string.length); + update_search_widgets (); + } catch (Error e) { + critical (e.message); } - public void focus_replace_entry () { - replace_entry.grab_focus (); - } + this.window.get_current_document ().toggle_changed_handlers (true); + } - public void reveal (bool to_reveal) { - revealer.reveal_child = to_reveal; - // Clear entry when searchbar is hidden - if (is_revealed && !to_reveal) { - set_search_entry_text (""); - } + // Called when one of the settings buttons or the search term changes + private void on_search_parameters_changed () { + if (search_context != null) { + var search_string = search_entry.text; + search_context.settings.search_text = search_string; + var case_mode = (CaseSensitiveMode)(case_sensitive_search_button.active); + switch (case_mode) { + case CaseSensitiveMode.NEVER: + search_context.settings.case_sensitive = false; + break; + case CaseSensitiveMode.MIXED: + var found = ((search_string.up () == search_string) || + (search_string.down () == search_string) + ); + search_context.settings.case_sensitive = !found; + break; + case CaseSensitiveMode.ALWAYS: + search_context.settings.case_sensitive = true; + break; + default: + assert_not_reached (); + } + + search_context.settings.at_word_boundaries = whole_word_search_button.active; + search_context.settings.regex_enabled = regex_search_button.active; } - public void set_search_entry_text (string text) { - search_entry.text = text; - } + update_search_widgets (); + } - private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { - if (!(search_is_focused || replace_is_focused)) { - return false; - } - /* We don't need to perform search if there is nothing to search... */ - if (!has_search_term) { - return false; - } + private bool search_for_iter (Gtk.TextIter? start_iter, out Gtk.TextIter? end_iter) { + end_iter = start_iter; - string key = Gdk.keyval_name (keyval); - if (Gdk.ModifierType.SHIFT_MASK in state) { - key = "" + key; - } + if (search_context == null) { + critical ("Trying to search forwards with no search context"); + return false; + } - if (search_is_focused) { - switch (key) { - case "Return": - case "Up": - search_previous (); - return true; - case "Return": - case "Down": - search_next (); - return true; - case "Tab": - focus_replace_entry (); - return true; - } + bool has_wrapped_around; + bool found = search_context.forward (start_iter, out start_iter, out end_iter, out has_wrapped_around); + if (found) { + text_buffer.select_range (start_iter, end_iter); + if (has_wrapped_around) { + start_iter.backward_lines (3); } else { - switch (Gdk.keyval_name (keyval)) { - case "Up": - search_previous (); - return true; - case "Down": - search_next (); - return true; - case "Tab": - focus_search_entry (); - return true; - } - - return false; + start_iter.forward_lines (3); } + text_view.scroll_to_iter (start_iter, 0, false, 0, 0); + } + return found; + } + + private bool search_for_iter_backward (Gtk.TextIter? start_iter, out Gtk.TextIter? end_iter) { + end_iter = start_iter; + + if (search_context == null) { + critical ("Trying to search backwards with no search context"); return false; } - private void cancel_update_search_widgets () { - if (update_search_label_timeout_id > 0) { - Source.remove (update_search_label_timeout_id); - update_search_label_timeout_id = 0; + bool has_wrapped_around; + bool found = search_context.backward (start_iter, out start_iter, out end_iter, out has_wrapped_around); + if (found) { + text_buffer.select_range (start_iter, end_iter); + if (has_wrapped_around) { + start_iter.forward_lines (3); + } else { + start_iter.backward_lines (3); } + text_view.scroll_to_iter (start_iter, 0, false, 0, 0); } + return found; + } - // Update search occurrence label, tool arrows and replace buttons in sync - private void update_search_widgets () { - cancel_update_search_widgets (); - update_search_label_timeout_id = Timeout.add (100, () => { - update_search_label_timeout_id = 0; - if (search_context == null) { - debug ("update occurrence with null context"); - replace_tool_button.sensitive = false; - replace_all_tool_button.sensitive = false; - tool_arrow_up.sensitive = false; - tool_arrow_down.sensitive = false; - return Source.REMOVE; - } + private void cancel_update_search_widgets () { + if (update_search_label_timeout_id > 0) { + Source.remove (update_search_label_timeout_id); + update_search_label_timeout_id = 0; + } + } - Gtk.TextIter? iter, start_iter, end_iter; - text_buffer.get_iter_at_offset (out iter, text_buffer.cursor_position); + // Update search occurrence label, tool arrows and replace buttons in sync + private void update_search_widgets () { + cancel_update_search_widgets (); + update_search_label_timeout_id = Timeout.add (100, () => { + update_search_label_timeout_id = 0; + if (search_context == null) { + debug ("update occurrence with null context"); + replace_tool_button.sensitive = false; + replace_all_tool_button.sensitive = false; + tool_arrow_up.sensitive = false; + tool_arrow_down.sensitive = false; + return Source.REMOVE; + } - int count_of_search = search_context.get_occurrences_count (); + Gtk.TextIter? iter, start_iter, end_iter; + text_buffer.get_iter_at_offset (out iter, text_buffer.cursor_position); - int location_of_search = 0; - bool found = search_context.forward (iter, out start_iter, out end_iter, null); - if (count_of_search > 0 && found) { - location_of_search = search_context.get_occurrence_position (start_iter, end_iter); - } + int count_of_search = search_context.get_occurrences_count (); - if (count_of_search > -1) { - if (count_of_search > 0) { - search_occurence_count_label.label = _("%d of %d").printf ( - location_of_search, - count_of_search - ); - } else { - search_occurence_count_label.label = _("no results"); - } + int location_of_search = 0; + bool found = search_context.forward (iter, out start_iter, out end_iter, null); + if (count_of_search > 0 && found) { + location_of_search = search_context.get_occurrence_position (start_iter, end_iter); + } + + if (count_of_search > -1) { + if (count_of_search > 0) { + search_occurence_count_label.label = _("%d of %d").printf ( + location_of_search, + count_of_search + ); + } else { + search_occurence_count_label.label = _("no results"); } + } - replace_tool_button.sensitive = location_of_search > 0; - replace_all_tool_button.sensitive = count_of_search > 0; + replace_tool_button.sensitive = location_of_search > 0; + replace_all_tool_button.sensitive = count_of_search > 0; - // Update tool arrows - if (text_buffer == null || - search_entry.text == "" || - count_of_search == 0) { + // Update tool arrows + if (text_buffer == null || + search_entry.text == "" || + count_of_search == 0) { - tool_arrow_up.sensitive = false; - tool_arrow_down.sensitive = false; + tool_arrow_up.sensitive = false; + tool_arrow_down.sensitive = false; + } else { + if (cycle_search_button.active) { + tool_arrow_down.sensitive = true; + tool_arrow_up.sensitive =true; } else { - if (cycle_search_button.active) { - tool_arrow_down.sensitive = true; - tool_arrow_up.sensitive =true; - } else { - Gtk.TextIter? tmp_start_iter, tmp_end_iter; + Gtk.TextIter? tmp_start_iter, tmp_end_iter; - bool is_in_start, is_in_end; + bool is_in_start, is_in_end; - text_buffer.get_start_iter (out tmp_start_iter); - text_buffer.get_end_iter (out tmp_end_iter); + text_buffer.get_start_iter (out tmp_start_iter); + text_buffer.get_end_iter (out tmp_end_iter); - text_buffer.get_selection_bounds (out start_iter, out end_iter); + text_buffer.get_selection_bounds (out start_iter, out end_iter); - is_in_start = start_iter.compare (tmp_start_iter) == 0; - is_in_end = end_iter.compare (tmp_end_iter) == 0; + is_in_start = start_iter.compare (tmp_start_iter) == 0; + is_in_end = end_iter.compare (tmp_end_iter) == 0; - if (!is_in_end) { - tool_arrow_down.sensitive = search_context.forward ( - end_iter, out tmp_start_iter, out tmp_end_iter, null - ); - } else { - tool_arrow_down.sensitive = false; - } + if (!is_in_end) { + tool_arrow_down.sensitive = search_context.forward ( + end_iter, out tmp_start_iter, out tmp_end_iter, null + ); + } else { + tool_arrow_down.sensitive = false; + } - if (!is_in_start) { - tool_arrow_up.sensitive = search_context.backward ( - start_iter, out tmp_start_iter, out end_iter, null - ); - } else { - tool_arrow_up.sensitive = false; - } + if (!is_in_start) { + tool_arrow_up.sensitive = search_context.backward ( + start_iter, out tmp_start_iter, out end_iter, null + ); + } else { + tool_arrow_up.sensitive = false; } } + } - // Update appearance of search entry - var ctx = search_entry.get_style_context (); + // Update appearance of search entry + var ctx = search_entry.get_style_context (); - if (search_entry.text != "" && count_of_search == 0) { - ctx.add_class (Gtk.STYLE_CLASS_ERROR); - search_entry.primary_icon_name = "dialog-error-symbolic"; - } else if (ctx.has_class (Gtk.STYLE_CLASS_ERROR)) { - ctx.remove_class (Gtk.STYLE_CLASS_ERROR); - search_entry.primary_icon_name = "edit-find-symbolic"; - } + if (search_entry.text != "" && count_of_search == 0) { + ctx.add_class (Gtk.STYLE_CLASS_ERROR); + search_entry.primary_icon_name = "dialog-error-symbolic"; + } else if (ctx.has_class (Gtk.STYLE_CLASS_ERROR)) { + ctx.remove_class (Gtk.STYLE_CLASS_ERROR); + search_entry.primary_icon_name = "edit-find-symbolic"; + } - return Source.REMOVE; - }); + return Source.REMOVE; + }); - } } }