Skip to content
Merged
90 changes: 37 additions & 53 deletions src/Dialogs/GlobalSearchDialog.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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) {
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);

Expand All @@ -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;
}
}
});
}
}
41 changes: 31 additions & 10 deletions src/FolderManager/ProjectFolderItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -516,26 +519,42 @@ 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;
}

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
};

dialog.response.connect ((response) => {
switch (response) {
case Gtk.ResponseType.ACCEPT:
search_term = dialog.search_term;
use_regex = dialog.use_regex;
case_sensitive = dialog.case_sensitive;
break;

default:
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 ()) {

Expand Down
19 changes: 13 additions & 6 deletions src/Widgets/SearchBar.vala
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

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; }


Expand Down Expand Up @@ -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,
Expand Down