diff --git a/src/Dialogs/GlobalSearchDialog.vala b/src/Dialogs/GlobalSearchDialog.vala index e43a907f9..077cbe466 100644 --- a/src/Dialogs/GlobalSearchDialog.vala +++ b/src/Dialogs/GlobalSearchDialog.vala @@ -23,8 +23,6 @@ public class Scratch.Dialogs.GlobalSearchDialog : Granite.MessageDialog { public string folder_name { get; construct; } public bool is_repo { get; construct; } private Granite.ValidatedEntry search_term_entry; - private Gtk.Switch case_switch; - private Gtk.Switch regex_switch; public string search_term { get { @@ -36,75 +34,54 @@ public class Scratch.Dialogs.GlobalSearchDialog : Granite.MessageDialog { } } - public bool use_regex { - get { - return regex_switch.active; - } - - set { - regex_switch.active = value; - } - } - - public bool case_sensitive { - get { - return case_switch.active; - } - - set { - case_switch.active = value; - } - } + public bool case_sensitive { get; construct; } + public bool wholeword { get; construct; } + public bool use_regex { get; construct; } - public GlobalSearchDialog (string folder_name, bool is_repo) { + public GlobalSearchDialog (string folder_name, bool is_repo, bool case_sensitive, bool wholeword, bool use_regex) { Object ( - transient_for: ((Gtk.Application) GLib.Application.get_default ()).active_window, folder_name: folder_name, is_repo: is_repo, - image_icon: new ThemedIcon ("edit-find") + case_sensitive: case_sensitive, + wholeword: wholeword, + use_regex: use_regex ); } construct { - primary_text = _("Search for text in ā€œ%sā€").printf (folder_name); - secondary_text = _("The search term must be at least 3 characters long."); + transient_for = ((Gtk.Application) GLib.Application.get_default ()).active_window; + image_icon = new ThemedIcon ("edit-find"); search_term_entry = new Granite.ValidatedEntry () { margin_bottom = 12, width_chars = 30 //Most searches are less than this, can expand window if required }; - case_switch = new Gtk.Switch () { - active = false, - halign = Gtk.Align.START, - hexpand = true - }; - - var case_label = new Gtk.Label (_("Case sensitive:")) { - halign = Gtk.Align.END - }; + string case_text = "", wholeword_text = "", regex_text = ""; + if (use_regex) { + regex_text = _("The search term will be treated as a regex expression"); + } else { + case_text = case_sensitive ? _("Search will be case sensitive") : _("Search will be case insensitive"); + wholeword_text = wholeword ? _("Search will match only whole words") : ""; + } - regex_switch = new Gtk.Switch () { - active = false, - halign = Gtk.Align.START - }; + primary_text = _("Search for text in ā€œ%sā€").printf (folder_name); + secondary_text = _("The search term must be at least 3 characters long."); - var regex_label = new Gtk.Label (_("Use regular expressions:")) { - halign = Gtk.Align.END - }; + var box = new Gtk.Box (VERTICAL, 0); + if (!use_regex) { + box.add (new Gtk.Label (case_text) { halign = START }); + if (wholeword_text != "") { + box.add (new Gtk.Label (wholeword_text) { halign = START }); + } + } else { + box.add (new Gtk.Label (regex_text) { halign = START }); + } - var layout = new Gtk.Grid () { - column_spacing = 12, - row_spacing = 6 - }; - layout.attach (search_term_entry, 0, 0, 2); - layout.attach (case_label, 0, 1); - layout.attach (case_switch, 1, 1); - layout.attach (regex_label, 0, 2); - layout.attach (regex_switch, 1, 2); - layout.show_all (); + box.add (search_term_entry); - custom_bin.add (layout); + custom_bin.add (box); + custom_bin.show_all (); add_button (_("Cancel"), Gtk.ResponseType.CANCEL); @@ -119,6 +96,13 @@ public class Scratch.Dialogs.GlobalSearchDialog : Granite.MessageDialog { search_term_entry.changed.connect (() => { search_term_entry.is_valid = search_term_entry.text.length >= 3; + if (use_regex) { + try { + var search_regex = new Regex (search_term_entry.text, 0); + } catch { + search_term_entry.is_valid = false; + } + } }); } } diff --git a/src/FolderManager/ProjectFolderItem.vala b/src/FolderManager/ProjectFolderItem.vala index 460504362..78c2ecdae 100644 --- a/src/FolderManager/ProjectFolderItem.vala +++ b/src/FolderManager/ProjectFolderItem.vala @@ -499,6 +499,10 @@ namespace Scratch.FolderManager { return is_git_repo ? monitored_repo.is_valid_new_local_branch_name (new_name) : false; } + // The parameter "is_explicit" indicates whether a global search was requested + // via a context menu on an explicitly chosen folder, in which case everything in that + // folder will be searched, or whether the hot-key was used in which case the search will + // take place on the active project and will omit certain folders public void global_search ( GLib.File start_folder = this.file.file, string? term = null, @@ -507,7 +511,6 @@ namespace Scratch.FolderManager { /* For now set all options to the most inclusive (except case). * The ability to set these in the dialog (or by parameter) may be added later. */ string? search_term = null; - bool use_regex = false; bool search_tracked_only = false; bool recurse_subfolders = true; bool check_is_text = true; @@ -516,6 +519,23 @@ namespace Scratch.FolderManager { bool case_sensitive = false; Regex? pattern = null; + var wholeword_search = Scratch.settings.get_boolean ("wholeword-search"); + var case_mode = (CaseSensitiveMode)(Scratch.settings.get_enum ("case-sensitive-search")); + var use_regex = Scratch.settings.get_boolean ("regex-search"); + switch (case_mode) { + case NEVER: + case_sensitive = false; + break; + case MIXED: + case_sensitive = (term != term.ascii_up () && term != term.ascii_down ()); + break; + case ALWAYS: + case_sensitive = true; + break; + default: + assert_not_reached (); + } + var folder_name = start_folder.get_basename (); if (this.file.file.equal (start_folder)) { folder_name = name; @@ -523,10 +543,11 @@ namespace Scratch.FolderManager { var dialog = new Scratch.Dialogs.GlobalSearchDialog ( folder_name, - monitored_repo != null && monitored_repo.git_repo != null + monitored_repo != null && monitored_repo.git_repo != null, + case_sensitive, + wholeword_search, + use_regex ) { - case_sensitive = case_sensitive, - use_regex = use_regex, search_term = term }; @@ -534,8 +555,6 @@ namespace Scratch.FolderManager { switch (response) { case Gtk.ResponseType.ACCEPT: search_term = dialog.search_term; - use_regex = dialog.use_regex; - case_sensitive = dialog.case_sensitive; break; default: @@ -556,8 +575,10 @@ namespace Scratch.FolderManager { win.actions.lookup_action ("action-find").activate (search_variant); if (!use_regex) { - search_term = Regex.escape_string (search_term); - } + if (wholeword_search) { + search_term = "\\b%s\\b".printf (search_term); + } + } // else use search_term as is - TODO do we need to escape it? try { var flags = RegexCompileFlags.MULTILINE; @@ -643,7 +664,7 @@ namespace Scratch.FolderManager { } private void perform_match (GLib.File target, - Regex pattern, + Regex search_regex, bool check_is_text = false, FileInfo? target_info = null) { string contents; @@ -687,7 +708,7 @@ namespace Scratch.FolderManager { MatchInfo? match_info = null; int match_count = 0; try { - for (pattern.match (contents, 0, out match_info); + for (search_regex.match (contents, RegexMatchFlags.NOTEMPTY, out match_info); match_info.matches (); match_info.next ()) { diff --git a/src/Widgets/SearchBar.vala b/src/Widgets/SearchBar.vala index d4c1d0f97..8fb50588d 100644 --- a/src/Widgets/SearchBar.vala +++ b/src/Widgets/SearchBar.vala @@ -19,13 +19,15 @@ * with this program. If not, see . */ +public enum Scratch.CaseSensitiveMode { + NEVER, + MIXED, + ALWAYS +} + namespace Scratch.Widgets { public class SearchBar : Gtk.Box { //TODO In Gtk4 use a BinLayout Widget - enum CaseSensitiveMode { - NEVER, - MIXED, - ALWAYS - } + public weak MainWindow window { get; construct; } @@ -172,8 +174,13 @@ namespace Scratch.Widgets { 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 ("regex-search", regex_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,